problemreductions/models/misc/
betweenness.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
9use crate::traits::Problem;
10use crate::types::Or;
11use serde::de::Error as _;
12use serde::{Deserialize, Deserializer, Serialize};
13
14inventory::submit! {
15 ProblemSchemaEntry {
16 name: "Betweenness",
17 display_name: "Betweenness",
18 aliases: &[],
19 dimensions: &[],
20 category: crate::registry::ProblemCategory::Misc,
21 module_path: module_path!(),
22 description: "Find a linear ordering where specified elements are between others",
23 fields: &[
24 FieldInfo { name: "num_elements", type_name: "usize", description: "Number of elements in the set A" },
25 FieldInfo { name: "triples", type_name: "Vec<(usize, usize, usize)>", description: "Collection of ordered triples (a, b, c) requiring b between a and c" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize)]
31pub struct Betweenness {
32 num_elements: usize,
33 triples: Vec<(usize, usize, usize)>,
34}
35
36impl Betweenness {
37 fn validate_inputs(
38 num_elements: usize,
39 triples: &[(usize, usize, usize)],
40 ) -> Result<(), crate::registry::ConstructionError> {
41 if num_elements == 0 {
42 return Err("Betweenness requires at least one element"
43 .to_string()
44 .into());
45 }
46 for (i, &(a, b, c)) in triples.iter().enumerate() {
47 if a >= num_elements || b >= num_elements || c >= num_elements {
48 return Err(format!(
49 "Triple {} has element(s) out of range 0..{}",
50 i, num_elements
51 )
52 .into());
53 }
54 if a == b || b == c || a == c {
55 return Err(
56 format!("Triple {} has duplicate elements ({}, {}, {})", i, a, b, c).into(),
57 );
58 }
59 }
60 Ok(())
61 }
62
63 pub fn try_new(
64 num_elements: usize,
65 triples: Vec<(usize, usize, usize)>,
66 ) -> Result<Self, crate::registry::ConstructionError> {
67 Self::validate_inputs(num_elements, &triples)?;
68 Ok(Self {
69 num_elements,
70 triples,
71 })
72 }
73
74 pub fn new(num_elements: usize, triples: Vec<(usize, usize, usize)>) -> Self {
80 Self::try_new(num_elements, triples).unwrap_or_else(|message| panic!("{message}"))
81 }
82
83 pub fn num_elements(&self) -> usize {
85 self.num_elements
86 }
87
88 pub fn num_triples(&self) -> usize {
90 self.triples.len()
91 }
92
93 pub fn triples(&self) -> &[(usize, usize, usize)] {
95 &self.triples
96 }
97
98 fn is_valid_solution(&self, config: &[usize]) -> bool {
101 if config.len() != self.num_elements {
102 return false;
103 }
104
105 let n = self.num_elements;
107 let mut seen = vec![false; n];
108 for &pos in config {
109 if pos >= n || seen[pos] {
110 return false;
111 }
112 seen[pos] = true;
113 }
114
115 for &(a, b, c) in &self.triples {
118 let fa = config[a];
119 let fb = config[b];
120 let fc = config[c];
121 if !((fa < fb && fb < fc) || (fc < fb && fb < fa)) {
122 return false;
123 }
124 }
125
126 true
127 }
128}
129
130#[derive(Deserialize)]
131struct BetweennessData {
132 num_elements: usize,
133 triples: Vec<(usize, usize, usize)>,
134}
135
136impl<'de> Deserialize<'de> for Betweenness {
137 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138 where
139 D: Deserializer<'de>,
140 {
141 let data = BetweennessData::deserialize(deserializer)?;
142 Self::try_new(data.num_elements, data.triples).map_err(D::Error::custom)
143 }
144}
145
146impl Problem for Betweenness {
147 const NAME: &'static str = "Betweenness";
148 type Solution = Vec<usize>;
149 type Value = Or;
150
151 crate::problem_parameters![("num_elements", num_elements), ("num_triples", num_triples),];
152
153 fn variant() -> Vec<(&'static str, &'static str)> {
154 crate::variant_params![]
155 }
156
157 fn evaluate(&self, config: &Self::Solution) -> Result<Or, crate::traits::EvaluationError> {
158 if config.len() != self.num_elements {
159 return Err(crate::traits::EvaluationError::InvalidConfiguration(
160 "ordering length does not match the elements".into(),
161 ));
162 }
163 if config.iter().any(|&position| position >= self.num_elements) {
164 return Err(crate::traits::EvaluationError::InvalidConfiguration(
165 "ordering contains an out-of-range position".into(),
166 ));
167 }
168 Ok(Or(self.is_valid_solution(config)))
169 }
170}
171
172impl crate::solvers::BruteForceProblem for Betweenness {
173 fn dimensions(&self) -> Vec<usize> {
174 vec![self.num_elements; self.num_elements]
175 }
176}
177
178crate::declare_variants! {
179 default Betweenness => "2^num_elements",
180}
181
182crate::register_brute_force! {
183 Betweenness,
184}
185
186#[cfg(feature = "example-db")]
187pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
188 vec![crate::example_db::specs::ModelExampleSpec {
189 id: "betweenness",
190 instance: Box::new(Betweenness::new(
191 5,
192 vec![(0, 1, 2), (2, 3, 4), (0, 2, 4), (1, 3, 4)],
193 )),
194 optimal_config: serde_json::json!(vec![0, 1, 2, 3, 4]),
195 optimal_value: serde_json::json!(true),
196 }]
197}
198
199#[cfg(test)]
200#[path = "../../unit_tests/models/misc/betweenness.rs"]
201mod tests;