Skip to main content

problemreductions/models/set/
three_matroid_intersection.rs

1//! Three-Matroid Intersection problem implementation.
2//!
3//! Given three partition matroids on a common ground set E and a positive integer K,
4//! determine whether there exists a common independent set of size K.
5
6use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::traits::Problem;
8use serde::{Deserialize, Serialize};
9
10inventory::submit! {
11    ProblemSchemaEntry {
12        name: "ThreeMatroidIntersection",
13        display_name: "Three-Matroid Intersection",
14        aliases: &[],
15        dimensions: &[],
16        module_path: module_path!(),
17        description: "Find a common independent set of size K in three partition matroids",
18        fields: &[
19            FieldInfo { name: "ground_set_size", type_name: "usize", description: "Number of elements in the ground set E" },
20            FieldInfo { name: "partitions", type_name: "Vec<Vec<Vec<usize>>>", description: "Three partition matroids, each as a list of groups" },
21            FieldInfo { name: "bound", type_name: "usize", description: "Required size K of the common independent set" },
22        ],
23    }
24}
25
26/// Three-Matroid Intersection problem.
27///
28/// Given three partition matroids on a common ground set E = {0, ..., n-1} and a
29/// positive integer K ≤ |E|, determine whether there exists a subset E' ⊆ E such
30/// that |E'| = K and E' is independent in all three matroids.
31///
32/// A partition matroid is defined by a partition of E into groups. A set S is
33/// independent if |S ∩ G| ≤ 1 for every group G.
34///
35/// While 2-matroid intersection is solvable in polynomial time (Edmonds, 1970),
36/// the jump to three matroids captures NP-hardness.
37///
38/// # Example
39///
40/// ```
41/// use problemreductions::models::set::ThreeMatroidIntersection;
42/// use problemreductions::{Problem, Solver, BruteForce};
43///
44/// // Ground set E = {0, 1, 2, 3, 4, 5}, K = 2
45/// let problem = ThreeMatroidIntersection::new(
46///     6,
47///     vec![
48///         vec![vec![0, 1, 2], vec![3, 4, 5]],       // M1
49///         vec![vec![0, 3], vec![1, 4], vec![2, 5]],  // M2
50///         vec![vec![0, 4], vec![1, 5], vec![2, 3]],  // M3
51///     ],
52///     2,
53/// );
54///
55/// let solver = BruteForce::new();
56/// let solutions = solver.find_all_witnesses(&problem);
57/// assert!(!solutions.is_empty());
58/// ```
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct ThreeMatroidIntersection {
61    /// Number of elements in the ground set E (elements are 0..ground_set_size).
62    ground_set_size: usize,
63    /// Three partition matroids. Each matroid is a list of groups, where each
64    /// group is a list of element indices. A set is independent in a partition
65    /// matroid if it contains at most one element from each group.
66    partitions: Vec<Vec<Vec<usize>>>,
67    /// Required size K of the common independent set.
68    bound: usize,
69}
70
71impl ThreeMatroidIntersection {
72    /// Create a new Three-Matroid Intersection problem.
73    ///
74    /// # Panics
75    ///
76    /// Panics if:
77    /// - `partitions` does not contain exactly 3 matroids
78    /// - Any element index is outside `0..ground_set_size`
79    /// - `bound` exceeds `ground_set_size`
80    pub fn new(ground_set_size: usize, partitions: Vec<Vec<Vec<usize>>>, bound: usize) -> Self {
81        assert_eq!(
82            partitions.len(),
83            3,
84            "Expected exactly 3 partition matroids, got {}",
85            partitions.len()
86        );
87        assert!(
88            bound <= ground_set_size,
89            "Bound {} exceeds ground set size {}",
90            bound,
91            ground_set_size
92        );
93        for (m_idx, matroid) in partitions.iter().enumerate() {
94            for (g_idx, group) in matroid.iter().enumerate() {
95                for &elem in group {
96                    assert!(
97                        elem < ground_set_size,
98                        "Matroid {} group {} contains element {} which is outside 0..{}",
99                        m_idx,
100                        g_idx,
101                        elem,
102                        ground_set_size
103                    );
104                }
105            }
106        }
107        Self {
108            ground_set_size,
109            partitions,
110            bound,
111        }
112    }
113
114    /// Get the ground set size.
115    pub fn ground_set_size(&self) -> usize {
116        self.ground_set_size
117    }
118
119    /// Get the three partition matroids.
120    pub fn partitions(&self) -> &[Vec<Vec<usize>>] {
121        &self.partitions
122    }
123
124    /// Get the bound K.
125    pub fn bound(&self) -> usize {
126        self.bound
127    }
128
129    /// Get the total number of groups across all three matroids.
130    pub fn num_groups(&self) -> usize {
131        self.partitions.iter().map(|m| m.len()).sum()
132    }
133}
134
135impl Problem for ThreeMatroidIntersection {
136    const NAME: &'static str = "ThreeMatroidIntersection";
137    type Value = crate::types::Or;
138
139    fn dims(&self) -> Vec<usize> {
140        vec![2; self.ground_set_size]
141    }
142
143    fn evaluate(&self, config: &[usize]) -> crate::types::Or {
144        crate::types::Or({
145            if config.len() != self.ground_set_size || config.iter().any(|&v| v > 1) {
146                return crate::types::Or(false);
147            }
148
149            // Check selected set has exactly K elements
150            let selected_count: usize = config.iter().filter(|&&v| v == 1).sum();
151            if selected_count != self.bound {
152                return crate::types::Or(false);
153            }
154
155            // Check independence in each of the three partition matroids
156            for matroid in &self.partitions {
157                for group in matroid {
158                    let count = group.iter().filter(|&&e| config[e] == 1).count();
159                    if count > 1 {
160                        return crate::types::Or(false);
161                    }
162                }
163            }
164
165            true
166        })
167    }
168
169    fn variant() -> Vec<(&'static str, &'static str)> {
170        crate::variant_params![]
171    }
172}
173
174crate::declare_variants! {
175    default ThreeMatroidIntersection => "2^ground_set_size",
176}
177
178#[cfg(feature = "example-db")]
179pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
180    vec![crate::example_db::specs::ModelExampleSpec {
181        id: "three_matroid_intersection",
182        instance: Box::new(ThreeMatroidIntersection::new(
183            6,
184            vec![
185                vec![vec![0, 1, 2], vec![3, 4, 5]],
186                vec![vec![0, 3], vec![1, 4], vec![2, 5]],
187                vec![vec![0, 4], vec![1, 5], vec![2, 3]],
188            ],
189            2,
190        )),
191        optimal_config: vec![1, 0, 0, 0, 0, 1],
192        optimal_value: serde_json::json!(true),
193    }]
194}
195
196#[cfg(test)]
197#[path = "../../unit_tests/models/set/three_matroid_intersection.rs"]
198mod tests;