linprog

A Rust library for optimizing linear programming (LP) models, implemented using Dantzig's simplex algorithm. Linprog provides utilities to create and optimize dynamic LP models.

This library does not (yet :turtle:) support mixed integer programming.

Linprog will be available on crates.io soon.

Table of contents

Usage

Add this to your Cargo.toml: toml [dependencies] linprog = "0.3" Then include it in your code like this: rust use linprog;

Understanding a LP's lifetime in linprog

In this library a linear program is represented by a datatype called Model, created like this: rust let mut model = Model::new("My LP", Objective::Max); The Model's lifetime follows three strictly seperated phases: - In the first (and initially set) phase, variables can be registered. rust let mut vars: Vec<Var> = vec![]; vars.push(model.reg_var(2.0)); // --snip-- - In the second phase, constraints can be registered. rust // model.update(); model.reg_constr(vec![Summand(1.0, &vars[0])], Operator::Le, 10.0); // --snip-- - In the third phase, the Model can be optimized. rust // model.update(); model.optimize(); The Models's phase can be explicitly updated to the next phase using the update method. Or implicitly, by calling the method for the next phase.

After the variables or constraints are submitted to the Model, they can not be changed again (The phases can not be reverted or modified).

Example

The code below can be used to optimize the following model: max. 3x + 5y st. x + 2y <= 170 3y <= 180 Rust implementation: ```rust let mut model = Model::new("Readme example", Objective::Max); let mut vars: Vec = vec![];

vars.push(model.regvar(3.0)); vars.push(model.regvar(5.0));

model.regconstr( vec![Summand(1.0, &vars[0]), Summand(2.0, &vars[1])], Operator::Le, 170.0, ); model.regconstr( vec![Summand(1.0, &vars[0]), Summand(1.0, &vars[1])], Operator::Le, 150.0, ); model.reg_constr( vec![Summand(0.0, &vars[0]), Summand(3.0, &vars[1])], Operator::Le, 180.0, );

model.optimize(); print!("{}", model); This program will print out the following: Model "Readme example" [optimized]: Optimum: 490 Variable "1": 130 Variable "2": 20 ```

Example with story

Lets say a company produces three products: - Product A selling at 50$ - Product B selling at 100$ - Product C selling at 110$

The company has three machines: - Machine X with a maximum operating minutes per week of 2500 - Machine Y with a maximum operating minutes per week of 2000 - Machine Z With a maximum operating minutes per week of 450

Every product needs to be processed by one of the machines for a specific amount of time: - One unit of A needs - 10   min. at X - 4     min. at Y - 1     min. at Z - One unit of B needs - 5     min. at X - 10   min. at Y - 1.5 min. at Z - One unit of C needs - 6     min. at X - 9     min. at Y - 3     min. at Z

The question is: How mutch units does the company want to produce for each product in order to maximize their profit?

In the Rust program, the data could look like this: ```rust let products: HashMap<&str, f64> = [ ("Product A", 50.0), ("Product B", 100.0), ("Product C", 110.0), ].iter().cloned().collect();

let machines: HashMap<&str, f64> = [ ("Machine X", 2500.0), ("Machine Y", 2000.0), ("Machine Z", 450.0), ].iter().cloned().collect();

let mut timeneeded: HashMap<(&str, &str), f64> = HashMap::new(); timeneeded.insert(("Product A", "Machine X"), 10.0); timeneeded.insert(("Product A", "Machine Y"), 4.0); timeneeded.insert(("Product A", "Machine Z"), 1.0);

timeneeded.insert(("Product B", "Machine X"), 5.0); timeneeded.insert(("Product B", "Machine Y"), 10.0); time_needed.insert(("Product B", "Machine Z"), 1.5);

timeneeded.insert(("Product C", "Machine X"), 6.0); timeneeded.insert(("Product C", "Machine Y"), 9.0); timeneeded.insert(("Product C", "Machine Z"), 3.0); A less verbose way to define the data might look like this: rust let productprice: [f64;3] = [50.0, 100.0, 110.0]; let machinemaxworkload: [f64;3] = [2500.0, 2000.0, 450.0]; let prodmachinetime_needed: [[f64;3];3] = [ [10.0, 4.0, 1.0], [5.0, 10.0, 1.5], [6.0, 9.0, 3.0], ]; ``` For the sake of this example, I will use the previous of the two versions.

Now onto the Model's construction: rust let mut model = Model::new("ABC_Company", Objective::Max); let mut vars: HashMap<&str, Var> = HashMap::new(); Then register the variables with names and prices: rust for (product, &price) in &products { vars.insert(product, model.reg_var_with_name(price, product)); } Register the constraints: rust for (&machine, &max_time) in &machines { let mut sum: Vec<Summand> = Vec::new(); for (&product, _) in &products { sum.push(Summand(time_needed[&(product, machine)], &vars[product])); } model.reg_constr(sum, Operator::Le, max_time); } Finally the model gets optimized and the results get printed: rust model.optimize(); print!("{}", model); The output will look like this: Model "ABC_Company" [optimized]: Optimum: 22738.095238095237 Variable "Product C": 47.61904761904763 Variable "Product A": 178.57142857142856 Variable "Product B": 85.71428571428572 A customized display of the solution could be done in this way: rust println!("\nThe optimum is at {:.*}$.", 2, model.optimum().unwrap()); for (product, var) in &vars { println!("We need to produce {} units of product {}.", model.x(&var).unwrap().floor(), product); } Leading to the following output: The optimum is at 22738.10$. We need to produce 85 units of product Product B. We need to produce 178 units of product Product A. We need to produce 47 units of product Product C. Make of this what you want :ok_woman:

How you can help

Authors

License

This project is licensed under the MIT License - see the LICENSE file for details