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;