Linear Programming for Rust, with an user-friendly API. This crate allows modeling LP problems, and let's you solve them with various solvers.

Overview

good_lp

A Linear Programming modeler that is easy to use, performant with large problems, and well-typed.

documentation MIT license

use good_lp::{variables, variable, coin_cbc, SolverModel, Solution, contraint};

fn main() {
    let mut vars = variables!();
    let a = vars.add(variable().max(1));
    let b = vars.add(variable().min(2).max(4));
    let solution = vars.maximise(10 * (a - b / 5) - b)
        .using(coin_cbc)
        .with(constraint!(a + 2 <= b))
        .with(constraint!(1 + a >= 4 - b))
        .solve()?;
    println!("a={}   b={}", solution.value(a), solution.value(b));
    println!("a + b = {}", solution.eval(a + b));
}

Features and limitations

  • Linear programming. This crate currently supports only the definition of linear programs. You cannot use it with quadratic functions, for instance: you can maximise y + 3 * y, but not 3 * x * y.
  • Continuous variables. Currently, only continuous variables are supported. Mixed-integer linear programming (MILP) is not supported.
  • Not a solver. This crate uses other rust crates to provide the solvers. There is no solving algorithm in good_lp itself. If you have an issue with a solver, report it to the solver directly. See below for the list of supported solvers.

Contributing

Pull requests are welcome ! If you need any of the features mentioned above, get in touch. Also, do not hesitate to open issues to discuss the implementation.

Alternatives

If you need non-linear programming or integer variables, you can use lp-modeler. However, it is currently very slow with large problems.

You can also directly use the underlying solver libraries, such as coin_cbc or minilp if you don't need a way to express your objective function and constraints using an idiomatic rust syntax.

Usage examples

You can find a resource allocation problem example in resource_allocation_problem.rs.

Solvers

This library offers an abstraction over multiple solvers. By default, it uses cbc, but you can also activate other solvers using cargo features.

cbc

Used by default, performant, but requires to have a C compiler and the cbc C library installed.

In ubuntu, you can install it with:

sudo apt-get install coinor-cbc coinor-libcbc-dev

In MacOS, using homebrew :

brew install cbc

minilp

minilp is a pure rust solver, which means it works out of the box without installing anything else.

You can activate it with :

good_lp = { version = "0.3", features = ["minilp"], default-features = false }

Then use minilp instead of coin_cbc in your code:

use good_lp::minilp;

fn optimize<V>(vars: ProblemVariables<V>) {
   vars.maximise(objective).using(minilp);
}

Minilp is written in pure rust, and performs poorly when compiled in debug mode. Be sure to compile your code in --release mode when solving large problems.

lpsolve

lp_solve is a free (LGPL) linear (integer) programming solver written in C and based on the revised simplex method.

good_lp = { version = "0.3", features = ["lpsolve"], default-features = false }

good_lp uses the lpsolve crate to call lpsolve. You will need a C compiler, but you won't have to install any additional library.

HiGHS

HiGHS is a free (MIT) parallel linear programming solver written in C++. It is able to leverage OpenMP to fully leverage all the available processor cores to solve a problem.

good_lp = { version = "0.3", features = ["highs"], default-features = false }

good_lp uses the highs crate to call HiGHS. You will need a C compiler, but you shouldn't have to install any additional library on linux (it depends only on OpenMP and the C++ standard library). More information in the highs-sys crate.

License

This library is published under the MIT license.

Comments
  • Set coin_cbc verbosity

    Set coin_cbc verbosity

    Hi,

    I was wondering how I can set the verbosity level of coin_cbc through this crate. I found https://github.com/KardinalAI/coin_cbc/issues/3 which looks to be what I want. I also saw that I can get an &Model from CoinCbcProblem::as_inner(). The problem is that Model::set_parameter() requires an &mut reference while I can only obtain an immutable one.

    Is there a different solution which I missed? If not, maybe a second method could be added to CoinCbcProblem for getting a mutable reference? If this could prove hazardous for upkeeping internal invariants, making this method unsafe would seem like a good compromise to me.

    opened by Rahix 6
  • Improve quality of life of SolverModel::Error

    Improve quality of life of SolverModel::Error

    Problem

    To implement a function Fn -> Result, the user has to constrain SolverModel::Error struct to 'static + std::error::Error (see example below), which is quite inconvenient and confusing at first (same problem if using anyhow).

    Implementation

    This PR simply adds those constraints to the Error type definition so that the user can dispense with that verbose constraint. There may be a reason why this wasn't like that, but I don't see it. If that's the case, please disregard and close this PR.

    Example

    Before this PR:

    use good_lp::{default_solver, Solver, variables, SolverModel, Solution, constraint};
    
    fn solve_example<S>(solver: S) -> Result<(), Box<dyn std::error::Error>>
    where
        S: Solver,
        // Here
        <<S as good_lp::Solver>::Model as good_lp::SolverModel>::Error: 'static + std::error::Error
    {
        variables!{
            vars:
                   a <= 1;
              2 <= b <= 4;
        };
        let solution = vars.maximise(10 * (a - b / 5) - b)
            .using(solver)
            .with(constraint!(a + 2 <= b))
            .with(constraint!(1 + a >= 4 - b))
            .solve()?;
        println!("a={}   b={}", solution.value(a), solution.value(b));
        Ok(())
    }
    
    fn main() {
        solve_example(default_solver);
    }
    

    After this PR:

    fn solve_example<S: Solver>(solver: S) -> Result<(), Box<dyn std::error::Error>> {
        variables!{
            vars:
                   a <= 1;
              2 <= b <= 4;
        };
        let solution = vars.maximise(10 * (a - b / 5) - b)
            .using(solver)
            .with(constraint!(a + 2 <= b))
            .with(constraint!(1 + a >= 4 - b))
            .solve()?;
        println!("a={}   b={}", solution.value(a), solution.value(b));
        Ok(())
    }
    

    By the way, there is a typo in the use "contraint" at the example in the README.

    opened by carrascomj 6
  • `From<Expression> not implemented for f64`

    `From not implemented for f64`

    Given this code, where batt_var.b_energy: Vec<Vec<Variable>>:

            for (bat, b_energy_row) in batt_var.b_energy.iter().enumerate() {
                for (t, b_energy) in b_energy_row.iter().enumerate() {
                    let mut energy_init: Expression = match bat {
                        0 => b_energy.clone(),
                        1 => Expression::from_other_affine(st_soc * e_max / 100.0),
                        _ => Expression::from_other_affine(b_energy_row[t-1]),
                    };
                    let bat_pc = Expression::from_other_affine(batt_var.bat_pc[bat][t]);
                    energy_init.mul(bat_pc);
                }
            }
    

    I get the following error output:

    error[E0277]: the trait bound `f64: From<Expression>` is not satisfied
       --> src/model/constr.rs:449:51
        |
    449 |                 energy_init.mul(bat_pc);
        |                                         --- ^^^^^^ the trait `From<Expression>` is not implemented for `f64`
        |                                         |
        |                                         required by a bound introduced by this call
        |
    
    

    Update:

    I have fixed my issue, but I am keeping this because it seems strange.

    opened by keegit 4
  • troubles adding a constraint with a count of bool variables

    troubles adding a constraint with a count of bool variables

    Thanks for this great library! I have my use-case almost perfect except for one spot.

    I have created two vectors of bool variables like so:

    let team1_bools = vars.add_vector(variable().binary(), num_players);
    let team2_bools = vars.add_vector(variable().binary(), num_players);
    

    later on in my program, I try to count the number of bool values used, and add it in as a constraint:

    model = model.with(constraint!(team2_bools.iter().sum() == 5))
    

    when that didn't work, I tried to use this instead:

    model = model.with(constraint!(team2_bools.iter().filter(|x| x == 1).count() == 5))
    

    no matter what I try, I always end up with my errors looking like this:

    error[E0277]: can't compare `&&good_lp::variable::Variable` with `{integer}`
       --> src/main.rs:147:31
        |
    147 |                 .filter(|x| x == 1)
        |                               ^^ no implementation for `&&good_lp::variable::Variable == {integer}`
        |
        = help: the trait `std::cmp::PartialEq<{integer}>` is not implemented for `&&good_lp::variable::Variable`
    
    error[E0271]: type mismatch resolving `<usize as std::ops::Sub>::Output == good_lp::expression::Expression`
       --> src/main.rs:144:24
        |
    144 |       model = model.with(constraint!(
        |  ________________________^
    145 | |                 team2_bools
    146 | |                 .iter()
    147 | |                 .filter(|x| x == 1)
    148 | |                 .count() == 5));
        | |______________________________^ expected struct `good_lp::expression::Expression`, found `usize`
        | 
       ::: /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/good_lp-1.1.3/src/constraint.rs:42:24
        |
    42  |   pub fn eq<B, A: Sub<B, Output = Expression>>(a: A, b: B) -> Constraint {
        |                          ------------------- required by this bound in `good_lp::constraint::eq`
    

    I currently have my algorithm working using another library in Python here. I just really want to use rust for the increased speed.

    Is there any possible way I would be able to do something like this? Thanks!

    invalid 
    opened by Crypto-Spartan 3
  • Creating a variable bounded by real numbers

    Creating a variable bounded by real numbers

    Quick side note: this crate is awesome!

    One problem I am running into is that I want to create an f64 variable that lies within the domain of all real numbers. How can I achieve this?

    This is what I am currently trying to do, but it does not compile due to From<bool> is not implemented for f64.

    use std::error::Error;
    
    use good_lp::{constraint, coin_cbc, SolverModel, variables};
    
    fn main() -> Result<(), Box<dyn Error>> {
        let INF: f32 = f32::INFINITY;
        let NEG_INF: f32 = f32::NEG_INFINITY; 
    
        variables! {
            vars:
                NEG_INF <= x1 <= INF;
                NEG_INF <= x2 <= INF;
        }
        let _solution = vars.minimise(2 * x1 + 3 * x2)
            .using(coin_cbc)
            .with(constraint!((3 * x1) + (4 * x2) >= 1))
            .solve()?;
    
        Ok(())
    }
    
    

    Edit: I can specify either a lower or upper bound but not both; I am wondering if this is suitable for this situation.

    opened by keegit 2
  • Input linear program using matrix format

    Input linear program using matrix format

    Hi,

    I was trying to input a linear program using matrix format, i.e., with constraints in the form of Ax = b. I constructed a COO matrix A but cannot multiply it with the variable x. I get the following error "cannot multiply numopt::matrix::coo::CooMat<f64> by good_lp::Variable". Do you have any more complicated examples where the input is a matrix? Below is my code.

    fn main() -> Result<(), Box<dyn Error>> {
            let x = arr1(&[1.0]);
            let c = arr1(&[1.0]); 
            let b = arr1(&[10.0]);
            let u = arr1(&[15.0]);
            let l = arr1(&[-15.0]);
            let p = vec![false ];
            let A: CooMat<f64> = CooMat::<f64>::new(
                (1 , 1 ),
                vec![0],
                vec![0],
                vec![1.0]
            );
            variables! {
                vars:
                       a <= x;
            } // variables can also be added dynamically
            let solution = vars.maximise(  &a  )
                .using(default_solver) // multiple solvers available
                .with(constraint!( A * a == 5))
                .solve()?;
            println!("a={}   b={}", solution.value(a), solution.value(b));
            println!("a + b = {}", solution.eval(a + b));
            Ok(())
        }
    }
    

    Thank you for your help!

    opened by luli2949 2
  • How can I set model/solver options, especially for HiGHS ?

    How can I set model/solver options, especially for HiGHS ?

    I tried to set time limit option but I found there is no way to call highs::Model::set_option.

    With good_lp API the only chance I can get an instance of highs::Model is to call good_lp::solvers::highs::HighsProblem::into_inner as solve does internally.

    https://github.com/rust-or/good_lp/blob/65b4018726ca966fb97ec781eac7178e8f9b8f44/src/solvers/highs.rs#L65

    Then I can have several set_option calls and at the end solve method call. Even though I still cannot have HighsSolution instance as solution: highs::Solution field is private and HighsSolution does not have any explicit constructor like new method.

    https://github.com/rust-or/good_lp/blob/65b4018726ca966fb97ec781eac7178e8f9b8f44/src/solvers/highs.rs#L126

    Can you give me any suggestion to simultaneously use good_lp API as well as set Highs options?

    Thank you.

    opened by lucidfrontier45 1
  • `mul` with a float doesn't seem to to the right thing for binary variables

    `mul` with a float doesn't seem to to the right thing for binary variables

    I'm trying to build a sum based on binary variables and coefficients, but the resulting expression doesn't seem to be correct.

    let weights = vec![1.1, 1.2, 1.0, 0.9];
    let mut vars = variables!();
    
    let xs = vars.add_vector(variable().binary(), weights.len());
    
    let objective = Expression::sum((0..weights.len()).iter().map(|i| xs[i].mul(weights[i])));
    

    I would expect the xs variables to interpreted as ones or zeroes in the sum, i.e., the objective being the sum of the weights of the variables set to 1. However, it appears that the objective is capped at a value smaller than the sum of the weights.

    opened by urli 1
  • add interface to set HiGHS option

    add interface to set HiGHS option

    I added an API to set HiGHS options to the HighsProblem struct. This solves #18 .

    Currently the API is not safe enough, we can still add invalid key and value type. For example set_option("time_limit".to_string(), 10) will panic when solve method is called. Perhaps I should add explicit APIs like set_time_limit(value: f64). What do you think?

    opened by lucidfrontier45 4
  • Add support for RELP

    Add support for RELP

    I am using good_lp (at the moment with minilp). I am interested in exact solutions for rational numbers, and therefore I would like to experiment with relp. This is a request to add relp, or guide me doing so.

    If I understand correctly, a new "type" of good_lp::variable::VariableDefinition (other than the existing types "continuous" and "integer") would be required. Generally, I am not sure how to deal with the discrepancy of f64 vs. the number types in relp_num which relp uses.

    opened by lorenzleutgeb 3
  • Add SCIP support

    Add SCIP support

    SCIP is one of the commonly-used MI(N)LP solvers and could be of interest to the rust-or communities (free and source available for academic use).

    I'd be willing to help add it to lp-solvers if this is the appropriate repository

    enhancement 
    opened by matbesancon 5
Owner
Rust Operations Research
Collection of projects dedicated to operations research in Rust. Currently mainly focused integer and linear programming libraries.
Rust Operations Research
Radiate is a parallel genetic programming engine capable of evolving solutions to many problems as well as training learning algorithms.

Radiate Coming from Evolutionary Radiation. Evolutionary radiation is a rapid increase in the number of species with a common ancestor, characterized

kalivas 109 Dec 18, 2022
Rust implementation of real-coded GA for solving optimization problems and training of neural networks

revonet Rust implementation of real-coded genetic algorithm for solving optimization problems and training of neural networks. The latter is also know

Yury Tsoy 19 Aug 11, 2022
Common data structures and algorithms for competitive programming in Rust

algorithm-rs algorithm-rs is common data structures and algorithms for competitive programming in Rust. Contents TBA How To Contribute Contributions a

Chris Ohk 16 Dec 21, 2022
This crate implements fast route planning algorithms in Rust.

This crate implements fast route planning algorithms in Rust. Algorithms Currently implemented: Contraction Hierarchies: The implementat

Payas Rajan 4 Aug 15, 2021
Bazaar is a Rust crate for algorithmic futures trading

Bazaar is a Rust crate for algorithmic futures trading

Fabian Bösiger 7 Jun 7, 2022
ANISE provides an open-source and open-governed library and algorithmic specification for most computations for astrodynamics

ANISE provides an open-source and open-governed library and algorithmic specification for most computations for astrodynamics. It is heavily inspired by NAIF SPICE, and may be considered as an open-source modern rewrite of SPICE.

ANISE 4 Mar 9, 2022
Fast, parallel, extensible and adaptable genetic algorithms framework written in Rust

oxigen Oxigen is a parallel genetic algorithm framework implemented in Rust. The name comes from the merge of OXIdación (Rust translated to Spanish) a

Martín Pozo 146 Dec 18, 2022
A browser app that evolves vehicles using genetic algorithms, written in Rust and Bevy

Vehicle Evolver Deluxe This is a simulation that uses AI (to be specific: genetic algorithms) to try to build better and better vehicles. The vehicles

null 95 Dec 26, 2022
zine/book about bitmap drawing algorithms and math with code examples in Rust

A Bitmapper's Companion - zine/book about bitmap drawing algorithms and math with code examples in Rust A small zine/book written in LaTeX. In progres

Manos Pitsidianakis 42 Nov 8, 2022
Example of a genetic algorithm in Rust and Python

Example of a genetic algorithm in Rust and Python Monkey typewriter Finding the phrase 'To be or not to be. That is the question.' Inspired by the exa

sotrh 2 Jan 27, 2022
A Rust implementation the HOTP and TOTP Algorithms

xotp A Rust implementation the HOTP and TOTP Algorithms. HOTP was implemented in accordance with RFC4226 TOTP was implemented in accordance with RFC62

Tejas Mehta 3 Jan 21, 2022
hubpack is an algorithm for converting Rust values to bytes and back.

hubpack is an algorithm for converting Rust values to bytes and back. It was originally designed for encoding messages sent between embedded programs. It is designed for use with serde.

Cliff L. Biffle 6 Nov 11, 2022
Execute genetic algorithm (GA) simulations in a customizable and extensible way.

genevo genevo provides building blocks to run simulations of optimization and search problems using genetic algorithms (GA). The vision for genevo is

Innoave 110 Dec 21, 2022
This library provides implementations of many algorithms and data structures that are useful for bioinformatics.

This library provides implementations of many algorithms and data structures that are useful for bioinformatics. All provided implementations are rigorously tested via continuous integration.

Rust-Bio 1.2k Dec 26, 2022
Library that implements different versions of PPMD algorithm (compress and decompress)

ppmd-rs Library that implements different versions of PPMD algorithm (compress and decompress) Dependencies Rust 1.58 or newer Cargo How to build Clon

Alexander Zaitsev 3 Jun 20, 2022
Online algorithm for mean and variance, with support for uneven weights

welford Online algorithm for mean and variance, with support for uneven weights. This implements the Welford's online algorithm for computing mean and

Felipe S. S. Schneider 1 Sep 8, 2022
darwin-rs, evolutionary algorithms with rust

darwin-rs This library allows you to write evolutionary algorithms (EA) using the Rust programming language. Written by Willi Kappler, License: MIT -

Willi Kappler 95 Jan 1, 2023
Genetic Algorithm library in Rust

RsGenetic Summary and Features RsGenetic is a framework for executing genetic algorithms in Rust. It is designed to have a simple but modular API. Exa

Mathieu De Coster 74 Dec 27, 2022
Raft implementation in Rust

rsraft Raft implementation in Rust. The aim of this project is implementing the Raft Consensus Algorithm as described in the paper, with the goal of f

Lauro Caetano 42 Dec 24, 2022