diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..dea9361 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,6 @@ +FROM mcr.microsoft.com/devcontainers/rust:1-1-bookworm + +RUN apt-get update && apt-get install -y vim curl + +COPY ./build/ ./build/ +RUN ./build/setup.sh && rm -r ./build diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..736e79f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "name": "Existing Dockerfile", + "build": { + "context": "..", + "dockerfile": "./Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-azuretools.vscode-docker", + "ms-vscode-remote.remote-containers", + "rust-lang.rust-analyzer" + ] + } + }, + "postCreateCommand": "./build/postsetup.sh && P=~/.local/share/bash-completion/completions && mkdir -p $P && rustup completions bash > $P/rustup && rustup completions bash cargo > $P/cargo" +} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4490eb6..0e98810 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,28 +21,11 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Check formatting run: cargo fmt -- --check - - uses: conda-incubator/setup-miniconda@v2 - 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 - # Install SCIP - conda install -y --prefix $CONDA/envs/test --channel conda-forge scip - echo "LD_LIBRARY_PATH=$CONDA/envs/test/lib" >> "${GITHUB_ENV}" - echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:$CONDA/envs/test/lib" >> "${GITHUB_ENV}" - echo "PATH=$CONDA/envs/test/bin:$PATH" >> "${GITHUB_ENV}" - echo "CONDA_PREFIX=$CONDA/envs/test" >> "${GITHUB_ENV}" - # install CPLEX - 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 + run: ./build/setup.sh && ./build/postsetup.sh - 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) @@ -57,7 +40,7 @@ jobs: - name: Run tests with lp_solvers run: cargo test --no-default-features --features lp-solvers - name: Run tests with SCIP - run: cargo test --no-default-features --features scip + run: cargo test --no-default-features --features "scip,scip_bundled" - name: Run tests with CPLEX run: cargo test --no-default-features --features cplex-rs - name: Run tests with Clarabel diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 013310d..bc07392 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,33 @@ -# General guidelines +# General Guidelines -Contribution happens through github. +Contribution happens through GitHub. Please format your code with `cargo fmt`, and if you add a new feature, update the README, the doc comments, and the doc tests accordingly. -# Adding a new solver +## Adding a New Solver Adding a new solver should not take more than a few hundred lines of code, tests included. - - add the solver as an optional dependency in `Cargo.toml` - - add a file named after your solver in the [solvers](./src/solvers) folder - - you can copy microlp, our smallest solver interface, as a starting point. - - create a struct to store linear problems in a way that will make it cheap to dynamically add new constraints to the problem, - and easy to pass the problem to the solver once it has been fully constructed. - This generally means constructing vectors to which you can push values for each new constraint. - You can generally reuse data structures provided by the library for which you are creating a wrapper. - - implement a function named after your solver that takes an [`UnsolvedProblem`](https://docs.rs/good_lp/latest/good_lp/variable/struct.UnsolvedProblem.html) and returns the struct you defined above. - - implement the [`SolverModel`](https://docs.rs/good_lp/latest/good_lp/index.html#reexport.SolverModel) trait for your new problem type. - - add your solver to `lib.rs` and to the `all_default_solvers` feature in Cargo.toml. - - open a [pull request](https://github.com/rust-or/good_lp/pulls) +- add the solver as an optional dependency in `Cargo.toml` +- add a file named after your solver in the [solvers](./src/solvers) folder + - you can copy microlp, our smallest solver interface, as a starting point. + - create a struct to store linear problems in a way that will make it cheap to dynamically add new constraints to the problem, + and easy to pass the problem to the solver once it has been fully constructed. + This generally means constructing vectors to which you can push values for each new constraint. + You can generally reuse data structures provided by the library for which you are creating a wrapper. + - implement a function named after your solver that takes an [`UnsolvedProblem`](https://docs.rs/good_lp/latest/good_lp/variable/struct.UnsolvedProblem.html) and returns the struct you defined above. + - implement the [`SolverModel`](https://docs.rs/good_lp/latest/good_lp/index.html#reexport.SolverModel) trait for your new problem type. + - add your solver to `lib.rs` and to the `all_default_solvers` feature in Cargo.toml. + - open a [pull request](https://github.com/rust-or/good_lp/pulls) + +## Dev Container Setup + +This repository contains a dev container definition. +This gives you a working system setup with all solvers installed with a single command. +It will also give you the entire Rust toolchain as well as all necessary VS Code extensions. + +1. Make sure you have [Docker](https://docs.docker.com/engine/install/) installed on your system +2. Make sure you have the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension installed in VS Code +3. Run the command `Dev Containers: Reopen in Container` in VS Code + +Done. diff --git a/Cargo.toml b/Cargo.toml index 7a1b310..51c8ada 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,8 @@ categories = ["mathematics", "algorithms", "science", "api-bindings", "data-stru default = ["coin_cbc", "singlethread-cbc"] singlethread-cbc = ["coin_cbc?/singlethread-cbc"] scip = ["russcip"] -scip_bundled = ["russcip/bundled"] -all_default_solvers = ["coin_cbc", "microlp", "lpsolve", "highs", "russcip/bundled", "lp-solvers", "clarabel"] # cplex-rs is not included because it is incompatible with lpsolve +scip_bundled = ["russcip?/bundled"] +all_default_solvers = ["coin_cbc", "microlp", "lpsolve", "highs", "russcip", "russcip/bundled", "lp-solvers", "clarabel"] # cplex-rs is not included because it is incompatible with lpsolve clarabel-wasm = ["clarabel/wasm"] minilp = ["microlp"] # minilp is not maintained anymore, we use the microlp fork instead diff --git a/README.md b/README.md index b560fd7..b58164f 100644 --- a/README.md +++ b/README.md @@ -67,20 +67,20 @@ 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 | WASM | -| ---------------------- |-------------------| --------------- | ---------------------- | ---- |---------| -| [`coin_cbc`][cbc] | ✅ | ✅ | ❌ | ✅ | ❌ | -| [`highs`][highs] | ✅ | ❌ | ✅\+ | ✅ | ❌ | -| [`lpsolve`][lpsolve] | ✅ | ❌ | ✅ | ❌ | ❌ | -| [`microlp`][microlp] | ✅ | ✅ | ✅ | ❌ | ✅ | -| [`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] | ✅ | ❌ | ✅ | ❌ | ❌ | +| [`microlp`][microlp] | ✅ | ✅ | ✅ | ❌ | ✅ | +| [`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). +- \+ 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 @@ -171,9 +171,9 @@ Additionally, the end user of your program will have to install the desired solv SCIP is currently one of the fastest open-source solvers for mixed integer programming (MIP) and mixed integer nonlinear programming (MINLP). It is also a framework for constraint integer programming and branch-cut-and-price. It allows for total control of the solution process and the access of detailed information down to the guts of the solver. -`good_lp` uses SCIP through its rust interface [russcip](https://github.com/mmghannam/russcip) which can ship a precompiled binary. The easiest way to use SCIP with `good_lp` is therefore to enable the `scip_bundled` feature. +`good_lp` uses SCIP through its rust interface [russcip](https://github.com/mmghannam/russcip) which can ship a precompiled binary. The easiest way to use SCIP with `good_lp` is therefore to enable both the `scip` and the `scip_bundled` features. -You can also use a custom installation of SCIP by enabling the `scip` feature instead. A good way of installing SCIP is by downloading a precompiled package from [here](https://scipopt.org/index.php#download) or through conda by running +You can also use a custom installation of SCIP by enabling only the `scip` feature. A good way of installing SCIP is by downloading a precompiled package from [here](https://scipopt.org/index.php#download) or through conda by running ``` conda install --channel conda-forge scip ``` @@ -194,7 +194,7 @@ Since cplex-rs-sys uses [bindgen](https://github.com/rust-lang/rust-bindgen) to ### [Clarabel][clarabel] -**Clarabel** is a free +**Clarabel** is a free ([Apache 2.0](https://github.com/oxfordcontrol/Clarabel.rs/blob/main/LICENSE.md)) linear programming solver written in Rust by the [Oxford Control group](https://eng.ox.ac.uk/control/). diff --git a/build/postsetup.sh b/build/postsetup.sh new file mode 100755 index 0000000..ad47d80 --- /dev/null +++ b/build/postsetup.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# Print commands, exit upon error +set -ex + +# Install wasm-pack +cargo install wasm-pack diff --git a/.github/cplex/response.properties b/build/response.properties similarity index 100% rename from .github/cplex/response.properties rename to build/response.properties diff --git a/build/setup.sh b/build/setup.sh new file mode 100755 index 0000000..f3dd625 --- /dev/null +++ b/build/setup.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# Print commands, exit upon error +set -ex + +# Change CWD to script location +cd "${0%/*}" + +# Install CBC, HiGHS, lp_solve +DEBIAN_FRONTEND=noninteractive sudo apt-get install -y coinor-cbc coinor-libcbc-dev libgsl-dev build-essential cmake clang + +# Install CPLEX +curl -LO https://github.com/rust-or/good_lp/releases/download/cplex/cplex.bin +chmod u+x cplex.bin +./cplex.bin -f ./response.properties