Skip to main content

problemreductions/
variant.rs

1//! Variant system for type-level problem parameterization.
2//!
3//! Types declare their variant category and value via `VariantParam`.
4//! The `impl_variant_param!` macro registers types with the trait.
5//! The `variant_params!` macro composes `Problem::variant()` bodies from type parameter names.
6
7/// A type that participates in the variant system.
8///
9/// Declares its category (e.g., `"graph"`) and value (e.g., `"SimpleGraph"`).
10pub trait VariantParam: 'static {
11    /// Category name (e.g., `"graph"`, `"weight"`, `"k"`).
12    const CATEGORY: &'static str;
13    /// Type name within the category (e.g., `"SimpleGraph"`, `"i64"`).
14    const VALUE: &'static str;
15}
16
17/// K-value marker trait for types that represent a const-generic K parameter.
18///
19/// Types implementing this trait declare an optional K value. `None` means
20/// the type represents an arbitrary K (like KN), while `Some(k)` means
21/// a specific value (like K2, K3).
22pub trait KValue: VariantParam + Clone + 'static {
23    /// The K value, or `None` for arbitrary K.
24    const K: Option<usize>;
25}
26
27/// Implement `VariantParam` and optionally `KValue` for a type.
28///
29/// # Usage
30///
31/// ```text
32/// // Variant parameter:
33/// impl_variant_param!(SimpleGraph, "graph");
34///
35/// // Generic K value:
36/// impl_variant_param!(KN, "k", k: None);
37///
38/// // Concrete K value:
39/// impl_variant_param!(K3, "k", k: Some(3));
40/// ```
41#[macro_export]
42macro_rules! impl_variant_param {
43    ($ty:ty, $cat:expr) => {
44        impl $crate::variant::VariantParam for $ty {
45            const CATEGORY: &'static str = $cat;
46            const VALUE: &'static str = stringify!($ty);
47        }
48    };
49    ($ty:ty, $cat:expr, k: $k:expr) => {
50        $crate::impl_variant_param!($ty, $cat);
51        impl $crate::variant::KValue for $ty {
52            const K: Option<usize> = $k;
53        }
54    };
55}
56
57/// Compose a `Problem::variant()` body from type parameter names.
58///
59/// All variant dimensions must be types implementing `VariantParam`.
60///
61/// # Usage
62///
63/// ```text
64/// variant_params![]           // -> vec![]
65/// variant_params![G, W]       // -> vec![(G::CATEGORY, G::VALUE), ...]
66/// ```
67#[macro_export]
68macro_rules! variant_params {
69    () => { vec![] };
70    ($($T:ident),+) => {
71        vec![$((<$T as $crate::variant::VariantParam>::CATEGORY,
72              <$T as $crate::variant::VariantParam>::VALUE)),+]
73    };
74}
75
76// --- Concrete KValue types ---
77
78/// K=1 (e.g., 1-coloring).
79#[derive(Clone, Copy, Debug, Default)]
80pub struct K1;
81
82/// K=2 (e.g., 2-SAT, 2-coloring).
83#[derive(Clone, Copy, Debug, Default)]
84pub struct K2;
85
86/// K=3 (e.g., 3-SAT, 3-coloring).
87#[derive(Clone, Copy, Debug, Default)]
88pub struct K3;
89
90/// K=4 (e.g., 4-coloring).
91#[derive(Clone, Copy, Debug, Default)]
92pub struct K4;
93
94/// K=5 (e.g., 5-coloring).
95#[derive(Clone, Copy, Debug, Default)]
96pub struct K5;
97
98/// Generic K (any value). Used for reductions that apply to all K.
99#[derive(Clone, Copy, Debug, Default)]
100pub struct KN;
101
102impl_variant_param!(KN, "k", k: None);
103impl_variant_param!(K5, "k", k: Some(5));
104impl_variant_param!(K4, "k", k: Some(4));
105impl_variant_param!(K3, "k", k: Some(3));
106impl_variant_param!(K2, "k", k: Some(2));
107impl_variant_param!(K1, "k", k: Some(1));
108
109// --- VariantSpec: canonical runtime representation of a problem variant ---
110
111use std::collections::BTreeMap;
112
113/// Canonical runtime representation of a problem variant.
114///
115/// Unlike raw `BTreeMap<String, String>`, construction from pairs rejects
116/// duplicate dimensions.
117#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
118pub struct VariantSpec {
119    dims: BTreeMap<String, String>,
120}
121
122impl VariantSpec {
123    /// Create a `VariantSpec` from key-value pairs, rejecting duplicate dimensions.
124    ///
125    /// Returns an error if the same dimension key appears more than once.
126    pub fn try_from_pairs<I, K, V>(
127        pairs: I,
128    ) -> std::result::Result<Self, crate::registry::ConstructionError>
129    where
130        I: IntoIterator<Item = (K, V)>,
131        K: Into<String>,
132        V: Into<String>,
133    {
134        let mut dims = BTreeMap::new();
135        for (k, v) in pairs {
136            let key = k.into();
137            let val = v.into();
138            if dims.insert(key.clone(), val).is_some() {
139                return Err(format!("duplicate dimension: {}", key).into());
140            }
141        }
142        Ok(Self { dims })
143    }
144
145    /// Create a `VariantSpec` from an existing `BTreeMap`.
146    pub fn try_from_map(
147        map: BTreeMap<String, String>,
148    ) -> std::result::Result<Self, crate::registry::ConstructionError> {
149        Ok(Self { dims: map })
150    }
151
152    /// View the dimensions as a map.
153    pub fn as_map(&self) -> &BTreeMap<String, String> {
154        &self.dims
155    }
156
157    /// Consume this `VariantSpec` and return the underlying map.
158    pub fn into_map(self) -> BTreeMap<String, String> {
159        self.dims
160    }
161
162    /// Update or add a single dimension.
163    pub fn update_dimension(&mut self, key: impl Into<String>, value: impl Into<String>) {
164        self.dims.insert(key.into(), value.into());
165    }
166}
167
168#[cfg(test)]
169#[path = "unit_tests/variant.rs"]
170mod tests;