conjure_core/model.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
use std::fmt::{Debug, Display};
use std::sync::{Arc, RwLock};
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use crate::ast::{DecisionVariable, Domain, Expression, Name, SymbolTable};
use crate::context::Context;
use crate::ast::pretty::{pretty_expressions_as_top_level, pretty_variable_declaration};
/// Represents a computational model containing variables, constraints, and a shared context.
///
/// The `Model` struct holds a set of variables and constraints for manipulating and evaluating symbolic expressions.
///
/// # Fields
/// - `constraints`:
/// - Type: `Vec<Expression>`
/// - Represents the logical constraints applied to the model's variables.
/// - Can be a single constraint or a combination of various expressions, such as logical operations (e.g., `AND`, `OR`),
/// arithmetic operations (e.g., `SafeDiv`, `UnsafeDiv`), or specialized constraints like `SumEq`.
///
/// - `context`:
/// - Type: `Arc<RwLock<Context<'static>>>`
/// - A shared object that stores global settings and state for the model.
/// - Can be safely read or changed by multiple parts of the program at the same time, making it good for multi-threaded use.
///
/// # Usage
/// This struct is typically used to:
/// - Define a set of variables and constraints for rule-based evaluation.
/// - Have transformations, optimizations, and simplifications applied to it using a set of rules.
#[derive(Derivative, Clone, Debug, Serialize, Deserialize)]
#[derivative(PartialEq, Eq)]
pub struct Model {
pub constraints: Vec<Expression>,
symbols: SymbolTable,
#[serde(skip)]
#[derivative(PartialEq = "ignore")]
pub context: Arc<RwLock<Context<'static>>>,
}
impl Model {
/// Creates a new model.
pub fn new(
symbols: SymbolTable,
constraints: Vec<Expression>,
context: Arc<RwLock<Context<'static>>>,
) -> Model {
Model {
symbols,
constraints,
context,
}
}
pub fn new_empty(context: Arc<RwLock<Context<'static>>>) -> Model {
Model::new(Default::default(), Vec::new(), context)
}
/// The global symbol table for this model.
pub fn symbols(&self) -> &SymbolTable {
&self.symbols
}
/// The global symbol table for this model, as a mutable reference.
pub fn symbols_mut(&mut self) -> &mut SymbolTable {
&mut self.symbols
}
// Function to update a DecisionVariable based on its Name
pub fn update_domain(&mut self, name: &Name, new_domain: Domain) {
if let Some(decision_var) = self.symbols_mut().get_var_mut(name) {
decision_var.domain = new_domain;
}
}
/// Gets the domain of `name` if it exists and has one.
pub fn get_domain(&self, name: &Name) -> Option<&Domain> {
self.symbols().domain_of(name)
}
/// Adds a decision variable to the model.
///
/// Returns `None` if there is a decision variable or other object with that name in the symbol
/// table.
pub fn add_variable(&mut self, name: Name, decision_var: DecisionVariable) -> Option<()> {
self.symbols_mut().add_var(name, decision_var)
}
pub fn get_constraints_vec(&self) -> Vec<Expression> {
self.constraints.clone()
}
pub fn set_constraints(&mut self, constraints: Vec<Expression>) {
if constraints.is_empty() {
self.constraints = Vec::new();
} else {
self.constraints = constraints;
}
}
pub fn set_context(&mut self, context: Arc<RwLock<Context<'static>>>) {
self.context = context;
}
pub fn add_constraint(&mut self, expression: Expression) {
// ToDo (gs248) - there is no checking whatsoever
// We need to properly validate the expression but this is just for testing
let mut constraints = self.get_constraints_vec();
constraints.push(expression);
self.set_constraints(constraints);
}
pub fn add_constraints(&mut self, expressions: Vec<Expression>) {
let mut constraints = self.get_constraints_vec();
constraints.extend(expressions);
self.set_constraints(constraints);
}
/// Returns an arbitrary variable name that is not in the model.
pub fn gensym(&self) -> Name {
self.symbols().gensym()
}
/// Extends the models symbol table with the given symbol table, updating the gensym counter if
/// necessary.
pub fn extend_sym_table(&mut self, other: SymbolTable) {
self.symbols_mut().extend(other);
}
}
impl Display for Model {
#[allow(clippy::unwrap_used)] // [rustdocs]: should only fail iff the formatter fails
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for name in self.symbols.names() {
writeln!(
f,
"find {}",
pretty_variable_declaration(&self.symbols, name).unwrap()
)?;
}
writeln!(f, "\nsuch that\n")?;
writeln!(f, "{}", pretty_expressions_as_top_level(&self.constraints))?;
Ok(())
}
}