problemreductions/models/set/
rooted_tree_storage_assignment.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
4use crate::traits::Problem;
5use serde::{Deserialize, Serialize};
6use std::collections::HashSet;
7
8inventory::submit! {
9 ProblemSchemaEntry {
10 name: "RootedTreeStorageAssignment",
11 display_name: "Rooted Tree Storage Assignment",
12 aliases: &[],
13 dimensions: &[],
14 module_path: module_path!(),
15 description: "Does there exist a rooted tree whose subset path extensions cost at most K?",
16 fields: &[
17 FieldInfo { name: "universe_size", type_name: "usize", description: "Size of the ground set X" },
18 FieldInfo { name: "subsets", type_name: "Vec<Vec<usize>>", description: "Collection of subsets of X" },
19 FieldInfo { name: "bound", type_name: "usize", description: "Upper bound K on the total extension cost" },
20 ],
21 }
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(try_from = "RootedTreeStorageAssignmentDef")]
26pub struct RootedTreeStorageAssignment {
27 universe_size: usize,
28 subsets: Vec<Vec<usize>>,
29 bound: usize,
30}
31
32#[derive(Debug, Deserialize)]
33struct RootedTreeStorageAssignmentDef {
34 universe_size: usize,
35 subsets: Vec<Vec<usize>>,
36 bound: usize,
37}
38
39impl RootedTreeStorageAssignment {
40 pub fn new(universe_size: usize, subsets: Vec<Vec<usize>>, bound: usize) -> Self {
41 Self::try_new(universe_size, subsets, bound).unwrap_or_else(|err| panic!("{err}"))
42 }
43
44 pub fn try_new(
45 universe_size: usize,
46 subsets: Vec<Vec<usize>>,
47 bound: usize,
48 ) -> Result<Self, String> {
49 let subsets = subsets
50 .into_iter()
51 .enumerate()
52 .map(|(subset_index, mut subset)| {
53 let mut seen = HashSet::with_capacity(subset.len());
54 for &element in &subset {
55 if element >= universe_size {
56 return Err(format!(
57 "subset {subset_index} contains element {element} outside universe of size {universe_size}"
58 ));
59 }
60 if !seen.insert(element) {
61 return Err(format!(
62 "subset {subset_index} contains duplicate element {element}"
63 ));
64 }
65 }
66 subset.sort_unstable();
67 Ok(subset)
68 })
69 .collect::<Result<Vec<_>, _>>()?;
70
71 Ok(Self {
72 universe_size,
73 subsets,
74 bound,
75 })
76 }
77
78 pub fn universe_size(&self) -> usize {
79 self.universe_size
80 }
81
82 pub fn num_subsets(&self) -> usize {
83 self.subsets.len()
84 }
85
86 pub fn subsets(&self) -> &[Vec<usize>] {
87 &self.subsets
88 }
89
90 pub fn bound(&self) -> usize {
91 self.bound
92 }
93
94 fn analyze_tree(config: &[usize]) -> Option<Vec<usize>> {
95 let roots = config
96 .iter()
97 .enumerate()
98 .filter(|(vertex, parent)| *vertex == **parent)
99 .count();
100 if roots != 1 {
101 return None;
102 }
103
104 let n = config.len();
105 let mut state = vec![0u8; n];
106 let mut depth = vec![0usize; n];
107
108 fn visit(vertex: usize, config: &[usize], state: &mut [u8], depth: &mut [usize]) -> bool {
109 match state[vertex] {
110 2 => return true,
111 1 => return false,
112 _ => {}
113 }
114
115 state[vertex] = 1;
116 let parent = config[vertex];
117 if parent == vertex {
118 depth[vertex] = 0;
119 } else {
120 if !visit(parent, config, state, depth) {
121 return false;
122 }
123 depth[vertex] = depth[parent] + 1;
124 }
125 state[vertex] = 2;
126 true
127 }
128
129 for vertex in 0..n {
130 if !visit(vertex, config, &mut state, &mut depth) {
131 return None;
132 }
133 }
134
135 Some(depth)
136 }
137
138 fn is_ancestor(ancestor: usize, mut vertex: usize, config: &[usize], depth: &[usize]) -> bool {
139 if depth[ancestor] > depth[vertex] {
140 return false;
141 }
142
143 while depth[vertex] > depth[ancestor] {
144 vertex = config[vertex];
145 }
146
147 ancestor == vertex
148 }
149
150 fn subset_extension_cost(
151 &self,
152 subset: &[usize],
153 config: &[usize],
154 depth: &[usize],
155 ) -> Option<usize> {
156 if subset.len() <= 1 {
157 return Some(0);
158 }
159
160 let mut ordered = subset.to_vec();
161 ordered.sort_by_key(|&vertex| depth[vertex]);
162
163 for pair in ordered.windows(2) {
164 if !Self::is_ancestor(pair[0], pair[1], config, depth) {
165 return None;
166 }
167 }
168
169 let top = ordered[0];
170 let bottom = *ordered.last().unwrap();
171 Some(depth[bottom] - depth[top] + 1 - ordered.len())
172 }
173}
174
175impl Problem for RootedTreeStorageAssignment {
176 const NAME: &'static str = "RootedTreeStorageAssignment";
177 type Value = crate::types::Or;
178
179 fn dims(&self) -> Vec<usize> {
180 vec![self.universe_size; self.universe_size]
181 }
182
183 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
184 crate::types::Or({
185 if config.len() != self.universe_size {
186 return crate::types::Or(false);
187 }
188 if config.iter().any(|&parent| parent >= self.universe_size) {
189 return crate::types::Or(false);
190 }
191 if self.universe_size == 0 {
192 return crate::types::Or(self.subsets.is_empty());
193 }
194
195 let Some(depth) = Self::analyze_tree(config) else {
196 return crate::types::Or(false);
197 };
198
199 let mut total_cost = 0usize;
200 for subset in &self.subsets {
201 let Some(cost) = self.subset_extension_cost(subset, config, &depth) else {
202 return crate::types::Or(false);
203 };
204 total_cost += cost;
205 if total_cost > self.bound {
206 return crate::types::Or(false);
207 }
208 }
209
210 true
211 })
212 }
213
214 fn variant() -> Vec<(&'static str, &'static str)> {
215 crate::variant_params![]
216 }
217}
218
219crate::declare_variants! {
220 default RootedTreeStorageAssignment => "universe_size^universe_size",
221}
222
223#[cfg(feature = "example-db")]
224pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
225 vec![crate::example_db::specs::ModelExampleSpec {
226 id: "rooted_tree_storage_assignment",
227 instance: Box::new(RootedTreeStorageAssignment::new(
228 5,
229 vec![vec![0, 2], vec![1, 3], vec![0, 4], vec![2, 4]],
230 1,
231 )),
232 optimal_config: vec![0, 0, 0, 1, 2],
233 optimal_value: serde_json::json!(true),
234 }]
235}
236
237impl TryFrom<RootedTreeStorageAssignmentDef> for RootedTreeStorageAssignment {
238 type Error = String;
239
240 fn try_from(value: RootedTreeStorageAssignmentDef) -> Result<Self, Self::Error> {
241 Self::try_new(value.universe_size, value.subsets, value.bound)
242 }
243}
244
245#[cfg(test)]
246#[path = "../../unit_tests/models/set/rooted_tree_storage_assignment.rs"]
247mod tests;