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 remaining: usize,
50}
51
52impl CartesianIndices {
53 pub(crate) fn new(dimensions: Vec<usize>) -> Result<Self, SolveError> {
54 let total = if dimensions.is_empty() {
55 1
56 } else if dimensions.contains(&0) {
57 0
58 } else {
59 dimensions.iter().try_fold(1usize, |total, &dimension| {
60 total
61 .checked_mul(dimension)
62 .ok_or_else(|| SolveError::SearchSpaceOverflow(dimensions.clone()))
63 })?
64 };
65 Ok(Self {
66 current: (total != 0).then(|| vec![0; dimensions.len()]),
67 dimensions,
68 remaining: total,
69 })
70 }
71}
72
73impl Iterator for CartesianIndices {
74 type Item = Vec<usize>;
75
76 fn next(&mut self) -> Option<Self::Item> {
77 let current = self.current.take()?;
78 let mut next = current.clone();
79 for index in (0..self.dimensions.len()).rev() {
80 next[index] += 1;
81 if next[index] < self.dimensions[index] {
82 break;
83 }
84 next[index] = 0;
85 }
86 self.remaining -= 1;
87 if self.remaining != 0 {
88 self.current = Some(next);
89 }
90 Some(current)
91 }
92
93 fn size_hint(&self) -> (usize, Option<usize>) {
94 (self.remaining, Some(self.remaining))
95 }
96}
97
98impl ExactSizeIterator for CartesianIndices {}
99
100#[derive(Debug, Clone, Default)]
102pub struct BruteForce;
103
104impl BruteForce {
105 pub fn new() -> Self {
107 Self
108 }
109
110 fn registration<P: Problem>(&self) -> Result<&'static BruteForceRegistration, SolveError> {
111 let key = crate::solvers::ExactProblemKey::new(
112 P::NAME,
113 P::variant()
114 .into_iter()
115 .map(|(name, value)| (name.to_string(), value.to_string()))
116 .collect(),
117 );
118 crate::solvers::registry::brute_force_registration(&key)
119 .map_err(SolveError::InvalidRegistry)?
120 .ok_or_else(|| SolveError::MissingRegistration(P::NAME.to_string()))
121 }
122
123 pub fn solve<P>(&self, problem: &P) -> Result<Option<P::Solution>, SolveError>
125 where
126 P: Problem + 'static,
127 P::Solution: 'static,
128 P::Value: SolutionAggregate + 'static,
129 {
130 let solution = (self.registration::<P>()?.solve_typed_fn)(problem as &dyn Any)?;
131 solution
132 .map(|solution| {
133 solution
134 .downcast::<P::Solution>()
135 .map(|value| *value)
136 .map_err(|_| {
137 SolveError::RegistrationTypeMismatch(format!(
138 "{} solution registration returned the wrong type",
139 P::NAME
140 ))
141 })
142 })
143 .transpose()
144 }
145
146 pub fn find_all_witnesses<P>(&self, problem: &P) -> Result<Vec<P::Solution>, SolveError>
148 where
149 P: Problem + 'static,
150 P::Solution: 'static,
151 P::Value: SolutionAggregate + 'static,
152 {
153 self.solve_with_witnesses(problem)
154 .map(|(_, witnesses)| witnesses)
155 }
156
157 pub fn solve_with_witnesses<P>(
159 &self,
160 problem: &P,
161 ) -> Result<(P::Value, Vec<P::Solution>), SolveError>
162 where
163 P: Problem + 'static,
164 P::Solution: 'static,
165 P::Value: SolutionAggregate + 'static,
166 {
167 (self.registration::<P>()?.solve_typed_with_witnesses_fn)(problem as &dyn Any)?
168 .downcast::<(P::Value, Vec<P::Solution>)>()
169 .map(|result| *result)
170 .map_err(|_| {
171 SolveError::RegistrationTypeMismatch(format!(
172 "{} aggregate-and-witness registration returned the wrong type",
173 P::NAME
174 ))
175 })
176 }
177
178 pub(crate) fn solve_cartesian<P, F>(
179 &self,
180 problem: &P,
181 decode: F,
182 ) -> Result<P::Value, SolveError>
183 where
184 P: BruteForceProblem,
185 P::Value: Aggregate,
186 F: Fn(Vec<usize>) -> P::Solution,
187 {
188 let mut total = P::Value::identity();
189 for indices in CartesianIndices::new(problem.dimensions())? {
190 total = total.combine(problem.evaluate(&decode(indices))?)?;
191 if total.is_absorbing() {
192 break;
193 }
194 }
195 Ok(total)
196 }
197
198 pub(crate) fn solve_with_witnesses_cartesian<P, F>(
199 &self,
200 problem: &P,
201 decode: F,
202 ) -> Result<(P::Value, Vec<P::Solution>), SolveError>
203 where
204 P: BruteForceProblem,
205 P::Value: SolutionAggregate,
206 F: Fn(Vec<usize>) -> P::Solution,
207 {
208 let total = self.solve_cartesian(problem, &decode)?;
209 let mut witnesses = Vec::new();
210 for indices in CartesianIndices::new(problem.dimensions())? {
211 let solution = decode(indices);
212 let value = problem.evaluate(&solution)?;
213 if P::Value::contributes_to_solution(&value, &total) {
214 witnesses.push(solution);
215 }
216 }
217 Ok((total, witnesses))
218 }
219
220 pub(crate) fn find_cartesian<P, F>(
221 &self,
222 problem: &P,
223 decode: F,
224 ) -> Result<CartesianWitness<P>, SolveError>
225 where
226 P: BruteForceProblem,
227 P::Value: SolutionAggregate,
228 F: Fn(Vec<usize>) -> P::Solution,
229 {
230 let total = self.solve_cartesian(problem, &decode)?;
231 for indices in CartesianIndices::new(problem.dimensions())? {
232 let solution = decode(indices);
233 let value = problem.evaluate(&solution)?;
234 if P::Value::contributes_to_solution(&value, &total) {
235 return Ok(Some((solution, value)));
236 }
237 }
238 Ok(None)
239 }
240}
241
242#[cfg(test)]
243#[path = "../unit_tests/solvers/brute_force.rs"]
244mod tests;