choco_solver/solver/
criterion.rs1use crate::{CHOCO_BACKEND, CHOCO_LIB, Model, utils::HandleT};
2
3#[derive(Debug, Clone, Default)]
5pub struct Criterion {
6 node_limit: Option<i64>,
7 fail_limit: Option<i64>,
8 restart_limit: Option<i64>,
9 backtrack_limit: Option<i64>,
10}
11
12impl Criterion {
13 #[must_use]
15 pub fn new() -> Self {
16 Criterion::default()
17 }
18
19 #[must_use]
21 pub fn with_node_limit(mut self, limit: i64) -> Self {
22 self.node_limit = Some(limit);
23 self
24 }
25
26 #[must_use]
28 pub fn with_fail_limit(mut self, limit: i64) -> Self {
29 self.fail_limit = Some(limit);
30 self
31 }
32
33 #[must_use]
35 pub fn with_restart_limit(mut self, limit: i64) -> Self {
36 self.restart_limit = Some(limit);
37 self
38 }
39
40 #[must_use]
42 pub fn with_backtrack_limit(mut self, limit: i64) -> Self {
43 self.backtrack_limit = Some(limit);
44 self
45 }
46}
47
48pub(super) fn make_criterion_var_array(
49 criterions: &Criterion,
50 model: &Model,
51) -> *mut std::os::raw::c_void {
52 CHOCO_BACKEND.with(|backend| {
53 unsafe {
56 let Criterion {
57 node_limit,
58 fail_limit,
59 restart_limit,
60 backtrack_limit,
61 } = *criterions;
62
63 let mut raw_criterions = vec![];
64 if let Some(node_limit) = node_limit {
65 assert!(!node_limit.is_negative(), "Node limit must be non-negative");
66
67 raw_criterions.push(
68 CHOCO_LIB.Java_org_chocosolver_capi_CriterionApi_node_counter(
69 backend.thread,
70 model.get_raw_handle(),
71 node_limit,
72 ),
73 );
74 };
75 if let Some(fail_limit) = fail_limit {
76 assert!(!fail_limit.is_negative(), "Fail limit must be non-negative");
79
80 raw_criterions.push(
81 CHOCO_LIB.Java_org_chocosolver_capi_CriterionApi_fail_counter(
82 backend.thread,
83 model.get_raw_handle(),
84 fail_limit,
85 ),
86 );
87 };
88 if let Some(restart_limit) = restart_limit {
89 assert!(
92 !restart_limit.is_negative(),
93 "Restart limit must be non-negative"
94 );
95
96 raw_criterions.push(
97 CHOCO_LIB.Java_org_chocosolver_capi_CriterionApi_restart_counter(
98 backend.thread,
99 model.get_raw_handle(),
100 restart_limit,
101 ),
102 );
103 };
104 if let Some(backtrack_limit) = backtrack_limit {
105 assert!(
108 !backtrack_limit.is_negative(),
109 "Backtrack limit must be non-negative"
110 );
111
112 raw_criterions.push(
113 CHOCO_LIB.Java_org_chocosolver_capi_CriterionApi_backtrack_counter(
114 backend.thread,
115 model.get_raw_handle(),
116 backtrack_limit,
117 ),
118 );
119 };
120 let len_i32: i32 = raw_criterions
121 .len()
122 .try_into()
123 .expect("Criterion array length exceeds i32");
124 let criterion_array = CHOCO_LIB
125 .Java_org_chocosolver_capi_ArrayApi_criterion_create(backend.thread, len_i32);
126 for (i, criterion) in raw_criterions.iter().enumerate() {
127 #[allow(
128 clippy::cast_possible_truncation,
129 reason = "Length checked to fit in i32"
130 )]
131 #[allow(clippy::cast_possible_wrap, reason = "Length checked to fit in i32")]
132 CHOCO_LIB.Java_org_chocosolver_capi_ArrayApi_criterion_set(
133 backend.thread,
134 criterion_array,
135 *criterion,
136 i as i32,
137 );
138 }
139 criterion_array
140 }
141 })
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_criterion_all_methods() {
150 let criterion = Criterion::new()
151 .with_node_limit(1000)
152 .with_fail_limit(500)
153 .with_restart_limit(100)
154 .with_backtrack_limit(200);
155
156 assert_eq!(criterion.node_limit, Some(1000));
157 assert_eq!(criterion.fail_limit, Some(500));
158 assert_eq!(criterion.restart_limit, Some(100));
159 assert_eq!(criterion.backtrack_limit, Some(200));
160 }
161
162 #[test]
163 fn test_criterion_restrictive_limits() {
164 use crate::{Model, constraint::EqualityOperator};
165
166 let model = Model::new(Some("RestrictiveTest"));
168 let x = model.int_var_bounded(0, 10, Some("x"), None);
169 let y = model.int_var_bounded(0, 10, Some("y"), None);
170 let z = model.int_var_bounded(0, 10, Some("z"), None);
171
172 assert!(x.arithm(EqualityOperator::Lt, 5i32).post().is_ok());
174 assert!(y.arithm(EqualityOperator::Gt, 3i32).post().is_ok());
175 assert!(z.arithm(EqualityOperator::Neq, 7i32).post().is_ok());
176
177 let solver = model.solver();
179 let criterion = Criterion::new()
180 .with_node_limit(1)
181 .with_fail_limit(0)
182 .with_restart_limit(0)
183 .with_backtrack_limit(0);
184
185 let solution = solver.find_solution(&criterion);
187
188 assert!(solution.is_none());
190 }
191}