problemreductions/rules/traits.rs
1//! Core traits for problem reductions.
2
3use crate::traits::Problem;
4use serde::de::DeserializeOwned;
5use serde::Serialize;
6use std::any::Any;
7use std::marker::PhantomData;
8
9/// Result of reducing a source problem to a target problem.
10///
11/// This trait encapsulates the target problem and provides methods
12/// to extract solutions back to the source problem space.
13pub trait ReductionResult {
14 /// The source problem type.
15 type Source: Problem;
16 /// The target problem type.
17 type Target: Problem;
18
19 /// Get a reference to the target problem.
20 fn target_problem(&self) -> &Self::Target;
21
22 /// Extract a solution from target problem space to source problem space.
23 ///
24 /// # Arguments
25 /// * `target_solution` - A solution to the target problem
26 ///
27 /// # Returns
28 /// The corresponding solution in the source problem space
29 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize>;
30}
31
32/// Trait for problems that can be reduced to target type T.
33///
34/// # Example
35/// ```text
36/// // Example showing reduction workflow
37/// use problemreductions::prelude::*;
38/// use problemreductions::rules::ReduceTo;
39///
40/// let sat_problem: Satisfiability = Satisfiability::new(
41/// 3, // 3 variables
42/// vec![
43/// CNFClause::new(vec![0, 1]), // (x0 OR x1)
44/// CNFClause::new(vec![1, 2]), // (x1 OR x2)
45/// ]
46/// );
47///
48/// // Reduce to Independent Set
49/// let reduction = sat_problem.reduce_to();
50/// let is_problem = reduction.target_problem();
51///
52/// // Solve and extract solutions
53/// let solver = BruteForce::new();
54/// let solutions = solver.find_all_witnesses(is_problem);
55/// let sat_solutions: Vec<_> = solutions.iter()
56/// .map(|s| reduction.extract_solution(s))
57/// .collect();
58/// ```
59pub trait ReduceTo<T: Problem>: Problem {
60 /// The reduction result type.
61 type Result: ReductionResult<Source = Self, Target = T>;
62
63 /// Reduce this problem to the target problem type.
64 fn reduce_to(&self) -> Self::Result;
65}
66
67/// Result of reducing a source problem to a target problem for aggregate values.
68///
69/// Unlike [`ReductionResult`], this trait maps aggregate values back from target
70/// space to source space instead of mapping witness configurations.
71pub trait AggregateReductionResult {
72 /// The source problem type.
73 type Source: Problem;
74 /// The target problem type.
75 type Target: Problem;
76
77 /// Get a reference to the target problem.
78 fn target_problem(&self) -> &Self::Target;
79
80 /// Extract an aggregate value from target problem space back to source space.
81 fn extract_value(
82 &self,
83 target_value: <Self::Target as Problem>::Value,
84 ) -> <Self::Source as Problem>::Value;
85}
86
87/// Trait for problems that can be reduced to target type T for aggregate-value
88/// workflows.
89pub trait ReduceToAggregate<T: Problem>: Problem {
90 /// The reduction result type.
91 type Result: AggregateReductionResult<Source = Self, Target = T>;
92
93 /// Reduce this problem to the target problem type.
94 fn reduce_to_aggregate(&self) -> Self::Result;
95}
96
97/// Generic reduction result for natural-edge (subtype) reductions.
98///
99/// Used when a problem on a specific graph type is trivially reducible to
100/// the same problem on a more general graph type (e.g., `MIS<Triangular>` →
101/// `MIS<SimpleGraph>`). The solution mapping is identity — vertex indices
102/// are preserved.
103#[derive(Debug, Clone)]
104pub struct ReductionAutoCast<S: Problem, T: Problem> {
105 target: T,
106 _phantom: PhantomData<S>,
107}
108
109impl<S: Problem, T: Problem> ReductionAutoCast<S, T> {
110 /// Create a new auto-cast reduction result.
111 pub fn new(target: T) -> Self {
112 Self {
113 target,
114 _phantom: PhantomData,
115 }
116 }
117}
118
119impl<S: Problem, T: Problem> ReductionResult for ReductionAutoCast<S, T> {
120 type Source = S;
121 type Target = T;
122
123 fn target_problem(&self) -> &Self::Target {
124 &self.target
125 }
126
127 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
128 target_solution.to_vec()
129 }
130}
131
132impl<S: Problem, T: Problem<Value = S::Value>> AggregateReductionResult
133 for ReductionAutoCast<S, T>
134{
135 type Source = S;
136 type Target = T;
137
138 fn target_problem(&self) -> &Self::Target {
139 &self.target
140 }
141
142 fn extract_value(&self, target_value: T::Value) -> S::Value {
143 target_value
144 }
145}
146
147/// Type-erased reduction result for runtime-discovered paths.
148///
149/// Implemented automatically for all `ReductionResult` types via blanket impl.
150/// Used internally by `ReductionChain`.
151pub trait DynReductionResult {
152 /// Get the target problem as a type-erased reference.
153 fn target_problem_any(&self) -> &dyn Any;
154 /// Extract a solution from target space to source space.
155 fn extract_solution_dyn(&self, target_solution: &[usize]) -> Vec<usize>;
156}
157
158impl<R: ReductionResult + 'static> DynReductionResult for R
159where
160 R::Target: 'static,
161{
162 fn target_problem_any(&self) -> &dyn Any {
163 self.target_problem() as &dyn Any
164 }
165 fn extract_solution_dyn(&self, target_solution: &[usize]) -> Vec<usize> {
166 self.extract_solution(target_solution)
167 }
168}
169
170/// Type-erased aggregate reduction result for runtime-discovered paths.
171pub trait DynAggregateReductionResult {
172 /// Get the target problem as a type-erased reference.
173 fn target_problem_any(&self) -> &dyn Any;
174 /// Extract an aggregate value from target space to source space.
175 fn extract_value_dyn(&self, target_value: serde_json::Value) -> serde_json::Value;
176}
177
178impl<R: AggregateReductionResult + 'static> DynAggregateReductionResult for R
179where
180 R::Target: 'static,
181 <R::Target as Problem>::Value: Serialize + DeserializeOwned,
182 <R::Source as Problem>::Value: Serialize,
183{
184 fn target_problem_any(&self) -> &dyn Any {
185 self.target_problem() as &dyn Any
186 }
187
188 fn extract_value_dyn(&self, target_value: serde_json::Value) -> serde_json::Value {
189 let target_value = serde_json::from_value(target_value)
190 .expect("DynAggregateReductionResult target value deserialize failed");
191 let source_value = self.extract_value(target_value);
192 serde_json::to_value(source_value)
193 .expect("DynAggregateReductionResult source value serialize failed")
194 }
195}
196
197#[cfg(test)]
198#[path = "../unit_tests/rules/traits.rs"]
199mod tests;