Skip to main content

minion_sys/
lib.rs

1//! This crate provides low level Rust bindings to the [Minion](https://github.com/minion/minion)
2//! constraint solver.
3//!
4//! # Examples
5//!
6//! Consider the following Minion problem:
7//!
8//! ```plaintext
9//! MINION 3
10//! **VARIABLES**
11//! DISCRETE x #
12//! {1..3}
13//! DISCRETE y #
14//! {2..4}
15//! DISCRETE z #
16//! {1..5}
17//! **SEARCH**
18//! PRINT[[x],[y],[z]]
19//! VARORDER STATIC [x, y, z]
20//! **CONSTRAINTS**
21//! sumleq([x,y,z],4)
22//! ineq(x, y, -1)
23//! **EOF**
24//! ```
25//!
26//! This can be solved in Rust like so:
27//!
28//! ```
29//! use minion_sys::ast::*;
30//! use minion_sys::run_minion;
31//! use std::collections::HashMap;
32//!
33//! // Collect solutions using a closure — no globals needed.
34//! let mut all_solutions: Vec<HashMap<VarName,Constant>> = vec![];
35//!
36//! let callback: Box<dyn FnMut(HashMap<VarName,Constant>) -> bool> =
37//!     Box::new(|solutions| {
38//!         all_solutions.push(solutions);
39//!         true
40//!     });
41//!
42//! // Build and run the model.
43//! let mut model = Model::new();
44//! model
45//!     .named_variables
46//!     .add_var("x".to_owned(), VarDomain::Bound(1, 3));
47//! model
48//!     .named_variables
49//!     .add_var("y".to_owned(), VarDomain::Bound(2, 4));
50//! model
51//!     .named_variables
52//!     .add_var("z".to_owned(), VarDomain::Bound(1, 5));
53//!
54//! let leq = Constraint::SumLeq(
55//!     vec![
56//!         Var::NameRef("x".to_owned()),
57//!         Var::NameRef("y".to_owned()),
58//!         Var::NameRef("z".to_owned()),
59//!     ],
60//!     Var::ConstantAsVar(4),
61//! );
62//!
63//! let geq = Constraint::SumGeq(
64//!     vec![
65//!         Var::NameRef("x".to_owned()),
66//!         Var::NameRef("y".to_owned()),
67//!         Var::NameRef("z".to_owned()),
68//!     ],
69//!     Var::ConstantAsVar(4),
70//! );
71//!
72//! let ineq = Constraint::Ineq(
73//!     Var::NameRef("x".to_owned()),
74//!     Var::NameRef("y".to_owned()),
75//!     Constant::Integer(-1),
76//! );
77//!
78//! model.constraints.push(leq);
79//! model.constraints.push(geq);
80//! model.constraints.push(ineq);
81//!
82//! let _solver_ctx = run_minion(model, callback).expect("Error occurred");
83//!
84//! let solution_set_1 = &all_solutions[0];
85//! let x1 = solution_set_1.get("x").unwrap();
86//! let y1 = solution_set_1.get("y").unwrap();
87//! let z1 = solution_set_1.get("z").unwrap();
88//!
89//! assert_eq!(all_solutions.len(),1);
90//! assert_eq!(*x1,Constant::Integer(1));
91//! assert_eq!(*y1,Constant::Integer(2));
92//! assert_eq!(*z1,Constant::Integer(1));
93//! ```
94//!
95//! ## `PRINT` and `VARORDER`
96//!
97//! These bindings have no replacement for Minion's `PRINT` and `VARORDER` statements — every
98//! variable added to the model (excluding auxiliary variables) is considered a search
99//! variable. Solutions are returned through the [callback](Callback) as a `HashMap`.
100//!
101//! ## Search options
102//!
103//! Use [`run_minion_with_options`] to set a random seed, variable/value ordering
104//! heuristics, or propagation levels:
105//!
106//! ```
107//! use minion_sys::{RunOptions, run_minion_with_options, VarOrder, ValOrder};
108//! # use minion_sys::ast::*;
109//! # use std::collections::HashMap;
110//! # let model = Model::new();
111//! # let callback = Box::new(|_: HashMap<VarName, Constant>| true);
112//! let opts = RunOptions {
113//!     seed: Some(42),
114//!     var_order: VarOrder::Wdeg,
115//!     val_order: ValOrder::Random,
116//!     ..Default::default()
117//! };
118//! let _ctx = run_minion_with_options(model, opts, callback).unwrap();
119//! ```
120//!
121//! ## Tuple tables
122//!
123//! Extensional constraints like [`ast::Constraint::Str2Plus`] reference named tuple tables
124//! registered on the model:
125//!
126//! ```
127//! use minion_sys::ast::*;
128//! # let mut model = Model::new();
129//! # model.named_variables.add_var("x".into(), VarDomain::Bool);
130//! # model.named_variables.add_var("y".into(), VarDomain::Bool);
131//! model.add_tuple_table("allowed".into(), vec![
132//!     vec![Constant::Integer(0), Constant::Integer(1)],
133//!     vec![Constant::Integer(1), Constant::Integer(0)],
134//! ]);
135//! model.constraints.push(Constraint::Str2Plus(
136//!     vec![Var::NameRef("x".into()), Var::NameRef("y".into())],
137//!     Var::NameRef("allowed".into()),
138//! ));
139//! ```
140//!
141//! ## Mid-search mutation
142//!
143//! [`run_minion_midsearch`] lets callbacks add variables or constraints during search
144//! via a [`MidSearchContext`] handle:
145//!
146//! ```
147//! use minion_sys::{run_minion_midsearch, MidSearchContext};
148//! use minion_sys::ast::*;
149//! use std::collections::HashMap;
150//! # let mut model = Model::new();
151//! # model.named_variables.add_var("x".into(), VarDomain::Discrete(0, 1));
152//! let _ctx = run_minion_midsearch(model, Box::new(|midctx, sol| {
153//!     // add a fresh variable on the first solution callback
154//!     if !sol.contains_key("y") {
155//!         midctx.add_var("y", VarDomain::Discrete(0, 1)).unwrap();
156//!     }
157//!     true
158//! })).unwrap();
159//! ```
160
161pub use run::*;
162
163pub mod error;
164mod ffi;
165
166pub mod ast;
167mod run;
168
169mod scoped_ptr;
170
171pub mod print;