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;