1use crate::rules::{AggregateReductionResult, ReduceTo, ReduceToAggregate, ReductionResult};
4use crate::traits::Problem;
5use crate::types::{OptimizationValue, Or};
6use serde::de::DeserializeOwned;
7use serde::{Deserialize, Deserializer, Serialize};
8
9pub trait DecisionProblemMeta: Problem
11where
12 Self::Value: OptimizationValue,
13{
14 const DECISION_NAME: &'static str;
16}
17
18#[macro_export]
20macro_rules! decision_problem_meta {
21 ($inner:ty, $name:literal) => {
22 impl $crate::models::decision::DecisionProblemMeta for $inner {
23 const DECISION_NAME: &'static str = $name;
24 }
25 };
26}
27
28#[macro_export]
35macro_rules! register_decision_variant {
36 (
37 $inner:ty,
38 $name:literal,
39 $complexity:literal,
40 $aliases:expr,
41 $description:literal,
42 category: $category:expr,
43 dims: [$($dim:expr),* $(,)?],
44 fields: [$($field:expr),* $(,)?],
45 $(additional: [$($additional:ty => $additional_complexity:literal),* $(,)?],)?
46 decode: $decoder:expr
47 $(, $random:ident)?
48 ) => {
49 $crate::register_decision_variant!(@declare $inner, $complexity, $decoder, [$($($additional => $additional_complexity),*)?] $(, $random)?);
50
51 $crate::inventory::submit! {
52 $crate::registry::ProblemSchemaEntry {
53 name: $name,
54 display_name: $crate::register_decision_variant!(@display_name $name),
55 aliases: $aliases,
56 dimensions: &[$($dim),*],
57 category: $category,
58 module_path: module_path!(),
59 description: $description,
60 fields: &[$($field),*],
61 }
62 }
63
64 $crate::register_decision_variant!(@edges $inner, $name);
65 $($(
66 $crate::register_brute_force! {
67 $crate::models::decision::Decision<$additional> decode $decoder,
68 }
69 $crate::register_decision_variant!(@edges $additional, $name);
70 )*)?
71 };
72
73 (@edges $inner:ty, $name:literal) => {
74 $crate::inventory::submit! {
76 $crate::rules::ReductionEntry {
77 source_name: $name,
78 target_name: <$inner as $crate::traits::Problem>::NAME,
79 source_variant_fn: <$crate::models::decision::Decision<$inner> as $crate::traits::Problem>::variant,
80 target_variant_fn: <$inner as $crate::traits::Problem>::variant,
81 parameter_declarations_fn: || $crate::rules::registry::ReductionParameterDeclarations {
82 relation: Some($crate::parameters::ParameterRelation::Exact),
83 fields: <$inner as $crate::traits::Problem>::parameter_names()
84 .iter()
85 .map(|&name| (name, $crate::expr::Expr::variable(name)))
86 .collect(),
87 unavailable: vec![],
88 },
89 module_path: module_path!(),
90 reduce_fn: Some(|any| {
91 let source = any
92 .downcast_ref::<$crate::models::decision::Decision<$inner>>()
93 .ok_or_else($crate::rules::ReductionError::source_type_mismatch::<
94 $crate::models::decision::Decision<$inner>,
95 $inner,
96 >)?;
97 let result =
98 <$crate::models::decision::Decision<$inner> as $crate::rules::ReduceTo<$inner>>::reduce_to(source)?;
99 Ok(Box::new(result))
100 }),
101 reduce_aggregate_fn: Some(|any| {
102 let source = any
103 .downcast_ref::<$crate::models::decision::Decision<$inner>>()
104 .ok_or_else($crate::rules::ReductionError::source_type_mismatch::<
105 $crate::models::decision::Decision<$inner>,
106 $inner,
107 >)?;
108 let result =
109 <$crate::models::decision::Decision<$inner> as $crate::rules::ReduceToAggregate<$inner>>::reduce_to_aggregate(source)?;
110 Ok(Box::new(result))
111 }),
112 turing: false,
113 }
114 }
115
116 $crate::inventory::submit! {
118 $crate::rules::ReductionEntry {
119 source_name: <$inner as $crate::traits::Problem>::NAME,
120 target_name: $name,
121 source_variant_fn: <$inner as $crate::traits::Problem>::variant,
122 target_variant_fn: <$crate::models::decision::Decision<$inner> as $crate::traits::Problem>::variant,
123 parameter_declarations_fn: || $crate::rules::registry::ReductionParameterDeclarations {
124 relation: Some($crate::parameters::ParameterRelation::Exact),
125 fields: <$inner as $crate::traits::Problem>::parameter_names()
126 .iter()
127 .map(|&name| (name, $crate::expr::Expr::variable(name)))
128 .collect(),
129 unavailable: vec![],
130 },
131 module_path: module_path!(),
132 reduce_fn: None,
133 reduce_aggregate_fn: None,
134 turing: true,
135 }
136 }
137 };
138
139 (@declare $inner:ty, $complexity:literal, $decoder:expr, [$($additional:ty => $additional_complexity:literal),*], random) => {
140 $crate::declare_variants! {
141 default $crate::models::decision::Decision<$inner> => $complexity create $crate::models::decision::DecisionCreateSpec<$inner> random,
142 $($crate::models::decision::Decision<$additional> => $additional_complexity create $crate::models::decision::DecisionCreateSpec<$additional>,)*
143 }
144 $crate::register_brute_force! {
145 $crate::models::decision::Decision<$inner> decode $decoder,
146 }
147 };
148 (@declare $inner:ty, $complexity:literal, $decoder:expr, [$($additional:ty => $additional_complexity:literal),*]) => {
149 $crate::declare_variants! {
150 default $crate::models::decision::Decision<$inner> => $complexity create $crate::models::decision::DecisionCreateSpec<$inner>,
151 $($crate::models::decision::Decision<$additional> => $additional_complexity create $crate::models::decision::DecisionCreateSpec<$additional>,)*
152 }
153 $crate::register_brute_force! {
154 $crate::models::decision::Decision<$inner> decode $decoder,
155 }
156 };
157 (@display_name "DecisionMinimumVertexCover") => {
158 "Decision Minimum Vertex Cover"
159 };
160 (@display_name "DecisionMinimumDominatingSet") => {
161 "Decision Minimum Dominating Set"
162 };
163 (@display_name "DecisionMaximumIndependentSet") => {
164 "Decision Maximum Independent Set"
165 };
166 (@display_name $name:literal) => {
167 $name
168 };
169}
170
171#[doc(hidden)]
176pub struct DecisionCreateSpec<P>
177where
178 P: Problem,
179 P::Value: OptimizationValue,
180{
181 inner: P,
182 bound: <P::Value as OptimizationValue>::Inner,
183}
184
185impl<P> crate::registry::CreateSpec for DecisionCreateSpec<P>
186where
187 P: Problem,
188 P::Value: OptimizationValue,
189{
190 const FIELDS: &'static [crate::registry::FieldInfo] = &[];
191
192 fn inputs() -> Vec<crate::registry::CreateInputInfo> {
193 let variant = P::variant()
194 .into_iter()
195 .map(|(k, v)| (k.to_string(), v.to_string()))
196 .collect();
197 let entry = crate::registry::find_variant_entry(P::NAME, &variant)
198 .expect("decision inner variant must be registered");
199 let mut inputs = entry.inputs();
200 inputs.push(crate::registry::CreateInputInfo {
201 name: "bound",
202 type_name: std::any::type_name::<<P::Value as OptimizationValue>::Inner>(),
203 description: "Decision objective bound",
204 required: true,
205 codec: crate::registry::CreateInputCodec::Scalar,
206 });
207 inputs
208 }
209}
210
211impl<'de, P> Deserialize<'de> for DecisionCreateSpec<P>
212where
213 P: Problem + 'static,
214 P::Value: OptimizationValue,
215 <P::Value as OptimizationValue>::Inner: DeserializeOwned,
216{
217 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
218 where
219 D: Deserializer<'de>,
220 {
221 let value = serde_json::Value::deserialize(deserializer)?;
222 let mut inputs = value.as_object().cloned().ok_or_else(|| {
223 serde::de::Error::custom("decision construction inputs must be an object")
224 })?;
225 let bound = inputs
226 .remove("bound")
227 .ok_or_else(|| serde::de::Error::missing_field("bound"))?;
228 let variant = P::variant()
229 .into_iter()
230 .map(|(k, v)| (k.to_string(), v.to_string()))
231 .collect();
232 let entry = crate::registry::find_variant_entry(P::NAME, &variant)
233 .expect("decision inner variant must be registered");
234 let constructed = (entry.construct_fn)(serde_json::Value::Object(inputs))
235 .map_err(serde::de::Error::custom)?;
236 let inner = *(constructed as Box<dyn std::any::Any>)
237 .downcast::<P>()
238 .expect("registered constructor must return the declared inner type");
239 let bound = serde_json::from_value(bound).map_err(serde::de::Error::custom)?;
240 Ok(Self { inner, bound })
241 }
242}
243
244impl<P> From<DecisionCreateSpec<P>> for Decision<P>
245where
246 P: Problem,
247 P::Value: OptimizationValue,
248{
249 fn from(spec: DecisionCreateSpec<P>) -> Self {
250 Self::new(spec.inner, spec.bound)
251 }
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct Decision<P: Problem>
257where
258 P::Value: OptimizationValue,
259{
260 inner: P,
261 bound: <P::Value as OptimizationValue>::Inner,
262}
263
264impl<P: Problem> Decision<P>
265where
266 P::Value: OptimizationValue,
267{
268 pub fn new(inner: P, bound: <P::Value as OptimizationValue>::Inner) -> Self {
270 Self { inner, bound }
271 }
272
273 pub fn inner(&self) -> &P {
275 &self.inner
276 }
277
278 pub fn bound(&self) -> &<P::Value as OptimizationValue>::Inner {
280 &self.bound
281 }
282}
283
284impl<P> Problem for Decision<P>
285where
286 P: DecisionProblemMeta,
287 P::Value: OptimizationValue,
288{
289 const NAME: &'static str = P::DECISION_NAME;
290 type Solution = P::Solution;
291 type Value = Or;
292
293 fn parameter_names() -> &'static [&'static str] {
294 P::parameter_names()
295 }
296
297 fn parameters(&self) -> crate::types::ProblemParameters {
298 self.inner.parameters()
299 }
300
301 fn evaluate(&self, config: &Self::Solution) -> Result<Or, crate::traits::EvaluationError> {
302 Ok({
303 Or(<P::Value as OptimizationValue>::meets_bound(
304 &self.inner.evaluate(config)?,
305 &self.bound,
306 ))
307 })
308 }
309
310 fn variant() -> Vec<(&'static str, &'static str)> {
311 P::variant()
312 }
313}
314
315impl<P> crate::solvers::BruteForceProblem for Decision<P>
316where
317 P: DecisionProblemMeta + crate::solvers::BruteForceProblem,
318 P::Value: OptimizationValue,
319{
320 fn dimensions(&self) -> Vec<usize> {
321 self.inner.dimensions()
322 }
323}
324
325#[derive(Debug, Clone)]
327pub struct DecisionToOptimizationResult<P>
328where
329 P: Problem,
330 P::Value: OptimizationValue,
331{
332 target: P,
333 bound: <P::Value as OptimizationValue>::Inner,
334}
335
336impl<P> AggregateReductionResult for DecisionToOptimizationResult<P>
337where
338 P: DecisionProblemMeta + 'static,
339 P::Value: OptimizationValue + Serialize + DeserializeOwned,
340{
341 type Source = Decision<P>;
342 type Target = P;
343
344 fn target_problem(&self) -> &Self::Target {
345 &self.target
346 }
347
348 fn extract_value(&self, target_value: P::Value) -> Or {
349 Or(<P::Value as OptimizationValue>::meets_bound(
350 &target_value,
351 &self.bound,
352 ))
353 }
354}
355
356impl<P> ReduceToAggregate<P> for Decision<P>
357where
358 P: DecisionProblemMeta + Clone + 'static,
359 P::Value: OptimizationValue + Serialize + DeserializeOwned,
360{
361 type Result = DecisionToOptimizationResult<P>;
362
363 fn reduce_to_aggregate(&self) -> Result<Self::Result, crate::rules::ReductionError> {
364 Ok(DecisionToOptimizationResult {
365 target: self.inner.clone(),
366 bound: self.bound.clone(),
367 })
368 }
369}
370
371#[derive(Debug, Clone)]
377pub struct DecisionToOptimizationWitnessResult<P>
378where
379 P: Problem,
380 P::Value: OptimizationValue,
381{
382 target: P,
383}
384
385impl<P> ReductionResult for DecisionToOptimizationWitnessResult<P>
386where
387 P: DecisionProblemMeta + 'static,
388 P::Solution: Clone,
389 P::Value: OptimizationValue + Serialize + DeserializeOwned,
390{
391 type Source = Decision<P>;
392 type Target = P;
393
394 fn target_problem(&self) -> &Self::Target {
395 &self.target
396 }
397
398 fn extract_solution(
399 &self,
400 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
401 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
402 crate::rules::validate_target_solution(self.target_problem(), target_solution)?;
403
404 Ok(target_solution.clone())
405 }
406}
407
408impl<P> ReduceTo<P> for Decision<P>
409where
410 P: DecisionProblemMeta + Clone + 'static,
411 P::Solution: Clone,
412 P::Value: OptimizationValue + Serialize + DeserializeOwned,
413{
414 type Result = DecisionToOptimizationWitnessResult<P>;
415
416 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
417 Ok(DecisionToOptimizationWitnessResult {
418 target: self.inner.clone(),
419 })
420 }
421}
422
423#[cfg(test)]
424#[path = "../unit_tests/models/decision.rs"]
425mod tests;