1use std::cell::Ref;
4
5use minion_ast::Model as MinionModel;
6use minion_rs::ast as minion_ast;
7use minion_rs::error::MinionError;
8use minion_rs::{get_from_table, run_minion};
9
10use crate::ast as conjure_ast;
11use crate::ast::Declaration;
12use crate::solver::SolverError::*;
13use crate::solver::SolverFamily;
14use crate::solver::SolverMutCallback;
15use crate::solver::{SolverCallback, SolverError};
16use crate::stats::SolverStats;
17use crate::Model as ConjureModel;
18
19pub fn model_to_minion(model: ConjureModel) -> Result<MinionModel, SolverError> {
21 let mut minion_model = MinionModel::new();
22 load_symbol_table(&model, &mut minion_model)?;
23 load_constraints(&model, &mut minion_model)?;
24 Ok(minion_model)
25}
26
27fn load_symbol_table(
29 conjure_model: &ConjureModel,
30 minion_model: &mut MinionModel,
31) -> Result<(), SolverError> {
32 for (name, decl) in conjure_model
33 .as_submodel()
34 .symbols()
35 .clone()
36 .into_iter_local()
37 {
38 let Some(var) = decl.as_var() else {
39 continue;
40 }; load_var(&name, var, minion_model)?;
43 }
44 Ok(())
45}
46
47fn load_var(
49 name: &conjure_ast::Name,
50 var: &conjure_ast::DecisionVariable,
51 minion_model: &mut MinionModel,
52) -> Result<(), SolverError> {
53 match &var.domain {
54 conjure_ast::Domain::IntDomain(ranges) => load_intdomain_var(name, ranges, minion_model),
55 conjure_ast::Domain::BoolDomain => load_booldomain_var(name, minion_model),
56 x => Err(ModelFeatureNotSupported(format!("{:?}", x))),
57 }
58}
59
60fn load_intdomain_var(
62 name: &conjure_ast::Name,
63 ranges: &[conjure_ast::Range<i32>],
64 minion_model: &mut MinionModel,
65) -> Result<(), SolverError> {
66 let str_name = name_to_string(name.to_owned());
67
68 if ranges.len() != 1 {
69 return Err(ModelFeatureNotImplemented(format!(
70 "variable {:?} has {:?} ranges. Multiple ranges / SparseBound is not yet supported.",
71 str_name,
72 ranges.len()
73 )));
74 }
75
76 let range = ranges.first().ok_or(ModelInvalid(format!(
77 "variable {:?} has no range",
78 str_name
79 )))?;
80
81 let (low, high) = match range {
82 conjure_ast::Range::Bounded(x, y) => Ok((x.to_owned(), y.to_owned())),
83 conjure_ast::Range::Single(x) => Ok((x.to_owned(), x.to_owned())),
84 #[allow(unreachable_patterns)]
85 x => Err(ModelFeatureNotSupported(format!("{:?}", x))),
86 }?;
87
88 _try_add_var(
89 str_name.to_owned(),
90 minion_ast::VarDomain::Bound(low, high),
91 minion_model,
92 )
93}
94
95fn load_booldomain_var(
97 name: &conjure_ast::Name,
98 minion_model: &mut MinionModel,
99) -> Result<(), SolverError> {
100 let str_name = name_to_string(name.to_owned());
101 _try_add_var(
102 str_name.to_owned(),
103 minion_ast::VarDomain::Bool,
104 minion_model,
105 )
106}
107
108fn _try_add_var(
109 name: minion_ast::VarName,
110 domain: minion_ast::VarDomain,
111 minion_model: &mut MinionModel,
112) -> Result<(), SolverError> {
113 minion_model
114 .named_variables
115 .add_var(name.clone(), domain)
116 .ok_or(ModelInvalid(format!(
117 "variable {:?} is defined twice",
118 name
119 )))
120}
121
122fn name_to_string(name: conjure_ast::Name) -> String {
123 match name {
124 conjure_ast::Name::UserName(x) => x,
125 conjure_ast::Name::MachineName(x) => format!("__conjure_machine_name_{}", x),
126 }
127}
128
129fn load_constraints(
131 conjure_model: &ConjureModel,
132 minion_model: &mut MinionModel,
133) -> Result<(), SolverError> {
134 for expr in conjure_model.as_submodel().constraints().iter() {
135 use crate::metadata::Metadata;
138 use conjure_ast::Atom;
139 use conjure_ast::Expression as Expr;
140 use conjure_ast::Literal::*;
141
142 match expr {
143 Expr::Atomic(_, Atom::Literal(Bool(false))) => {
145 minion_model.constraints.push(minion_ast::Constraint::False);
146 }
147 Expr::Atomic(_, Atom::Literal(Bool(true))) => {
149 minion_model.constraints.push(minion_ast::Constraint::True);
150 }
151
152 _ => {
153 load_expr(expr.to_owned(), minion_model)?;
154 }
155 }
156 }
157 Ok(())
158}
159
160fn load_expr(
162 expr: conjure_ast::Expression,
163 minion_model: &mut MinionModel,
164) -> Result<(), SolverError> {
165 minion_model.constraints.push(parse_expr(expr)?);
166 Ok(())
167}
168
169fn parse_expr(expr: conjure_ast::Expression) -> Result<minion_ast::Constraint, SolverError> {
171 match expr {
172 conjure_ast::Expression::Atomic(_metadata, atom) => Ok(minion_ast::Constraint::WLiteral(
173 parse_atom(atom)?,
174 minion_ast::Constant::Integer(1),
175 )),
176 conjure_ast::Expression::FlatSumLeq(_metadata, lhs, rhs) => Ok(
177 minion_ast::Constraint::SumLeq(parse_atoms(lhs)?, parse_atom(rhs)?),
178 ),
179 conjure_ast::Expression::FlatSumGeq(_metadata, lhs, rhs) => Ok(
180 minion_ast::Constraint::SumGeq(parse_atoms(lhs)?, parse_atom(rhs)?),
181 ),
182 conjure_ast::Expression::FlatIneq(_metadata, a, b, c) => Ok(minion_ast::Constraint::Ineq(
183 parse_atom(a)?,
184 parse_atom(b)?,
185 parse_literal(c)?,
186 )),
187 conjure_ast::Expression::Neq(_metadata, a, b) => Ok(minion_ast::Constraint::DisEq(
188 parse_atomic_expr(*a)?,
189 parse_atomic_expr(*b)?,
190 )),
191 conjure_ast::Expression::MinionDivEqUndefZero(_metadata, a, b, c) => Ok(
192 minion_ast::Constraint::DivUndefZero((parse_atom(a)?, parse_atom(b)?), parse_atom(c)?),
193 ),
194 conjure_ast::Expression::MinionModuloEqUndefZero(_metadata, a, b, c) => {
195 Ok(minion_ast::Constraint::ModuloUndefZero(
196 (parse_atom(a)?, parse_atom(b)?),
197 parse_atom(c)?,
198 ))
199 }
200 conjure_ast::Expression::Or(_metadata, exprs) => Ok(minion_ast::Constraint::WatchedOr(
201 exprs
202 .iter()
203 .map(|x| parse_expr(x.to_owned()))
204 .collect::<Result<Vec<minion_ast::Constraint>, SolverError>>()?,
205 )),
206 conjure_ast::Expression::And(_metadata, exprs) => Ok(minion_ast::Constraint::WatchedAnd(
207 exprs
208 .iter()
209 .map(|x| parse_expr(x.to_owned()))
210 .collect::<Result<Vec<minion_ast::Constraint>, SolverError>>()?,
211 )),
212 conjure_ast::Expression::Eq(_metadata, a, b) => Ok(minion_ast::Constraint::Eq(
213 parse_atomic_expr(*a)?,
214 parse_atomic_expr(*b)?,
215 )),
216
217 conjure_ast::Expression::FlatWatchedLiteral(_metadata, name, k) => Ok(
218 minion_ast::Constraint::WLiteral(parse_name(name)?, parse_literal(k)?),
219 ),
220 conjure_ast::Expression::MinionReify(_metadata, e, v) => Ok(minion_ast::Constraint::Reify(
221 Box::new(parse_expr(*e)?),
222 parse_atom(v)?,
223 )),
224
225 conjure_ast::Expression::MinionReifyImply(_metadata, e, v) => Ok(
226 minion_ast::Constraint::ReifyImply(Box::new(parse_expr(*e)?), parse_atom(v)?),
227 ),
228
229 conjure_ast::Expression::AuxDeclaration(_metadata, name, expr) => Ok(
230 minion_ast::Constraint::Eq(parse_name(name)?, parse_atomic_expr(*expr)?),
231 ),
232
233 conjure_ast::Expression::FlatMinusEq(_metadata, a, b) => Ok(
234 minion_ast::Constraint::MinusEq(parse_atom(a)?, parse_atom(b)?),
235 ),
236
237 conjure_ast::Expression::FlatProductEq(_metadata, a, b, c) => Ok(
238 minion_ast::Constraint::Product((parse_atom(a)?, parse_atom(b)?), parse_atom(c)?),
239 ),
240 conjure_ast::Expression::FlatWeightedSumLeq(_metadata, coeffs, vars, total) => {
241 Ok(minion_ast::Constraint::WeightedSumLeq(
242 parse_literals(coeffs)?,
243 parse_atoms(vars)?,
244 parse_atom(total)?,
245 ))
246 }
247 conjure_ast::Expression::FlatWeightedSumGeq(_metadata, coeffs, vars, total) => {
248 Ok(minion_ast::Constraint::WeightedSumGeq(
249 parse_literals(coeffs)?,
250 parse_atoms(vars)?,
251 parse_atom(total)?,
252 ))
253 }
254 conjure_ast::Expression::FlatAbsEq(_metadata, x, y) => {
255 Ok(minion_ast::Constraint::Abs(parse_atom(x)?, parse_atom(y)?))
256 }
257 conjure_ast::Expression::MinionPow(_, x, y, z) => Ok(minion_ast::Constraint::Pow(
258 (parse_atom(x)?, parse_atom(y)?),
259 parse_atom(z)?,
260 )),
261 x => Err(ModelFeatureNotSupported(format!("{:?}", x))),
262 }
263}
264
265fn parse_atomic_expr(expr: conjure_ast::Expression) -> Result<minion_ast::Var, SolverError> {
266 let conjure_ast::Expression::Atomic(_, atom) = expr else {
267 return Err(ModelInvalid(format!(
268 "expected atomic expression, got {:?}",
269 expr
270 )));
271 };
272
273 parse_atom(atom)
274}
275
276fn parse_atoms(exprs: Vec<conjure_ast::Atom>) -> Result<Vec<minion_ast::Var>, SolverError> {
277 let mut minion_vars: Vec<minion_ast::Var> = vec![];
278 for expr in exprs {
279 let minion_var = parse_atom(expr)?;
280 minion_vars.push(minion_var);
281 }
282 Ok(minion_vars)
283}
284
285fn parse_atom(atom: conjure_ast::Atom) -> Result<minion_ast::Var, SolverError> {
286 match atom {
287 conjure_ast::Atom::Literal(l) => {
288 Ok(minion_ast::Var::ConstantAsVar(parse_literal_as_int(l)?))
289 }
290 conjure_ast::Atom::Reference(name) => Ok(parse_name(name))?,
291 }
292}
293
294fn parse_literal_as_int(k: conjure_ast::Literal) -> Result<i32, SolverError> {
295 match k {
296 conjure_ast::Literal::Int(n) => Ok(n),
297 conjure_ast::Literal::Bool(true) => Ok(1),
298 conjure_ast::Literal::Bool(false) => Ok(0),
299 x => Err(ModelInvalid(format!(
300 "expected a literal but got `{0:?}`",
301 x
302 ))),
303 }
304}
305
306fn parse_literals(
307 literals: Vec<conjure_ast::Literal>,
308) -> Result<Vec<minion_ast::Constant>, SolverError> {
309 let mut minion_constants: Vec<minion_ast::Constant> = vec![];
310 for literal in literals {
311 let minion_constant = parse_literal(literal)?;
312 minion_constants.push(minion_constant);
313 }
314 Ok(minion_constants)
315}
316
317fn parse_literal(k: conjure_ast::Literal) -> Result<minion_ast::Constant, SolverError> {
318 Ok(minion_ast::Constant::Integer(parse_literal_as_int(k)?))
319}
320
321fn parse_name(name: conjure_ast::Name) -> Result<minion_ast::Var, SolverError> {
322 Ok(minion_ast::Var::NameRef(name_to_string(name)))
323}