problemreductions/models/misc/
closest_string.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry};
9use crate::traits::Problem;
10use crate::types::Min;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "ClosestString",
16 display_name: "Closest String",
17 aliases: &[],
18 dimensions: &[],
19 module_path: module_path!(),
20 description: "Find a center string of fixed length that minimizes the maximum Hamming distance to a list of equal-length input strings",
21 fields: &[
22 FieldInfo {
23 name: "alphabet_size",
24 type_name: "usize",
25 description: "Size q of the finite alphabet {0, ..., q-1}",
26 },
27 FieldInfo {
28 name: "strings",
29 type_name: "Vec<Vec<usize>>",
30 description: "Input strings s_1, ..., s_n over the alphabet, all of equal length m",
31 },
32 ],
33 }
34}
35
36inventory::submit! {
37 ProblemSizeFieldEntry {
38 name: "ClosestString",
39 fields: &["alphabet_size", "num_strings", "string_length", "total_length"],
40 }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct ClosestString {
56 alphabet_size: usize,
57 strings: Vec<Vec<usize>>,
58}
59
60impl ClosestString {
61 pub fn new(alphabet_size: usize, strings: Vec<Vec<usize>>) -> Self {
71 assert!(
72 !strings.is_empty(),
73 "ClosestString requires at least one input string"
74 );
75 let string_length = strings[0].len();
76 assert!(
77 strings.iter().all(|s| s.len() == string_length),
78 "all input strings must have the same length"
79 );
80 assert!(
81 alphabet_size > 0 || string_length == 0,
82 "alphabet_size must be > 0 when input strings are non-empty"
83 );
84 assert!(
85 strings
86 .iter()
87 .flat_map(|s| s.iter())
88 .all(|&symbol| symbol < alphabet_size),
89 "input symbols must be less than alphabet_size"
90 );
91 Self {
92 alphabet_size,
93 strings,
94 }
95 }
96
97 pub fn alphabet_size(&self) -> usize {
99 self.alphabet_size
100 }
101
102 pub fn strings(&self) -> &[Vec<usize>] {
104 &self.strings
105 }
106
107 pub fn num_strings(&self) -> usize {
109 self.strings.len()
110 }
111
112 pub fn string_length(&self) -> usize {
114 self.strings[0].len()
115 }
116
117 pub fn total_length(&self) -> usize {
119 self.num_strings() * self.string_length()
120 }
121}
122
123impl Problem for ClosestString {
124 const NAME: &'static str = "ClosestString";
125 type Value = Min<i64>;
126
127 fn variant() -> Vec<(&'static str, &'static str)> {
128 crate::variant_params![]
129 }
130
131 fn dims(&self) -> Vec<usize> {
132 vec![self.alphabet_size; self.string_length()]
133 }
134
135 fn evaluate(&self, config: &[usize]) -> Min<i64> {
136 let m = self.string_length();
137 if config.len() != m {
138 return Min(None);
139 }
140 if config.iter().any(|&symbol| symbol >= self.alphabet_size) {
141 return Min(None);
142 }
143 let max_distance = self
145 .strings
146 .iter()
147 .map(|s| config.iter().zip(s.iter()).filter(|(c, t)| c != t).count() as i64)
148 .max()
149 .unwrap_or(0);
150 Min(Some(max_distance))
151 }
152}
153
154crate::declare_variants! {
155 default ClosestString => "alphabet_size ^ string_length",
156}
157
158#[cfg(feature = "example-db")]
159pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
160 vec![crate::example_db::specs::ModelExampleSpec {
161 id: "closest_string",
162 instance: Box::new(ClosestString::new(
163 2,
164 vec![vec![0, 0, 0], vec![0, 1, 1], vec![1, 0, 1], vec![1, 1, 0]],
165 )),
166 optimal_config: vec![0, 0, 0],
167 optimal_value: serde_json::json!(2),
168 }]
169}
170
171#[cfg(test)]
172#[path = "../../unit_tests/models/misc/closest_string.rs"]
173mod tests;