problemreductions/models/set/
three_dimensional_matching.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10use std::collections::HashSet;
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "ThreeDimensionalMatching",
15 display_name: "Three-Dimensional Matching",
16 aliases: &["3DM"],
17 dimensions: &[],
18 module_path: module_path!(),
19 description: "Find a perfect matching in a tripartite hypergraph",
20 fields: &[
21 FieldInfo { name: "universe_size", type_name: "usize", description: "Size of each set W, X, Y (q)" },
22 FieldInfo { name: "triples", type_name: "Vec<(usize, usize, usize)>", description: "Set M of triples (w, x, y)" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct ThreeDimensionalMatching {
59 universe_size: usize,
61 triples: Vec<(usize, usize, usize)>,
63}
64
65impl ThreeDimensionalMatching {
66 pub fn new(universe_size: usize, triples: Vec<(usize, usize, usize)>) -> Self {
72 for (i, &(w, x, y)) in triples.iter().enumerate() {
73 assert!(
74 w < universe_size,
75 "Triple {} has w-coordinate {} which is outside 0..{}",
76 i,
77 w,
78 universe_size
79 );
80 assert!(
81 x < universe_size,
82 "Triple {} has x-coordinate {} which is outside 0..{}",
83 i,
84 x,
85 universe_size
86 );
87 assert!(
88 y < universe_size,
89 "Triple {} has y-coordinate {} which is outside 0..{}",
90 i,
91 y,
92 universe_size
93 );
94 }
95 Self {
96 universe_size,
97 triples,
98 }
99 }
100
101 pub fn universe_size(&self) -> usize {
103 self.universe_size
104 }
105
106 pub fn num_triples(&self) -> usize {
108 self.triples.len()
109 }
110
111 pub fn triples(&self) -> &[(usize, usize, usize)] {
113 &self.triples
114 }
115
116 pub fn get_triple(&self, index: usize) -> Option<&(usize, usize, usize)> {
118 self.triples.get(index)
119 }
120}
121
122impl Problem for ThreeDimensionalMatching {
123 const NAME: &'static str = "ThreeDimensionalMatching";
124 type Value = crate::types::Or;
125
126 fn dims(&self) -> Vec<usize> {
127 vec![2; self.triples.len()]
128 }
129
130 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
131 crate::types::Or({
132 if config.len() != self.triples.len() || config.iter().any(|&value| value > 1) {
133 return crate::types::Or(false);
134 }
135
136 let selected_count: usize = config.iter().filter(|&&v| v == 1).sum();
138 if selected_count != self.universe_size {
139 return crate::types::Or(false);
140 }
141
142 let mut used_w = HashSet::with_capacity(self.universe_size);
144 let mut used_x = HashSet::with_capacity(self.universe_size);
145 let mut used_y = HashSet::with_capacity(self.universe_size);
146
147 for (i, &selected) in config.iter().enumerate() {
148 if selected == 1 {
149 let (w, x, y) = self.triples[i];
150 if !used_w.insert(w) {
151 return crate::types::Or(false);
152 }
153 if !used_x.insert(x) {
154 return crate::types::Or(false);
155 }
156 if !used_y.insert(y) {
157 return crate::types::Or(false);
158 }
159 }
160 }
161
162 true
163 })
164 }
165
166 fn variant() -> Vec<(&'static str, &'static str)> {
167 crate::variant_params![]
168 }
169}
170
171crate::declare_variants! {
172 default ThreeDimensionalMatching => "2^num_triples",
173}
174
175#[cfg(feature = "example-db")]
176pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
177 vec![crate::example_db::specs::ModelExampleSpec {
178 id: "three_dimensional_matching",
179 instance: Box::new(ThreeDimensionalMatching::new(
180 3,
181 vec![(0, 1, 2), (1, 0, 1), (2, 2, 0), (0, 0, 0), (1, 2, 2)],
182 )),
183 optimal_config: vec![1, 1, 1, 0, 0],
184 optimal_value: serde_json::json!(true),
185 }]
186}
187
188#[cfg(test)]
189#[path = "../../unit_tests/models/set/three_dimensional_matching.rs"]
190mod tests;