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 category: crate::registry::ProblemCategory::Set,
19 module_path: module_path!(),
20 description: "Find a perfect matching in a tripartite hypergraph",
21 fields: &[
22 FieldInfo { name: "universe_size", type_name: "usize", description: "Size of each set W, X, Y (q)" },
23 FieldInfo { name: "triples", type_name: "Vec<(usize, usize, usize)>", description: "Set M of triples (w, x, y)" },
24 ],
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(try_from = "ThreeDimensionalMatchingData")]
60pub struct ThreeDimensionalMatching {
61 universe_size: usize,
63 triples: Vec<(usize, usize, usize)>,
65}
66
67#[derive(Deserialize)]
68struct ThreeDimensionalMatchingData {
69 universe_size: usize,
70 triples: Vec<(usize, usize, usize)>,
71}
72
73impl TryFrom<ThreeDimensionalMatchingData> for ThreeDimensionalMatching {
74 type Error = crate::registry::ConstructionError;
75
76 fn try_from(data: ThreeDimensionalMatchingData) -> Result<Self, Self::Error> {
77 Self::try_new(data.universe_size, data.triples)
78 }
79}
80
81impl ThreeDimensionalMatching {
82 pub fn new(universe_size: usize, triples: Vec<(usize, usize, usize)>) -> Self {
88 Self::try_new(universe_size, triples).unwrap_or_else(|error| panic!("{error}"))
89 }
90
91 fn try_new(
92 universe_size: usize,
93 triples: Vec<(usize, usize, usize)>,
94 ) -> Result<Self, crate::registry::ConstructionError> {
95 for (index, &(w, x, y)) in triples.iter().enumerate() {
96 if w >= universe_size || x >= universe_size || y >= universe_size {
97 return Err(format!(
98 "triple {index} contains a coordinate outside 0..{universe_size}"
99 )
100 .into());
101 }
102 }
103 Ok(Self {
104 universe_size,
105 triples,
106 })
107 }
108
109 pub fn universe_size(&self) -> usize {
111 self.universe_size
112 }
113
114 pub fn num_triples(&self) -> usize {
116 self.triples.len()
117 }
118
119 pub fn triples(&self) -> &[(usize, usize, usize)] {
121 &self.triples
122 }
123
124 pub fn get_triple(&self, index: usize) -> Option<&(usize, usize, usize)> {
126 self.triples.get(index)
127 }
128}
129
130impl Problem for ThreeDimensionalMatching {
131 const NAME: &'static str = "ThreeDimensionalMatching";
132 type Solution = Vec<bool>;
133 type Value = crate::types::Or;
134
135 crate::problem_parameters![
136 ("num_triples", num_triples),
137 ("universe_size", universe_size),
138 ];
139
140 fn evaluate(
141 &self,
142 config: &Self::Solution,
143 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
144 Ok({
145 crate::types::Or({
146 if config.len() != self.triples.len() {
147 return Err(crate::traits::EvaluationError::InvalidConfiguration(
148 "triple-selection length does not match the instance".into(),
149 ));
150 }
151
152 let selected_count = config.iter().filter(|&&v| v).count();
154 if selected_count != self.universe_size {
155 return Ok(crate::types::Or(false));
156 }
157
158 let mut used_w = HashSet::with_capacity(self.universe_size);
160 let mut used_x = HashSet::with_capacity(self.universe_size);
161 let mut used_y = HashSet::with_capacity(self.universe_size);
162
163 for (i, &selected) in config.iter().enumerate() {
164 if selected {
165 let (w, x, y) = self.triples[i];
166 if !used_w.insert(w) {
167 return Ok(crate::types::Or(false));
168 }
169 if !used_x.insert(x) {
170 return Ok(crate::types::Or(false));
171 }
172 if !used_y.insert(y) {
173 return Ok(crate::types::Or(false));
174 }
175 }
176 }
177
178 true
179 })
180 })
181 }
182
183 fn variant() -> Vec<(&'static str, &'static str)> {
184 crate::variant_params![]
185 }
186}
187
188impl crate::solvers::BruteForceProblem for ThreeDimensionalMatching {
189 fn dimensions(&self) -> Vec<usize> {
190 vec![2; self.triples.len()]
191 }
192}
193
194crate::declare_variants! {
195 default ThreeDimensionalMatching => "2^num_triples",
196}
197
198crate::register_brute_force! {
199 ThreeDimensionalMatching decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
200}
201
202#[cfg(feature = "example-db")]
203pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
204 vec![crate::example_db::specs::ModelExampleSpec {
205 id: "three_dimensional_matching",
206 instance: Box::new(ThreeDimensionalMatching::new(
207 3,
208 vec![(0, 1, 2), (1, 0, 1), (2, 2, 0), (0, 0, 0), (1, 2, 2)],
209 )),
210 optimal_config: serde_json::json!(vec![true, true, true, false, false]),
211 optimal_value: serde_json::json!(true),
212 }]
213}
214
215#[cfg(test)]
216#[path = "../../unit_tests/models/set/three_dimensional_matching.rs"]
217mod tests;