pumpkin_core/branching/branchers/
autonomous_search.rs1use super::independent_variable_value_brancher::IndependentVariableValueBrancher;
2use crate::DefaultBrancher;
3use crate::basic_types::DeletablePredicateIdGenerator;
4use crate::basic_types::PredicateId;
5use crate::basic_types::SolutionReference;
6use crate::branching::Brancher;
7use crate::branching::BrancherEvent;
8use crate::branching::SelectionContext;
9use crate::branching::value_selection::RandomSplitter;
10use crate::branching::variable_selection::RandomSelector;
11use crate::containers::KeyValueHeap;
12use crate::containers::StorageKey;
13use crate::create_statistics_struct;
14use crate::engine::Assignments;
15use crate::engine::predicates::predicate::Predicate;
16use crate::propagation::ReadDomains;
17use crate::results::Solution;
18use crate::statistics::Statistic;
19use crate::statistics::StatisticLogger;
20use crate::statistics::moving_averages::CumulativeMovingAverage;
21use crate::statistics::moving_averages::MovingAverage;
22use crate::variables::DomainId;
23#[derive(Debug)]
65pub struct AutonomousSearch<BackupBrancher> {
66 predicate_id_info: DeletablePredicateIdGenerator,
68 heap: KeyValueHeap<PredicateId, f64>,
70 dormant_predicates: Vec<Predicate>,
75 increment: f64,
78 max_threshold: f64,
81 decay_factor: f64,
86 best_known_solution: Option<Solution>,
88 backup_brancher: BackupBrancher,
91 statistics: AutonomousSearchStatistics,
93 should_synchronise: bool,
99}
100
101create_statistics_struct!(AutonomousSearchStatistics {
102 num_backup_called: usize,
103 num_predicates_removed: usize,
104 num_calls: usize,
105 num_predicates_added: usize,
106 average_size_of_heap: CumulativeMovingAverage<usize>,
107 num_assigned_predicates_encountered: usize,
108});
109
110const DEFAULT_VSIDS_INCREMENT: f64 = 1.0;
111const DEFAULT_VSIDS_MAX_THRESHOLD: f64 = 1e100;
112const DEFAULT_VSIDS_DECAY_FACTOR: f64 = 0.95;
113const DEFAULT_VSIDS_VALUE: f64 = 0.0;
114
115impl DefaultBrancher {
116 pub fn default_over_all_variables(assignments: &Assignments) -> DefaultBrancher {
123 AutonomousSearch {
124 predicate_id_info: DeletablePredicateIdGenerator::default(),
125 heap: KeyValueHeap::default(),
126 dormant_predicates: vec![],
127 increment: DEFAULT_VSIDS_INCREMENT,
128 max_threshold: DEFAULT_VSIDS_MAX_THRESHOLD,
129 decay_factor: DEFAULT_VSIDS_DECAY_FACTOR,
130 best_known_solution: None,
131 should_synchronise: false,
132 backup_brancher: IndependentVariableValueBrancher::new(
133 RandomSelector::new(assignments.get_domains()),
134 RandomSplitter,
135 ),
136 statistics: Default::default(),
137 }
138 }
139
140 pub fn add_domain(&mut self, domain: DomainId) {
141 self.backup_brancher.variable_selector.add_domain(domain);
142 }
143}
144
145impl<BackupSelector> AutonomousSearch<BackupSelector> {
146 pub fn new(backup_brancher: BackupSelector) -> Self {
152 AutonomousSearch {
153 predicate_id_info: DeletablePredicateIdGenerator::default(),
154 heap: KeyValueHeap::default(),
155 dormant_predicates: vec![],
156 increment: DEFAULT_VSIDS_INCREMENT,
157 max_threshold: DEFAULT_VSIDS_MAX_THRESHOLD,
158 decay_factor: DEFAULT_VSIDS_DECAY_FACTOR,
159 best_known_solution: None,
160 should_synchronise: false,
161 backup_brancher,
162 statistics: Default::default(),
163 }
164 }
165
166 fn resize_heap(&mut self, id: PredicateId) {
169 while self.heap.len() <= id.index() {
170 self.heap.grow(id, DEFAULT_VSIDS_VALUE);
171 }
172 }
173
174 fn bump_activity(&mut self, predicate: Predicate) {
177 self.statistics.num_predicates_added +=
178 (!self.predicate_id_info.has_id_for_predicate(predicate)) as usize;
179 let id = self.predicate_id_info.get_id(predicate);
180 self.resize_heap(id);
181 self.heap.restore_key(id);
182
183 let activity = self.heap.get_value(id);
186 if activity + self.increment >= self.max_threshold {
187 self.heap.divide_values(self.max_threshold);
189
190 self.increment /= self.max_threshold;
192 }
193 self.heap.increment(id, self.increment);
195 }
196
197 fn decay_activities(&mut self) {
203 self.increment *= 1.0 / self.decay_factor;
204 }
205
206 fn next_candidate_predicate(&mut self, context: &mut SelectionContext) -> Option<Predicate> {
207 loop {
208 if let Some((candidate, _)) = self.heap.peek_max() {
211 let predicate = self
212 .predicate_id_info
213 .get_predicate(*candidate)
214 .expect("Expected predicate id to exist");
215 if context.is_predicate_assigned(predicate) {
216 self.statistics.num_assigned_predicates_encountered += 1;
217 let _ = self.heap.pop_max();
218
219 let predicate_id = self.predicate_id_info.get_id(predicate);
221 self.heap.delete_key(predicate_id);
222 self.predicate_id_info.delete_id(predicate_id);
223 self.dormant_predicates.push(predicate);
224 } else {
225 return Some(predicate);
226 }
227 } else {
228 return None;
229 }
230 }
231 }
232
233 fn determine_polarity(&self, predicate: Predicate) -> Predicate {
240 if let Some(solution) = &self.best_known_solution {
241 if !solution.contains_domain_id(predicate.get_domain()) {
243 return predicate;
245 }
246 if solution.evaluate_predicate(predicate) == Some(true) {
248 predicate
249 } else {
250 !predicate
251 }
252 } else {
253 predicate
256 }
257 }
258
259 fn synchronise_internal(&mut self) {
260 self.dormant_predicates.drain(..).for_each(|predicate| {
264 let id = self.predicate_id_info.get_id(predicate);
265
266 while self.heap.len() <= id.index() {
267 self.heap.grow(id, DEFAULT_VSIDS_VALUE);
268 }
269
270 self.heap.restore_key(id);
271 });
272 }
273}
274
275impl<BackupBrancher: Brancher> Brancher for AutonomousSearch<BackupBrancher> {
276 fn next_decision(&mut self, context: &mut SelectionContext) -> Option<Predicate> {
277 if self.should_synchronise {
278 self.synchronise_internal();
279 self.should_synchronise = false;
280 }
281 self.statistics.num_calls += 1;
282 self.statistics
283 .average_size_of_heap
284 .add_term(self.heap.num_nonremoved_elements());
285 let result = self
286 .next_candidate_predicate(context)
287 .map(|predicate| self.determine_polarity(predicate));
288 if result.is_none() && !context.are_all_variables_assigned() {
289 self.statistics.num_backup_called += 1;
291 self.backup_brancher.next_decision(context)
292 } else {
293 result
294 }
295 }
296
297 fn log_statistics(&self, statistic_logger: StatisticLogger) {
298 let statistic_logger = statistic_logger.attach_to_prefix("AutonomousSearch");
299 self.statistics.log(statistic_logger);
300 }
301
302 fn on_backtrack(&mut self) {
303 self.backup_brancher.on_backtrack()
304 }
305
306 fn synchronise(&mut self, context: &mut SelectionContext) {
308 self.should_synchronise = true;
309 self.backup_brancher.synchronise(context);
310 }
311
312 fn on_conflict(&mut self) {
313 self.decay_activities();
314 self.backup_brancher.on_conflict();
315 }
316
317 fn on_solution(&mut self, solution: SolutionReference) {
318 self.best_known_solution = Some(solution.into());
320 self.backup_brancher.on_solution(solution);
321 }
322
323 fn on_appearance_in_conflict_predicate(&mut self, predicate: Predicate) {
324 self.bump_activity(predicate);
325 self.backup_brancher
326 .on_appearance_in_conflict_predicate(predicate);
327 }
328
329 fn on_restart(&mut self) {
330 self.backup_brancher.on_restart();
331 }
332
333 fn on_unassign_integer(&mut self, variable: DomainId, value: i32) {
334 self.backup_brancher.on_unassign_integer(variable, value)
335 }
336
337 fn is_restart_pointless(&mut self) -> bool {
338 false
339 }
340
341 fn subscribe_to_events(&self) -> Vec<BrancherEvent> {
342 [
343 BrancherEvent::Solution,
344 BrancherEvent::Conflict,
345 BrancherEvent::Backtrack,
346 BrancherEvent::Synchronise,
347 BrancherEvent::AppearanceInConflictPredicate,
348 ]
349 .into_iter()
350 .chain(self.backup_brancher.subscribe_to_events())
351 .collect()
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::AutonomousSearch;
358 use crate::basic_types::tests::TestRandom;
359 use crate::branching::Brancher;
360 use crate::branching::SelectionContext;
361 use crate::engine::Assignments;
362 use crate::engine::notifications::NotificationEngine;
363 use crate::predicate;
364 use crate::results::SolutionReference;
365
366 #[test]
367 fn brancher_picks_bumped_values() {
368 let mut assignments = Assignments::default();
369 let x = assignments.grow(0, 10);
370 let y = assignments.grow(-10, 0);
371
372 let mut brancher = AutonomousSearch::default_over_all_variables(&assignments);
373 brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5));
374 brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5));
375 brancher.on_appearance_in_conflict_predicate(predicate!(y >= -5));
376
377 (0..100).for_each(|_| brancher.on_conflict());
378 }
379
380 #[test]
381 fn dormant_values() {
382 let mut notification_engine = NotificationEngine::default();
383 let mut assignments = Assignments::default();
384 let x = assignments.grow(0, 10);
385 notification_engine.grow();
386
387 let mut brancher = AutonomousSearch::default_over_all_variables(&assignments);
388
389 let predicate = predicate!(x >= 5);
390 brancher.on_appearance_in_conflict_predicate(predicate);
391 let decision = brancher.next_decision(&mut SelectionContext::new(
392 &assignments,
393 &mut TestRandom::default(),
394 ));
395 assert_eq!(decision, Some(predicate));
396
397 assignments.new_checkpoint();
398 let _ = assignments.post_predicate(predicate!(x >= 5), None, &mut notification_engine);
400
401 assignments.new_checkpoint();
402 let _ = assignments.post_predicate(predicate!(x >= 7), None, &mut notification_engine);
404
405 assignments.new_checkpoint();
406 let _ = assignments.post_predicate(predicate!(x >= 10), None, &mut notification_engine);
408
409 assignments.new_checkpoint();
410 let decision = brancher.next_decision(&mut SelectionContext::new(
413 &assignments,
414 &mut TestRandom::default(),
415 ));
416 assert!(decision.is_none());
417 assert!(brancher.dormant_predicates.contains(&predicate));
418
419 let _ = assignments.synchronise(3, &mut notification_engine);
420
421 let decision = brancher.next_decision(&mut SelectionContext::new(
422 &assignments,
423 &mut TestRandom::default(),
424 ));
425 assert!(decision.is_none());
426 assert!(brancher.dormant_predicates.contains(&predicate));
427
428 let _ = assignments.synchronise(0, &mut notification_engine);
429 brancher.synchronise(&mut SelectionContext::new(
430 &assignments,
431 &mut TestRandom::default(),
432 ));
433
434 let decision = brancher.next_decision(&mut SelectionContext::new(
435 &assignments,
436 &mut TestRandom::default(),
437 ));
438 assert_eq!(decision, Some(predicate));
439 assert!(!brancher.dormant_predicates.contains(&predicate));
440 }
441
442 #[test]
443 fn uses_fallback() {
444 let mut assignments = Assignments::default();
445 let x = assignments.grow(0, 10);
446
447 let mut brancher = AutonomousSearch::default_over_all_variables(&assignments);
448
449 let result = brancher.next_decision(&mut SelectionContext::new(
450 &assignments,
451 &mut TestRandom {
452 integers: vec![2],
453 usizes: vec![0],
454 bools: vec![false],
455 weighted_choice: |_| unreachable!(),
456 },
457 ));
458
459 assert_eq!(result, Some(predicate!(x <= 2)));
460 }
461
462 #[test]
463 fn uses_stored_solution() {
464 let mut notification_engine = NotificationEngine::default();
465 let mut assignments = Assignments::default();
466 let x = assignments.grow(0, 10);
467 notification_engine.grow();
468
469 assignments.new_checkpoint();
470 let _ = assignments.post_predicate(predicate!(x == 7), None, &mut notification_engine);
471
472 let mut brancher = AutonomousSearch::default_over_all_variables(&assignments);
473
474 brancher.on_solution(SolutionReference::new(&assignments));
475
476 let _ = assignments.synchronise(0, &mut notification_engine);
477
478 assert_eq!(
479 predicate!(x >= 5),
480 brancher.determine_polarity(predicate!(x >= 5))
481 );
482 assert_eq!(
483 !predicate!(x >= 10),
484 brancher.determine_polarity(predicate!(x >= 10))
485 );
486 assert_eq!(
487 predicate!(x <= 8),
488 brancher.determine_polarity(predicate!(x <= 8))
489 );
490 assert_eq!(
491 !predicate!(x <= 5),
492 brancher.determine_polarity(predicate!(x <= 5))
493 );
494
495 brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5));
496
497 let result = brancher.next_decision(&mut SelectionContext::new(
498 &assignments,
499 &mut TestRandom::default(),
500 ));
501 assert_eq!(result, Some(predicate!(x >= 5)));
502 }
503}