Skip to main content

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;