Skip to content

Rust interface#672

Open
cvanaret wants to merge 11 commits into
mainfrom
rust_interface
Open

Rust interface#672
cvanaret wants to merge 11 commits into
mainfrom
rust_interface

Conversation

@cvanaret

@cvanaret cvanaret commented Apr 23, 2026

Copy link
Copy Markdown
Owner

Closes #357

@cvanaret cvanaret self-assigned this Apr 23, 2026
@cvanaret

cvanaret commented Apr 23, 2026

Copy link
Copy Markdown
Owner Author

@cvanaret

cvanaret commented May 6, 2026

Copy link
Copy Markdown
Owner Author

Had a quick chat with @mmghannam. The following steps are:

  • have ffi.rs generated automatically and on the fly (before the build) by bindgen
  • write a Cargo.toml
  • write a build.rs that fetches the Uno precompiled library + dependencies to link them
  • upload the crates (precompiled artifacts) (source code) to crates.io
  • precompiled dependencies will be fetched at build time

Example file: https://github.com/scipopt/scip-sys/blob/main/build.rs

@mmghannam

Copy link
Copy Markdown

looks good I would just add a small correction

upload the crate to crates.io

not precompiled packages, just the source code, and it will pull the precompiled dependencies at build time.

@cvanaret

cvanaret commented May 6, 2026

Copy link
Copy Markdown
Owner Author

looks good I would just add a small correction

upload the crate to crates.io

not precompiled packages, just the source code, and it will pull the precompiled dependencies at build time.

Ah my bad, it's now fixed.
Does the linking phase on the user's machine usually run smoothly? A bit scared of all kinds of weird that may happen on Windows 😅

@mmghannam

Copy link
Copy Markdown

looks good I would just add a small correction

upload the crate to crates.io

not precompiled packages, just the source code, and it will pull the precompiled dependencies at build time.

Ah my bad, it's now fixed. Does the linking phase on the user's machine usually run smoothly? A bit scared of all kinds of weird that may happen on Windows 😅

Not always, one just needs to try and see :)
There were some issues for windows in scip-sys but nothing major.

@GermanHeim

Copy link
Copy Markdown

Hello,

I just looked at the example in example_hs015.rs, and I was wondering whether it would make sense for the crate to provide a more idiomatic Rust interface on top of the raw C callbacks.

At the moment, users need to define several functions, such as:

unsafe extern "C" fn objective_function(...)
unsafe extern "C" fn objective_gradient(...)

This adds a lot of boilerplate for the user. Would it be possible for the crate to handle the conversion between Rust closures/functions and the underlying Uno callback types internally? That way, users could provide ordinary Rust functions or closures, while the crate handles the FFI layer.

I'm curious whether you've considered this approach or whether there are technical limitations that make it difficult to support.

@cvanaret

Copy link
Copy Markdown
Owner Author

@GermanHeim I had Claude rewrite the wrapper and generate a more idiomatic example. Let me know what you think!

@GermanHeim

Copy link
Copy Markdown

I just saw the example, and it is definitely much better!

However, this part of the code, in my opinion, isn't very Rusty:

problem.set_objective(UNO_MINIMIZE, 
	|x, obj| { *obj = 100.0 * (x[1] - x[0] * x[0]).powi(2) + (1.0 - x[0]).powi(2); Ok(()) },
	|x, g| { g[0] = 400.0 * x[0].powi(3) - 400.0 *x[0] * x[1] + 2.0 * x[0] - 2.0; g[1] = 200.0 * (x[1] - x[0] * x[0]); Ok(()) }, 
).expect("set objective");

This API probably comes from C/C++ style. Maybe because the underlying API is something like:

void evaluate_objective(
    int n,
    const double* x,
    double* obj
);

?

I think a better approach (here it might be more of a personal opinion) for the Rust API might be:

problem.set_objective_fn(|x| {
    100.0 * (x[1] - x[0] * x[0]).powi(2)
        + (1.0 - x[0]).powi(2)
});

problem.set_gradient_fn(|x, grad| {
    grad[0] = ...;
    grad[1] = ...;
});

Other Rust libraries might implement a Struct. For example, argmin. Here is an example of their API.

Again, this is very much a personal preference, so whatever you decide is better will be fine. The API is much better right now and completely usable.

@cvanaret

Copy link
Copy Markdown
Owner Author

Indeed. In terms of parameters, we can implement something similar to what we have in the Python bindings (see here).

I wouldn't necessarily separate set_objective_fn and set_gradient_fn because we'd have to add more checks (e.g., if we set the gradient, is the objective already defined?). Setting them together guarantees consistency of the model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Write a Rust interface

4 participants