conjure_cp_core/ast/
model.rs

1#![allow(clippy::arc_with_non_send_sync)] // uniplate needs this
2use std::cell::RefCell;
3use std::collections::{HashMap, VecDeque};
4use std::fmt::{Debug, Display};
5use std::rc::Rc;
6use std::sync::{Arc, RwLock};
7
8use derivative::Derivative;
9use serde::{Deserialize, Serialize};
10use uniplate::{Biplate, Tree, Uniplate};
11
12use crate::ast::{Expression, Typeable};
13use crate::context::Context;
14
15use super::serde::{HasId, ObjId};
16use super::{DeclarationPtr, Name, SubModel};
17use super::{ReturnType, SymbolTable};
18
19/// An Essence model.
20///
21/// - This type wraps a [`Submodel`] containing the top-level lexical scope. To manipulate the
22///   model's constraints or symbols, first convert it to a [`Submodel`] using
23///   [`as_submodel`](Model::as_submodel) / [`as_submodel_mut`](Model::as_submodel_mut).
24///
25/// - To de/serialise a model using `serde`, see [`SerdeModel`].
26#[derive(Derivative, Clone, Debug)]
27#[derivative(PartialEq, Eq)]
28pub struct Model {
29    submodel: SubModel,
30    pub search_order: Option<Vec<Name>>,
31    pub dominance: Option<Expression>,
32    #[derivative(PartialEq = "ignore")]
33    pub context: Arc<RwLock<Context<'static>>>,
34}
35
36impl Model {
37    pub fn from_submodel(submodel: SubModel) -> Model {
38        Model {
39            submodel,
40            ..Default::default()
41        }
42    }
43
44    /// Creates a new model from the given context.
45    pub fn new(context: Arc<RwLock<Context<'static>>>) -> Model {
46        Model {
47            submodel: SubModel::new_top_level(),
48            dominance: None,
49            context,
50            search_order: None,
51        }
52    }
53
54    /// Returns this model as a [`Submodel`].
55    pub fn as_submodel(&self) -> &SubModel {
56        &self.submodel
57    }
58
59    /// Returns this model as a mutable [`Submodel`].
60    pub fn as_submodel_mut(&mut self) -> &mut SubModel {
61        &mut self.submodel
62    }
63
64    /// Replaces the model contents with `new_submodel`, returning the old contents.
65    pub fn replace_submodel(&mut self, new_submodel: SubModel) -> SubModel {
66        std::mem::replace(self.as_submodel_mut(), new_submodel)
67    }
68}
69
70impl Default for Model {
71    fn default() -> Self {
72        Model {
73            submodel: SubModel::new_top_level(),
74            dominance: None,
75            context: Arc::new(RwLock::new(Context::default())),
76            search_order: None,
77        }
78    }
79}
80
81impl Typeable for Model {
82    fn return_type(&self) -> ReturnType {
83        ReturnType::Bool
84    }
85}
86
87// At time of writing (03/02/2025), the Uniplate derive macro doesn't like the lifetimes inside
88// context, and we do not yet have a way of ignoring this field.
89impl Uniplate for Model {
90    fn uniplate(&self) -> (Tree<Self>, Box<dyn Fn(Tree<Self>) -> Self>) {
91        // Model contains no sub-models.
92        let self2 = self.clone();
93        (Tree::Zero, Box::new(move |_| self2.clone()))
94    }
95}
96
97impl Biplate<Expression> for Model {
98    fn biplate(&self) -> (Tree<Expression>, Box<dyn Fn(Tree<Expression>) -> Self>) {
99        // walk into submodel
100        let submodel = self.as_submodel();
101        let (expr_tree, expr_ctx) = <SubModel as Biplate<Expression>>::biplate(submodel);
102
103        // walk into dominance relation if it exists
104        let dom_tree = match &self.dominance {
105            Some(expr) => Tree::One(expr.clone()),
106            None => Tree::Zero,
107        };
108        let tree = Tree::<Expression>::Many(VecDeque::from([expr_tree, dom_tree]));
109
110        let self2 = self.clone();
111        let ctx = Box::new(move |x| match x {
112            Tree::Many(xs) => {
113                if xs.len() != 2 {
114                    panic!("Expected a tree with two children");
115                }
116                let submodel_tree = xs[0].clone();
117                let dom_tree = xs[1].clone();
118
119                // reconstruct the submodel
120                let submodel = expr_ctx(submodel_tree);
121                // reconstruct the dominance relation
122                let dominance = match dom_tree {
123                    Tree::One(expr) => Some(expr),
124                    Tree::Zero => None,
125                    _ => panic!("Expected a tree with two children"),
126                };
127
128                let mut self3 = self2.clone();
129                self3.replace_submodel(submodel);
130                self3.dominance = dominance;
131                self3
132            }
133            _ => {
134                panic!("Expected a tree with two children");
135            }
136        });
137
138        (tree, ctx)
139    }
140}
141
142impl Biplate<SubModel> for Model {
143    fn biplate(&self) -> (Tree<SubModel>, Box<dyn Fn(Tree<SubModel>) -> Self>) {
144        let submodel = self.as_submodel().clone();
145
146        let self2 = self.clone();
147        let ctx = Box::new(move |x| {
148            let Tree::One(submodel) = x else {
149                panic!();
150            };
151
152            let mut self3 = self2.clone();
153            self3.replace_submodel(submodel);
154            self3
155        });
156
157        (Tree::One(submodel), ctx)
158    }
159}
160
161impl Display for Model {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        std::fmt::Display::fmt(self.as_submodel(), f)
164    }
165}
166
167/// A model that is de/serializable using `serde`.
168///
169/// To turn this into a rewritable model, it needs to be initialised using [`initialise`](SerdeModel::initialise).
170///
171/// To deserialise a [`Model`], use `.into()` to convert it into a `SerdeModel` first.
172#[derive(Clone, Debug, Serialize, Deserialize)]
173pub struct SerdeModel {
174    #[serde(flatten)]
175    submodel: SubModel,
176    search_order: Option<Vec<Name>>, // TODO: make this a [expressions]
177    dominance: Option<Expression>,
178}
179
180impl SerdeModel {
181    /// Initialises the model for rewriting.
182    ///
183    /// This swizzles the pointers to symbol tables and declarations using the stored ids.
184    pub fn initialise(mut self, context: Arc<RwLock<Context<'static>>>) -> Option<Model> {
185        // The definitive versions of each symbol table are stored in the submodels. Parent
186        // pointers store dummy values with the correct ids, but nothing else. We need to replace
187        // these dummy values with pointers to the actual parent symbol tables, using the ids to
188        // know which tables should be equal.
189        //
190        // See super::serde::RcRefCellToInner, super::serde::RcRefCellToId.
191
192        // Store the definitive versions of all symbol tables by id.
193        let mut tables: HashMap<ObjId, Rc<RefCell<SymbolTable>>> = HashMap::new();
194
195        // Find the definitive versions by traversing the sub-models.
196        for submodel in self.submodel.universe() {
197            let id = submodel.symbols().id();
198
199            // ids should be unique!
200            assert_eq!(
201                tables.insert(id, submodel.symbols_ptr_unchecked().clone()),
202                None
203            );
204        }
205
206        // Restore parent pointers using `tables`.
207        for table in tables.clone().into_values() {
208            let mut table_mut = table.borrow_mut();
209            let parent_mut = table_mut.parent_mut_unchecked();
210
211            #[allow(clippy::unwrap_used)]
212            if let Some(parent) = parent_mut {
213                let parent_id = parent.borrow().id();
214
215                *parent = tables.get(&parent_id).unwrap().clone();
216            }
217        }
218
219        // The definitive versions of declarations are stored in the symbol table. References store
220        // dummy values with the correct ids, but nothing else.
221
222        // Store the definitive version of all declarations by id.
223        let mut all_declarations: HashMap<ObjId, DeclarationPtr> = HashMap::new();
224        for table in tables.values() {
225            for (_, decl) in table.as_ref().borrow().clone().into_iter_local() {
226                let id = decl.id();
227                all_declarations.insert(id, decl);
228            }
229        }
230
231        // Swizzle declaration pointers in expressions (references, auxdecls) using their ids and `all_declarations`.
232        *self.submodel.constraints_mut() = self.submodel.constraints().transform_bi(&move |decl: DeclarationPtr| {
233                let id = decl.id();
234                        all_declarations
235                            .get(&id)
236                            .unwrap_or_else(|| panic!("A declaration used in the expression tree should exist in the symbol table. The missing declaration has id {id}."))
237                            .clone()
238        });
239
240        Some(Model {
241            submodel: self.submodel,
242            dominance: self.dominance,
243            context,
244            search_order: self.search_order,
245        })
246    }
247}
248
249impl From<Model> for SerdeModel {
250    fn from(val: Model) -> Self {
251        SerdeModel {
252            submodel: val.submodel,
253            dominance: val.dominance,
254            search_order: val.search_order,
255        }
256    }
257}
258
259impl Display for SerdeModel {
260    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261        std::fmt::Display::fmt(&self.submodel, f)
262    }
263}
264
265impl SerdeModel {
266    /// Collects all ObjId values from the model using uniplate traversal.
267    ///
268    /// Traverses the model structure using `universe_bi` to collect IDs from:
269    /// - All SubModels (to get SymbolTable IDs via `HasId`)
270    /// - All DeclarationPtrs (to get declaration IDs via `HasId`)
271    ///
272    /// Returns a mapping from original ID to stable sequential ID (0, 1, 2, ...).
273    /// IDs are assigned in the order they are encountered during traversal, ensuring
274    /// stability across identical model structures.
275    pub fn collect_stable_id_mapping(&self) -> HashMap<ObjId, ObjId> {
276        let mut id_list: Vec<ObjId> = Vec::new();
277
278        // Collect SymbolTable IDs by traversing all SubModels
279        for submodel in self.submodel.universe() {
280            let symbol_table_id = submodel.symbols().id();
281            if !id_list.contains(&symbol_table_id) {
282                id_list.push(symbol_table_id);
283            }
284        }
285
286        // Collect DeclarationPtr IDs by traversing the constraints expression tree
287        for decl_ptr in Biplate::<DeclarationPtr>::universe_bi(self.submodel.constraints()) {
288            let decl_id = decl_ptr.id();
289            if !id_list.contains(&decl_id) {
290                id_list.push(decl_id);
291            }
292        }
293
294        // Create stable mapping: original_id -> stable_id
295        let mut id_map = HashMap::new();
296        for (stable_id, &original_id) in id_list.iter().enumerate() {
297            id_map.insert(original_id, stable_id as ObjId);
298        }
299
300        id_map
301    }
302}