Skip to content

Commit 542c89e

Browse files
committed
feat!: add Submodel
Add `Submodel`, and refactor `Model` to be a wrapper over `Submodel`. This commit is part of on-going work to add lexical scope to Conjure Oxide and is a follow up to 8636432 (feat!: add parent symbol tables (#680), 2025-02-17). DETAILS A `Submodel` represents a particular scope and holds the symbol-table and constraints tree for that scope. This commit refactors `Model` to be a wrapper over `Submodel`, and removes methods operating on constraints and the symbol table from `Model`, placing them in `Submodel` instead. A `Model` can be borrowed as a `Submodel` using `as_submodel()` and `as_submodel_mut()`. The language semantics of a top level model and a sub-model are identical, so treating it as `Submodel` in most cases is valid. `Model` is a separate type than `Submodel` for the following reasons: + It will hold global-only information in the future, such as dominance constraints. + It holds a pointer to the context. + We need special initialisation and de-serialisation logic for the top level model that we do not want for `Submodel`. See `SerdeModel`.
1 parent 4043234 commit 542c89e

22 files changed

+407
-222
lines changed

conjure_oxide/examples/solver-hello-minion.rs

+3-12
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ use std::collections::HashMap;
1313
#[allow(clippy::unwrap_used)]
1414
pub fn main() {
1515
use conjure_core::solver::SolverFamily;
16-
use conjure_core::{
17-
ast::pretty::pretty_expressions_as_top_level, parse::get_example_model,
18-
rule_engine::resolve_rule_sets,
19-
};
16+
use conjure_core::{parse::get_example_model, rule_engine::resolve_rule_sets};
2017
use conjure_core::{
2118
rule_engine::rewrite_model,
2219
solver::{adaptors, Solver},
@@ -25,19 +22,13 @@ pub fn main() {
2522

2623
// Load an example model and rewrite it with conjure oxide.
2724
let model = get_example_model("div-05").unwrap();
28-
println!(
29-
"Input model: \n {} \n",
30-
pretty_expressions_as_top_level(&model.get_constraints_vec())
31-
);
25+
println!("Input model: \n {model} \n",);
3226

3327
// TODO: We will have a nicer way to do this in the future
3428
let rule_sets = resolve_rule_sets(SolverFamily::Minion, &get_default_rule_sets()).unwrap();
3529

3630
let model = rewrite_model(&model, &rule_sets).unwrap();
37-
println!(
38-
"Rewritten model: \n {} \n",
39-
pretty_expressions_as_top_level(&model.get_constraints_vec())
40-
);
31+
println!("Rewritten model: \n {model} \n",);
4132

4233
// To tell the `Solver` type what solver to use, you pass it a `SolverAdaptor`.
4334
// Here we use Minion.

conjure_oxide/src/utils/essence_parser.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![allow(clippy::legacy_numeric_constants)]
2-
use conjure_core::ast::declaration::Declaration;
2+
use conjure_core::ast::Declaration;
33
use conjure_core::error::Error;
44
use std::fs;
55
use std::rc::Rc;
@@ -23,7 +23,7 @@ pub fn parse_essence_file_native(
2323
) -> Result<Model, EssenceParseError> {
2424
let (tree, source_code) = get_tree(path, filename, extension);
2525

26-
let mut model = Model::new_empty(context);
26+
let mut model = Model::new(context);
2727
let root_node = tree.root_node();
2828
for statement in named_children(&root_node) {
2929
match statement.kind() {
@@ -32,6 +32,7 @@ pub fn parse_essence_file_native(
3232
let var_hashmap = parse_find_statement(statement, &source_code);
3333
for (name, decision_variable) in var_hashmap {
3434
model
35+
.as_submodel_mut()
3536
.symbols_mut()
3637
.insert(Rc::new(Declaration::new_var(name, decision_variable)));
3738
}
@@ -43,12 +44,12 @@ pub fn parse_essence_file_native(
4344
constraint_vec.push(parse_constraint(constraint, &source_code));
4445
}
4546
}
46-
model.add_constraints(constraint_vec);
47+
model.as_submodel_mut().add_constraints(constraint_vec);
4748
}
4849
"e_prime_label" => {}
4950
"letting_statement_list" => {
5051
let letting_vars = parse_letting_statement(statement, &source_code);
51-
model.symbols_mut().extend(letting_vars);
52+
model.as_submodel_mut().symbols_mut().extend(letting_vars);
5253
}
5354
_ => {
5455
let kind = statement.kind();

conjure_oxide/src/utils/testing.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::hash::Hash;
77
use std::io::Write;
88
use std::sync::{Arc, RwLock};
99

10-
use conjure_core::ast::model::SerdeModel;
10+
use conjure_core::ast::SerdeModel;
1111
use conjure_core::context::Context;
1212
use serde_json::{json, Error as JsonError, Value as JsonValue};
1313

conjure_oxide/tests/model_tests.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
// Tests for various functionalities of the Model
22

3-
use std::{cell::RefCell, rc::Rc};
3+
use std::rc::Rc;
44

55
use conjure_core::ast::Model;
66
use conjure_oxide::ast::*;
7-
use declaration::Declaration;
87

98
#[test]
109
fn modify_domain() {
10+
let mut m = Model::new(Default::default());
11+
12+
let mut symbols = m.as_submodel_mut().symbols_mut();
13+
1114
let a = Name::UserName(String::from("a"));
1215

1316
let d1 = Domain::IntDomain(vec![Range::Bounded(1, 3)]);
1417
let d2 = Domain::IntDomain(vec![Range::Bounded(1, 2)]);
1518

16-
let mut symbols = SymbolTable::new();
1719
symbols
1820
.insert(Rc::new(Declaration::new_var(a.clone(), d1.clone())))
1921
.unwrap();
2022

21-
let m = Model::new(Rc::new(RefCell::new(symbols)), vec![], Default::default());
22-
23-
assert_eq!(&m.symbols().domain(&a).unwrap(), &d1);
23+
assert_eq!(symbols.domain(&a).unwrap(), d1);
2424

25-
let mut decl_a = m.symbols().lookup(&a).unwrap();
25+
let mut decl_a = symbols.lookup(&a).unwrap();
2626

2727
Rc::make_mut(&mut decl_a).as_var_mut().unwrap().domain = d2.clone();
2828

29-
m.symbols_mut().update_insert(decl_a);
29+
symbols.update_insert(decl_a);
3030

31-
assert_eq!(&m.symbols().domain(&a).unwrap(), &d2);
31+
assert_eq!(symbols.domain(&a).unwrap(), d2);
3232
}

conjure_oxide/tests/rewrite_tests.rs

+13-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::cell::RefCell;
21
use std::collections::VecDeque;
32
use std::process::exit;
43
use std::rc::Rc;
@@ -13,7 +12,6 @@ use conjure_oxide::{
1312
solver::{adaptors, Solver},
1413
Metadata, Model, Rule,
1514
};
16-
use declaration::Declaration;
1715
use uniplate::{Biplate, Uniplate};
1816

1917
fn var_name_from_atom(a: &Atom) -> Name {
@@ -325,26 +323,27 @@ fn reduce_solve_xyz() {
325323
)
326324
);
327325

328-
let model = Model::new(
329-
Rc::new(RefCell::new(SymbolTable::new())),
330-
vec![expr1, expr2],
331-
Default::default(),
332-
);
326+
let mut model = Model::new(Default::default());
327+
*model.as_submodel_mut().constraints_mut() = vec![expr1, expr2];
328+
333329
model
330+
.as_submodel_mut()
334331
.symbols_mut()
335332
.insert(Rc::new(Declaration::new_var(
336333
Name::UserName(String::from("a")),
337334
Domain::IntDomain(vec![Range::Bounded(1, 3)]),
338335
)))
339336
.unwrap();
340337
model
338+
.as_submodel_mut()
341339
.symbols_mut()
342340
.insert(Rc::new(Declaration::new_var(
343341
Name::UserName(String::from("b")),
344342
Domain::IntDomain(vec![Range::Bounded(1, 3)]),
345343
)))
346344
.unwrap();
347345
model
346+
.as_submodel_mut()
348347
.symbols_mut()
349348
.insert(Rc::new(Declaration::new_var(
350349
Name::UserName(String::from("c")),
@@ -667,45 +666,34 @@ fn rewrite_solve_xyz() {
667666
};
668667

669668
// Apply rewrite function to the nested expression
670-
let rewritten_expr = rewrite_naive(
671-
&Model::new(
672-
Rc::new(RefCell::new(SymbolTable::new())),
673-
vec![nested_expr],
674-
Default::default(),
675-
),
676-
&rule_sets,
677-
true,
678-
)
679-
.unwrap()
680-
.get_constraints_vec();
669+
let mut model = Model::new(Default::default());
670+
*model.as_submodel_mut().constraints_mut() = vec![nested_expr];
671+
model = rewrite_naive(&model, &rule_sets, true).unwrap();
672+
let rewritten_expr = model.as_submodel().constraints();
681673

682674
// Check if the expression is in its simplest form
683675

684676
assert!(rewritten_expr.iter().all(is_simple));
685677

686-
// Create model with variables and constraints
687-
let model = Model::new(
688-
Rc::new(RefCell::new(SymbolTable::new())),
689-
rewritten_expr,
690-
Default::default(),
691-
);
692-
693678
// Insert variables and domains
694679
model
680+
.as_submodel_mut()
695681
.symbols_mut()
696682
.insert(Rc::new(Declaration::new_var(
697683
var_name_from_atom(&variable_a.clone()),
698684
domain.clone(),
699685
)))
700686
.unwrap();
701687
model
688+
.as_submodel_mut()
702689
.symbols_mut()
703690
.insert(Rc::new(Declaration::new_var(
704691
var_name_from_atom(&variable_b.clone()),
705692
domain.clone(),
706693
)))
707694
.unwrap();
708695
model
696+
.as_submodel_mut()
709697
.symbols_mut()
710698
.insert(Rc::new(Declaration::new_var(
711699
var_name_from_atom(&variable_c.clone()),

crates/conjure_core/src/ast/expressions.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@ use crate::ast::Name;
1616
use crate::ast::ReturnType;
1717
use crate::metadata::Metadata;
1818

19-
use super::{Domain, Range};
19+
use super::{Domain, Range, SubModel};
2020

2121
/// Represents different types of expressions used to define rules and constraints in the model.
2222
///
2323
/// The `Expression` enum includes operations, constants, and variable references
2424
/// used to build rules and conditions for the model.
2525
#[document_compatibility]
2626
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Uniplate)]
27-
#[uniplate(walk_into=[Atom])]
27+
#[uniplate(walk_into=[Atom,Submodel])]
2828
#[biplate(to=Literal)]
2929
#[biplate(to=Metadata)]
3030
#[biplate(to=Atom)]
3131
#[biplate(to=Name)]
3232
#[biplate(to=Vec<Expression>)]
33+
#[biplate(to=SubModel)]
3334
pub enum Expression {
3435
/// The top of the model
3536
Root(Metadata, Vec<Expression>),

crates/conjure_core/src/ast/mod.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
pub mod pretty;
2-
pub mod types;
2+
pub mod serde;
33

44
mod atom;
5-
pub mod declaration;
5+
mod declaration;
66
mod domains;
77
mod expressions;
88
mod literals;
9-
pub mod model;
9+
mod model;
1010
mod name;
11-
pub mod serde;
11+
mod submodel;
1212
mod symbol_table;
13+
mod types;
1314
mod variables;
1415

1516
pub use atom::Atom;
17+
pub use declaration::*;
1618
pub use domains::Domain;
1719
pub use domains::Range;
1820
pub use expressions::Expression;
1921
pub use literals::Literal;
20-
pub use model::Model;
22+
pub use model::*;
2123
pub use name::Name;
24+
pub use submodel::SubModel;
2225
pub use symbol_table::SymbolTable;
23-
pub use types::ReturnType;
26+
pub use types::*;
2427
pub use variables::DecisionVariable;

0 commit comments

Comments
 (0)