dypdl_heuristic_search/search_algorithm/
search.rs1use super::data_structure::{SuccessorGenerator, TransitionWithId};
2use dypdl::{variable_type::Numeric, Model, Transition, TransitionInterface};
3use std::error::Error;
4use std::{ops::Deref, rc::Rc};
5
6#[derive(Debug, PartialEq, Clone)]
8pub struct SearchInput<'a, N, V = Transition, D = Rc<TransitionWithId<V>>, R = Rc<dypdl::Model>>
9where
10 V: TransitionInterface,
11 D: Deref<Target = TransitionWithId<V>> + Clone,
12 R: Deref<Target = Model>,
13{
14 pub node: Option<N>,
16 pub generator: SuccessorGenerator<V, D, R>,
18 pub solution_suffix: &'a [TransitionWithId<V>],
20}
21
22#[derive(Debug, PartialEq, Clone, Copy, Default)]
24pub struct Parameters<T> {
25 pub primal_bound: Option<T>,
27 pub time_limit: Option<f64>,
29 pub get_all_solutions: bool,
31 pub initial_registry_capacity: Option<usize>,
33 pub quiet: bool,
35}
36
37#[derive(Debug, Default, PartialEq, Clone)]
39pub struct Solution<T: Numeric, V = Transition> {
40 pub cost: Option<T>,
43 pub best_bound: Option<T>,
45 pub is_optimal: bool,
47 pub is_infeasible: bool,
49 pub transitions: Vec<V>,
51 pub expanded: usize,
53 pub generated: usize,
55 pub time: f64,
57 pub time_out: bool,
59}
60
61impl<T: Numeric, V> Solution<T, V> {
62 pub fn is_terminated(&self) -> bool {
64 self.is_optimal || self.is_infeasible || self.time_out
65 }
66}
67
68pub trait Search<T: Numeric> {
70 fn search(&mut self) -> Result<Solution<T>, Box<dyn Error>> {
75 loop {
76 let (solution, terminated) = self.search_next()?;
77
78 if terminated {
79 return Ok(solution);
80 }
81 }
82 }
83
84 fn search_next(&mut self) -> Result<(Solution<T>, bool), Box<dyn Error>>;
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use dypdl::Integer;
95
96 struct MockSearch {
97 counter: Integer,
98 }
99
100 impl Search<Integer> for MockSearch {
101 fn search_next(&mut self) -> Result<(Solution<Integer>, bool), Box<dyn Error>> {
102 if self.counter > 0 {
103 self.counter -= 1;
104 Ok((
105 Solution {
106 cost: Some(self.counter),
107 ..Default::default()
108 },
109 false,
110 ))
111 } else {
112 Ok((
113 Solution {
114 cost: Some(0),
115 ..Default::default()
116 },
117 true,
118 ))
119 }
120 }
121 }
122
123 #[test]
124 fn solution_is_terminated_optimal() {
125 let solution: Solution<Integer> = Solution {
126 is_optimal: true,
127 ..Default::default()
128 };
129 assert!(solution.is_terminated());
130 }
131
132 #[test]
133 fn solution_is_terminated_infeasible() {
134 let solution: Solution<Integer> = Solution {
135 is_infeasible: true,
136 ..Default::default()
137 };
138 assert!(solution.is_terminated());
139 }
140
141 #[test]
142 fn solution_is_terminated_time_out() {
143 let solution: Solution<Integer> = Solution {
144 time_out: true,
145 ..Default::default()
146 };
147 assert!(solution.is_terminated());
148 }
149
150 #[test]
151 fn solution_is_not_terminated() {
152 let solution: Solution<Integer> = Solution {
153 is_optimal: false,
154 is_infeasible: false,
155 time_out: false,
156 ..Default::default()
157 };
158 assert!(!solution.is_terminated());
159 }
160
161 #[test]
162 fn search() {
163 let mut solver = MockSearch { counter: 3 };
164 let solution = solver.search();
165 assert!(solution.is_ok());
166 assert_eq!(
167 solution.unwrap(),
168 Solution {
169 cost: Some(0),
170 ..Default::default()
171 }
172 );
173 }
174}