Skip to main content

choco_solver/
model.rs

1use crate::utils::{Handle, HandleT};
2use crate::variables::{IntVar, NewIntVarT};
3use crate::{BoolVar, CHOCO_BACKEND, CHOCO_LIB};
4
5/// A constraint programming model that owns variables and constraints.
6///
7/// Models are the root of object lifetimes and create variables, constraints,
8/// and solvers associated with the same backend instance.
9pub struct Model {
10    handle: Handle,
11}
12
13impl Model {
14    /// Creates a new Model instance.
15    /// # Panics
16    ///
17    ///  if the string name contains a null byte.
18    #[must_use]
19    pub fn new(name: Option<&str>) -> Self {
20        // Safety:
21        // create model function are guarantee to be called after chocosolver_init
22        // by the ChocoBackend lazy initialization.
23        let model_handle = unsafe {
24            match name {
25                Some(n) => {
26                    let c_name =
27                        std::ffi::CString::new(n).expect("Failed to convert name to CString");
28                    let model_handle = CHOCO_BACKEND.with(|backend| {
29                        CHOCO_LIB.Java_org_chocosolver_capi_ModelApi_createModel_s(
30                            backend.thread,
31                            c_name.as_ptr().cast_mut(),
32                        )
33                    });
34
35                    assert!(
36                        !model_handle.is_null(),
37                        "Failed to create model with name: received null handle"
38                    );
39
40                    model_handle
41                }
42                None => {
43                    let model_handle = CHOCO_BACKEND.with(|backend| {
44                        CHOCO_LIB.Java_org_chocosolver_capi_ModelApi_createModel(backend.thread)
45                    });
46                    assert!(
47                        !model_handle.is_null(),
48                        "Failed to create model: received null handle"
49                    );
50                    model_handle
51                }
52            }
53        };
54        Model {
55            handle: Handle::new(model_handle),
56        }
57    }
58
59    /// Returns the name of the model, if set.
60    #[must_use]
61    pub fn name(&self) -> Option<String> {
62        // Safety:
63        // Safe because this can be called only after the model is created and therefore the backend is initialized.
64        unsafe {
65            let name_ptr = CHOCO_BACKEND.with(|backend| {
66                CHOCO_LIB.Java_org_chocosolver_capi_ModelApi_getName(
67                    backend.thread,
68                    self.handle.get_raw_handle(),
69                )
70            });
71            if name_ptr.is_null() {
72                None
73            } else {
74                let c_str = std::ffi::CStr::from_ptr(name_ptr);
75                Some(c_str.to_string_lossy().into_owned())
76            }
77        }
78    }
79
80    /// New integer variable associated with this model.
81    /// x can be:
82    /// - a single integer value (constant variable)
83    /// - a slice of possible integer values
84    #[must_use]
85    #[allow(private_bounds)]
86    pub fn int_var<'b, X>(&'b self, x: X, name: Option<&str>) -> IntVar<'b>
87    where
88        for<'a> (X, Option<&'a str>): NewIntVarT,
89    {
90        (x, name).create_int_var(self)
91    }
92
93    /// New bounded integer variable associated with this model.
94    /// x and y shall be integer values
95    /// bounded: Force bounded (True) or enumerated domain (False). If None, Choco will automatically choose the best option.
96    #[must_use]
97    #[allow(private_bounds)]
98    pub fn int_var_bounded<'b, X, Y>(
99        &'b self,
100        x: X,
101        y: Y,
102        name: Option<&str>,
103        bounded: Option<bool>,
104    ) -> IntVar<'b>
105    where
106        for<'a> (X, Y, Option<&'a str>, bool): NewIntVarT,
107        for<'a> (X, Y, Option<&'a str>): NewIntVarT,
108    {
109        match bounded {
110            Some(v) => (x, y, name, v).create_int_var(self),
111            None => (x, y, name).create_int_var(self),
112        }
113    }
114    #[must_use]
115    pub fn bool_var<'model>(
116        &'model self,
117        value: Option<bool>,
118        name: Option<&str>,
119    ) -> BoolVar<'model> {
120        BoolVar::new(self, value, name)
121    }
122
123    #[must_use]
124    pub fn solver(&self) -> crate::solver::Solver<'_> {
125        crate::solver::Solver::new(self)
126    }
127}
128
129impl HandleT for Model {
130    fn get_raw_handle(&self) -> *mut std::os::raw::c_void {
131        self.handle.get_raw_handle()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use crate::constraint::{ArithmeticOperator, EqualityOperator};
138
139    use super::Model;
140
141    #[test]
142    fn test_model_basic() {
143        let model = Model::new(Some("TestModel"));
144        assert_eq!(model.name().as_deref(), Some("TestModel"));
145
146        let unnamed_model = Model::new(None);
147
148        let a = unnamed_model.int_var_bounded(-100, 100, Some("var1"), None);
149        let b = unnamed_model.int_var_bounded(-100, 100, Some("var2"), None);
150        let c = unnamed_model.int_var_bounded(0, 50, Some("var3"), None);
151        assert!(a.arithm(EqualityOperator::Eq, 32i32).post().is_ok());
152        // unnamed_model
153        //     .arithm(&a, EqualityOperator::Eq, &b, ArithmeticOperator::Sum, 10)
154        //     .post();
155
156        // Constraint::arithm(&a, EqualityOperator::Eq, &b).post();
157
158        assert!(
159            a.arithm2(EqualityOperator::Eq, &b, ArithmeticOperator::Sum, &c)
160                .post()
161                .is_ok()
162        );
163        let solver = unnamed_model.solver();
164        let solution = solver
165            .find_solution(&Default::default())
166            .expect("Expected to find a solution");
167        println!(
168            "Solution: var1 = {}, var2 = {}, var3 = {}",
169            solution.get_int_var(&a).unwrap(),
170            solution.get_int_var(&b).unwrap(),
171            solution.get_int_var(&c).unwrap()
172        );
173    }
174}