problemreductions/solvers/
brute_force.rs1use std::any::Any;
4
5use crate::solvers::SolveError;
6use crate::traits::Problem;
7use crate::types::{Aggregate, SolutionAggregate};
8
9type CartesianWitness<P> = Option<(<P as Problem>::Solution, <P as Problem>::Value)>;
10
11#[doc(hidden)]
12pub type BruteForceDimensionsFn = fn(&dyn Any) -> Vec<usize>;
13#[doc(hidden)]
14pub type BruteForceSolveFn =
15 fn(&dyn Any) -> Result<Option<(serde_json::Value, String)>, SolveError>;
16#[doc(hidden)]
17pub type BruteForceSolveTypedFn = fn(&dyn Any) -> Result<Option<Box<dyn Any>>, SolveError>;
18#[doc(hidden)]
19pub type BruteForceSolveTypedWithWitnessesFn = fn(&dyn Any) -> Result<Box<dyn Any>, SolveError>;
20
21#[derive(Debug)]
23#[doc(hidden)]
24pub struct BruteForceRegistration {
25 pub source_name: &'static str,
26 pub source_variant_fn: fn() -> Vec<(&'static str, &'static str)>,
27 pub dimensions_fn: BruteForceDimensionsFn,
28 pub solve_fn: BruteForceSolveFn,
29 pub solve_typed_fn: BruteForceSolveTypedFn,
30 pub solve_typed_with_witnesses_fn: BruteForceSolveTypedWithWitnessesFn,
31}
32
33inventory::collect!(BruteForceRegistration);
34
35pub trait BruteForceProblem: Problem {
37 fn dimensions(&self) -> Vec<usize>;
39
40 fn num_variables(&self) -> usize {
42 self.dimensions().len()
43 }
44}
45
46pub(crate) struct CartesianIndices {
47 dimensions: Vec<usize>,
48 current: Option<Vec<usize>>,
49}
50
51impl CartesianIndices {
52 pub(crate) fn new(dimensions: Vec<usize>) -> Result<Self, SolveError> {
53 let current = if dimensions.contains(&0) {
54 None
55 } else {
56 let mut current = Vec::new();
57 current.try_reserve_exact(dimensions.len())?;
58 current.resize(dimensions.len(), 0);
59 Some(current)
60 };
61 Ok(Self {
62 current,
63 dimensions,
64 })
65 }
66}
67
68impl Iterator for CartesianIndices {
69 type Item = Vec<usize>;
70
71 fn next(&mut self) -> Option<Self::Item> {
72 let current = self.current.take()?;
73 let mut next = current.clone();
74 for index in (0..self.dimensions.len()).rev() {
75 next[index] += 1;
76 if next[index] < self.dimensions[index] {
77 self.current = Some(next);
78 break;
79 }
80 next[index] = 0;
81 }
82 Some(current)
83 }
84
85 fn size_hint(&self) -> (usize, Option<usize>) {
86 if self.current.is_some() {
87 (1, None)
88 } else {
89 (0, Some(0))
90 }
91 }
92}
93
94#[derive(Debug, Clone, Default)]
96pub struct BruteForce;
97
98impl BruteForce {
99 pub fn new() -> Self {
101 Self
102 }
103
104 fn registration<P: Problem>(&self) -> Result<&'static BruteForceRegistration, SolveError> {
105 let key = crate::solvers::ExactProblemKey::new(
106 P::NAME,
107 P::variant()
108 .into_iter()
109 .map(|(name, value)| (name.to_string(), value.to_string()))
110 .collect(),
111 );
112 crate::solvers::registry::brute_force_registration(&key)
113 .map_err(SolveError::InvalidRegistry)?
114 .ok_or_else(|| SolveError::MissingRegistration(P::NAME.to_string()))
115 }
116
117 pub fn solve<P>(&self, problem: &P) -> Result<Option<P::Solution>, SolveError>
119 where
120 P: Problem + 'static,
121 P::Solution: 'static,
122 P::Value: SolutionAggregate + 'static,
123 {
124 let solution = (self.registration::<P>()?.solve_typed_fn)(problem as &dyn Any)?;
125 solution
126 .map(|solution| {
127 solution
128 .downcast::<P::Solution>()
129 .map(|value| *value)
130 .map_err(|_| {
131 SolveError::RegistrationTypeMismatch(format!(
132 "{} solution registration returned the wrong type",
133 P::NAME
134 ))
135 })
136 })
137 .transpose()
138 }
139
140 pub fn find_all_witnesses<P>(&self, problem: &P) -> Result<Vec<P::Solution>, SolveError>
142 where
143 P: Problem + 'static,
144 P::Solution: 'static,
145 P::Value: SolutionAggregate + 'static,
146 {
147 self.solve_with_witnesses(problem)
148 .map(|(_, witnesses)| witnesses)
149 }
150
151 pub fn solve_with_witnesses<P>(
153 &self,
154 problem: &P,
155 ) -> Result<(P::Value, Vec<P::Solution>), SolveError>
156 where
157 P: Problem + 'static,
158 P::Solution: 'static,
159 P::Value: SolutionAggregate + 'static,
160 {
161 (self.registration::<P>()?.solve_typed_with_witnesses_fn)(problem as &dyn Any)?
162 .downcast::<(P::Value, Vec<P::Solution>)>()
163 .map(|result| *result)
164 .map_err(|_| {
165 SolveError::RegistrationTypeMismatch(format!(
166 "{} aggregate-and-witness registration returned the wrong type",
167 P::NAME
168 ))
169 })
170 }
171
172 pub(crate) fn solve_cartesian<P, F>(
173 &self,
174 problem: &P,
175 decode: F,
176 ) -> Result<P::Value, SolveError>
177 where
178 P: BruteForceProblem,
179 P::Value: Aggregate,
180 F: Fn(Vec<usize>) -> P::Solution,
181 {
182 let mut total = P::Value::identity();
183 for indices in CartesianIndices::new(problem.dimensions())? {
184 total = total.combine(problem.evaluate(&decode(indices))?)?;
185 if total.is_absorbing() {
186 break;
187 }
188 }
189 Ok(total)
190 }
191
192 pub(crate) fn solve_with_witnesses_cartesian<P, F>(
193 &self,
194 problem: &P,
195 decode: F,
196 ) -> Result<(P::Value, Vec<P::Solution>), SolveError>
197 where
198 P: BruteForceProblem,
199 P::Value: SolutionAggregate,
200 F: Fn(Vec<usize>) -> P::Solution,
201 {
202 let total = self.solve_cartesian(problem, &decode)?;
203 let mut witnesses = Vec::new();
204 for indices in CartesianIndices::new(problem.dimensions())? {
205 let solution = decode(indices);
206 let value = problem.evaluate(&solution)?;
207 if P::Value::contributes_to_solution(&value, &total) {
208 witnesses.push(solution);
209 }
210 }
211 Ok((total, witnesses))
212 }
213
214 pub(crate) fn find_cartesian<P, F>(
215 &self,
216 problem: &P,
217 decode: F,
218 ) -> Result<CartesianWitness<P>, SolveError>
219 where
220 P: BruteForceProblem,
221 P::Value: SolutionAggregate,
222 F: Fn(Vec<usize>) -> P::Solution,
223 {
224 let total = self.solve_cartesian(problem, &decode)?;
225 for indices in CartesianIndices::new(problem.dimensions())? {
226 let solution = decode(indices);
227 let value = problem.evaluate(&solution)?;
228 if P::Value::contributes_to_solution(&value, &total) {
229 return Ok(Some((solution, value)));
230 }
231 }
232 Ok(None)
233 }
234}
235
236#[cfg(test)]
237#[path = "../unit_tests/solvers/brute_force.rs"]
238mod tests;