Skip to content

add clarabel wasm feature flag #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:
with:
activate-environment: test
allow-softlinks: true
- uses: actions/setup-node@v4
with:
node-version: 22
- name: install deps
run: |
sudo apt-get install coinor-cbc coinor-libcbc-dev libgsl-dev
Expand All @@ -38,6 +41,8 @@ jobs:
curl -LO https://github.com/rust-or/good_lp/releases/download/cplex/cplex.bin
chmod u+x cplex.bin
./cplex.bin -f ./.github/cplex/response.properties
# Install wasm-pack
cargo install wasm-pack
- name: Build with all default solvers (no cplex)
run: cargo build --features all_default_solvers --tests
- name: Run tests with all default solvers (no cplex)
Expand All @@ -57,4 +62,8 @@ jobs:
run: cargo test --no-default-features --features cplex-rs
- name: Run tests with Clarabel
run: cargo test --no-default-features --features clarabel
- name: Run tests with Clarabel on WASM
run: wasm-pack test --node --no-default-features --features "clarabel,clarabel-wasm"
- name: Run tests with minilp on WASM
run: wasm-pack test --node --no-default-features --features "minilp"
- run: cargo bench
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ default = ["coin_cbc", "singlethread-cbc"]
singlethread-cbc = ["coin_cbc?/singlethread-cbc"]
scip = ["russcip"]
all_default_solvers = ["coin_cbc", "minilp", "lpsolve", "highs", "russcip", "lp-solvers", "clarabel"] # cplex-rs is not included because it is incompatible with lpsolve
clarabel-wasm = ["clarabel/wasm"]

[dependencies]
coin_cbc = { version = "0.1", optional = true, default-features = false }
Expand All @@ -29,9 +30,14 @@ clarabel = { version = "0.9.0", optional = true, features = [] }
fnv = "1.0.5"

[dev-dependencies]
criterion = "0.5"
float_eq = "1.0"

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = "0.5"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.0"

[[bench]]
name = "benchmark"
harness = false
Expand Down
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,22 @@ You can find a resource allocation problem example in
This library offers an abstraction over multiple solvers. By default, it uses [cbc][cbc], but
you can also activate other solvers using cargo features.

| solver feature name | integer variables | no C compiler\* | no additional libs\*\* | fast |
| ---------------------- | ----------------- | --------------- | ---------------------- | ---- |
| [`coin_cbc`][cbc] | ✅ | ✅ | ❌ | ✅ |
| [`highs`][highs] | ✅ | ❌ | ✅\+ | ✅ |
| [`lpsolve`][lpsolve] | ✅ | ❌ | ✅ | ❌ |
| [`minilp`][minilp] | ❌ | ✅ | ✅ | ❌ |
| [`lp-solvers`][lps] | ✅ | ✅ | ✅ | ❌ |
| [`scip`][scip] | ✅ | ✅ | ❌ | ✅ |
| [`cplex-rs`][cplex] | ✅ | ❌ | ✅\+\+ | ✅ |
| [`clarabel`][clarabel] | ❌ | ✅ | ✅ | ✅ |
| solver feature name | integer variables | no C compiler\* | no additional libs\*\* | fast | WASM |
| ---------------------- | ----------------- | --------------- | ---------------------- | ---- |---------|
| [`coin_cbc`][cbc] | ✅ | ✅ | ❌ | ✅ | ❌ |
| [`highs`][highs] | ✅ | ❌ | ✅\+ | ✅ | ❌ |
| [`lpsolve`][lpsolve] | ✅ | ❌ | ✅ | ❌ | ❌ |
| [`minilp`][minilp] | ❌ | ✅ | ✅ | ❌ | ✅ |
| [`lp-solvers`][lps] | ✅ | ✅ | ✅ | ❌ | ❌ |
| [`scip`][scip] | ✅ | ✅ | ❌ | ✅ | ❌ |
| [`cplex-rs`][cplex] | ✅ | ❌ | ✅\+\+ | ✅ | ❌ |
| [`clarabel`][clarabel] | ❌ | ✅ | ✅ | ✅ | ✅\+\+\+ |

- \* no C compiler: builds with only cargo, without requiring you to install a C compiler
- \*\* no additional libs: works without additional libraries at runtime, all the dependencies are statically linked
- \+ highs itself is statically linked and does not require manual installation. However, on some systems, you may have to [install dependencies of highs itself](https://github.com/rust-or/good_lp/issues/29).
- \+\+ the cplex_rs crate links statically to a local installation of the IBM ILOG CPLEX Optimizer.
- \+\+\+ to use clarabel for WASM targets, set the `clarabel-wasm` feature flag

To use an alternative solver, put the following in your `Cargo.toml`:

Expand Down Expand Up @@ -125,7 +126,7 @@ Minilp is written in pure rust, so you can use it without having to install a C
or having to install any external library, but it is slower than other solvers.

It performs very poorly when compiled in debug mode, so be sure to compile your code
in `--release` mode when solving large problems.
in `--release` mode when solving large problems. This solver can compile to WASM targets.

### [HiGHS][highs]

Expand Down Expand Up @@ -201,6 +202,7 @@ linear programming solver written in Rust by the
It does not support integer variables, but it is fast and easy to install.
It does implement the [SolutionWithDual](https://docs.rs/good_lp/latest/good_lp/solvers/trait.SolutionWithDual.html)
trait, which allows you to access the dual values of the constraints (the shadow prices).
If you want to use it with WASM targets, you must include the `clarabel-wasm` feature flag

[clarabel]: https://github.com/oxfordcontrol/Clarabel.rs

Expand Down
3 changes: 3 additions & 0 deletions tests/readme_example.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::error::Error;

use good_lp::{constraint, default_solver, variables, Solution, SolverModel};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn main() -> Result<(), Box<dyn Error>> {
variables! {
vars:
Expand Down
5 changes: 4 additions & 1 deletion tests/resource_allocation_problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

use good_lp::variable::ProblemVariables;
use good_lp::{default_solver, variable, variables, Expression, Solution, SolverModel, Variable};

#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
struct Product {
// amount of fuel producing 1 unit takes
needed_fuel: f64,
Expand Down Expand Up @@ -65,6 +66,7 @@ impl ResourceAllocationProblem {
use float_eq::assert_float_eq;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn resource_allocation() {
let mut pb = ResourceAllocationProblem::new(5., 3.);
let steel = pb.add(Product {
Expand All @@ -87,6 +89,7 @@ fn resource_allocation() {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn using_a_vector() {
let products = vec![
Product {
Expand Down
5 changes: 4 additions & 1 deletion tests/shadow_price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use good_lp::{
solvers::{DualValues, SolutionWithDual},
variable, variables, Solution, Solver, SolverModel,
};

#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
// Using a generic function here ensures the dual can be retrieved in a generic,
// solver-independent manner.
#[allow(dead_code)]
Expand Down Expand Up @@ -75,6 +76,7 @@ where
macro_rules! dual_test {
($([$solver_feature:literal, $solver:expr])*) => {
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn determine_shadow_prices() {
$(
#[cfg(feature = $solver_feature)]
Expand All @@ -83,6 +85,7 @@ macro_rules! dual_test {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn furniture_problem() {
$(
#[cfg(feature = $solver_feature)]
Expand Down
5 changes: 4 additions & 1 deletion tests/solver_scaling.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use float_eq::assert_float_eq;
use good_lp::{constraint, default_solver, variable, variables, Expression, Solution, SolverModel};

#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
const BIG_NUM: usize = 1000; // <- Set this higher to test how good_lp and the solvers scale

#[test]
Expand All @@ -21,6 +22,7 @@ fn solve_large_problem() {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn add_10_000_constraints() {
let mut vars = variables!();
let v = vars.add_vector(variable(), 10_000);
Expand All @@ -31,6 +33,7 @@ fn add_10_000_constraints() {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn sum_binaries() {
// See: https://github.com/rust-or/good_lp/issues/8
let mut vars = variables!();
Expand Down
4 changes: 3 additions & 1 deletion tests/static_solver.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use good_lp::{constraint, variables, Solution, SolverModel, StaticSolver};

#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
// See: https://github.com/rust-or/good_lp/pull/5

fn generic_solve_example<S: StaticSolver>(solver: S) -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -19,6 +20,7 @@ fn generic_solve_example<S: StaticSolver>(solver: S) -> Result<(), Box<dyn std::
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn concrete() {
generic_solve_example(good_lp::default_solver).expect("solve")
}
6 changes: 6 additions & 0 deletions tests/variables.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use good_lp::{variables, Expression};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn complex_expression() {
let mut var1 = variables!();
let a = var1.add_variable();
Expand All @@ -14,6 +17,7 @@ fn complex_expression() {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn large_sum() {
let mut var1 = variables!();
let var_vec: Vec<_> = (0..100_000).map(|_i| var1.add_variable()).collect();
Expand All @@ -23,6 +27,7 @@ fn large_sum() {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn complete() {
let mut var1 = variables!();
let mut var2 = variables!();
Expand All @@ -38,6 +43,7 @@ fn complete() {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn debug_format() {
let mut vars = variables!();
let a = vars.add_variable();
Expand Down
Loading