good_lp/
lib.rs

1#![deny(missing_docs)]
2#![forbid(unsafe_code)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4//!  A Linear Programming modeler that is easy to use, performant with large problems, and well-typed.
5//!
6//!  ```rust
7//!  use good_lp::{variables, variable, default_solver, SolverModel, Solution};
8//!
9//! // Create variables in a readable format with a macro...
10//! variables!{
11//!     vars:
12//!         a <= 1;
13//!         2 <= b <= 4;
14//! }
15//! // ... or add variables programmatically
16//! vars.add(variable().min(2).max(9));
17//!
18//!  let solution = vars.maximise(10 * (a - b / 5) - b)
19//!      .using(default_solver)
20//!      .with(a + 2. << b) // or (a + 2).leq(b)
21//!      .with(1 + a >> 4. - b)
22//!      .solve()?;
23//!
24//! # use float_eq::assert_float_eq;
25//!  assert_float_eq!(solution.value(a), 1., abs <= 1e-8);
26//!  assert_float_eq!(solution.value(b), 3., abs <= 1e-8);
27//!  # Ok::<_, good_lp::ResolutionError>(())
28//!  ```
29//!
30//! ## Solvers
31//!
32//! This crate supports multiple solvers,
33//! that can be activated using [feature flags](https://docs.rs/crate/good_lp/latest/features).
34//!
35//! ## Usage
36//!
37//! You initially create your variables using [variables] and [ProblemVariables::add].
38//!
39//! Then you create your objective function.If it's large, you can write rust functions
40//! to split complex expressions into components.
41//!
42//! ```
43//! use good_lp::{Expression, Variable};
44//!
45//! fn total_cost(energy: Variable, time: Variable) -> Expression {
46//! #   let dollars_per_hour = 0;
47//!     energy_cost(energy) + dollars_per_hour * time
48//! }
49//!
50//! fn energy_cost(energy: Variable) -> Expression {
51//! #   let fetch_energy_price = |_| 0.;
52//!     let price = fetch_energy_price(energy);
53//!     energy * price
54//! }
55//! ```
56//!
57//! Then you create a [solver](solvers) problem model instance
58//! ```
59//! # let my_variables = good_lp::variables!();
60//! # let my_objective = good_lp::Expression::from(0);
61//! # let my_solver = good_lp::default_solver;
62//! let mut model = my_variables.minimise(my_objective).using(my_solver);
63//! ```
64//!
65//! Then you add constraints and solve your problem using the methods in [SolverModel].
66//!
67
68pub use affine_expression_trait::IntoAffineExpression;
69pub use cardinality_constraint_solver_trait::CardinalityConstraintSolver;
70pub use constraint::Constraint;
71pub use expression::Expression;
72#[cfg(not(any(
73    feature = "coin_cbc",
74    feature = "microlp",
75    feature = "lpsolve",
76    feature = "highs",
77    feature = "scip",
78    feature = "cplex-rs",
79)))]
80#[cfg(feature = "clarabel")]
81pub use solvers::clarabel::clarabel as default_solver;
82#[cfg_attr(docsrs, doc(cfg(feature = "clarabel")))]
83#[cfg(feature = "clarabel")]
84pub use solvers::clarabel::clarabel;
85#[cfg_attr(docsrs, doc(cfg(feature = "coin_cbc")))]
86#[cfg(feature = "coin_cbc")]
87pub use solvers::coin_cbc::coin_cbc;
88#[cfg(feature = "coin_cbc")]
89/// When the "coin_cbc" cargo feature is present, it is used as the default solver
90pub use solvers::coin_cbc::coin_cbc as default_solver;
91#[cfg(not(any(
92    feature = "coin_cbc",
93    feature = "microlp",
94    feature = "lpsolve",
95    feature = "highs"
96)))]
97#[cfg(feature = "cplex-rs")]
98pub use solvers::cplex::cplex as default_solver;
99#[cfg(feature = "highs")]
100#[cfg_attr(docsrs, doc(cfg(feature = "highs")))]
101pub use solvers::highs::highs;
102#[cfg(not(any(feature = "coin_cbc", feature = "microlp", feature = "lpsolve")))]
103#[cfg(feature = "highs")]
104/// When the "highs" cargo feature is present, highs is used as the default solver
105pub use solvers::highs::highs as default_solver;
106#[cfg(feature = "lp-solvers")]
107#[cfg_attr(docsrs, doc(cfg(feature = "lp-solvers")))]
108pub use solvers::lp_solvers::LpSolver;
109#[cfg(feature = "lpsolve")]
110#[cfg_attr(docsrs, doc(cfg(feature = "lpsolve")))]
111pub use solvers::lpsolve::lp_solve;
112#[cfg(not(any(feature = "coin_cbc", feature = "microlp")))]
113#[cfg(feature = "lpsolve")]
114/// When the "lpsolve" cargo feature is present, lpsolve is used as the default solver
115pub use solvers::lpsolve::lp_solve as default_solver;
116#[cfg(feature = "microlp")]
117#[cfg_attr(docsrs, doc(cfg(feature = "microlp")))]
118pub use solvers::microlp::microlp;
119#[cfg(not(feature = "coin_cbc"))]
120#[cfg(feature = "microlp")]
121/// When the "coin_cbc" cargo feature is absent, microlp is used as the default solver
122pub use solvers::microlp::microlp as default_solver;
123#[cfg(feature = "scip")]
124#[cfg_attr(docsrs, doc(cfg(feature = "highs")))]
125pub use solvers::scip::scip;
126#[cfg(not(any(
127    feature = "coin_cbc",
128    feature = "microlp",
129    feature = "lpsolve",
130    feature = "highs"
131)))]
132#[cfg(feature = "scip")]
133pub use solvers::scip::scip as default_solver;
134
135pub use solvers::{
136    solver_name, DualValues, ModelWithSOS1, ObjectiveDirection, ResolutionError, Solution,
137    SolutionStatus, SolutionWithDual, Solver, SolverModel, StaticSolver, WithInitialSolution,
138    WithMipGap, WithTimeLimit,
139};
140pub use variable::{variable, ProblemVariables, Variable, VariableDefinition};
141
142#[cfg(not(any(
143    feature = "coin_cbc",
144    feature = "microlp",
145    feature = "lpsolve",
146    feature = "highs",
147    feature = "scip",
148)))]
149#[cfg(feature = "lp-solvers")]
150/// Default solvers for the 'lp-solvers' feature: a solver that calls Cbc as an external command
151#[allow(non_upper_case_globals)]
152pub const default_solver: LpSolver<
153    solvers::lp_solvers::StaticSolver<solvers::lp_solvers::AllSolvers>,
154> = LpSolver(solvers::lp_solvers::StaticSolver::new());
155
156#[cfg(not(any(
157    feature = "coin_cbc",
158    feature = "microlp",
159    feature = "lpsolve",
160    feature = "highs",
161    feature = "lp-solvers",
162    feature = "scip",
163    feature = "cplex-rs",
164    feature = "clarabel",
165)))]
166compile_error!(
167    "No solver available. \
168You need to activate at least one solver feature flag in good_lp. \
169You can do by adding the following to your Cargo.toml :
170[dependencies]
171good_lp = { version = \"*\", features = [\"microlp\"] }
172"
173);
174
175/// The "lpsolve" and "cplex-rs" cargo feature are incompatible,
176/// since the above crates link statically to c libraries defining some
177/// common symbols
178#[cfg(all(feature = "lpsolve", feature = "cplex-rs",))]
179compile_error!(
180    "'lpsolve' and 'cplex-rs' features are incompatible. \
181Please select just one of the two. If you need all compatible solvers, use the 'all_deafult_solvers' feature. \
182"
183);
184
185mod expression;
186#[macro_use]
187pub mod variable;
188mod affine_expression_trait;
189mod cardinality_constraint_solver_trait;
190pub mod constraint;
191pub mod solvers;
192mod variables_macro;