Skip to main content

problemreductions/models/graph/
rooted_tree_arrangement.rs

1//! Rooted Tree Arrangement problem implementation.
2//!
3//! The Rooted Tree Arrangement problem asks whether a graph can be embedded
4//! into the nodes of a rooted tree so that every graph edge lies on a single
5//! root-to-leaf path and the total tree stretch is bounded.
6
7use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
8use crate::topology::{Graph, SimpleGraph};
9use crate::traits::Problem;
10use crate::variant::VariantParam;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14    ProblemSchemaEntry {
15        name: "RootedTreeArrangement",
16        display_name: "Rooted Tree Arrangement",
17        aliases: &[],
18        dimensions: &[
19            VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20        ],
21        module_path: module_path!(),
22        description: "Find a rooted-tree embedding of a graph with bounded total edge stretch",
23        fields: &[
24            FieldInfo { name: "graph", type_name: "G", description: "The undirected graph G=(V,E)" },
25            FieldInfo { name: "bound", type_name: "usize", description: "Upper bound K on total tree stretch" },
26        ],
27    }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
32pub struct RootedTreeArrangement<G> {
33    graph: G,
34    bound: usize,
35}
36
37#[derive(Debug, Clone)]
38struct TreeInfo {
39    depth: Vec<usize>,
40}
41
42impl<G: Graph> RootedTreeArrangement<G> {
43    pub fn new(graph: G, bound: usize) -> Self {
44        Self { graph, bound }
45    }
46
47    pub fn graph(&self) -> &G {
48        &self.graph
49    }
50
51    pub fn bound(&self) -> usize {
52        self.bound
53    }
54
55    pub fn num_vertices(&self) -> usize {
56        self.graph.num_vertices()
57    }
58
59    pub fn num_edges(&self) -> usize {
60        self.graph.num_edges()
61    }
62
63    pub fn is_valid_solution(&self, config: &[usize]) -> bool {
64        matches!(self.total_edge_stretch(config), Some(stretch) if stretch <= self.bound)
65    }
66
67    pub fn total_edge_stretch(&self, config: &[usize]) -> Option<usize> {
68        let n = self.graph.num_vertices();
69        if n == 0 {
70            return config.is_empty().then_some(0);
71        }
72
73        let (parent, mapping) = self.split_config(config)?;
74        let tree = analyze_parent_array(parent)?;
75        if !is_valid_permutation(mapping) {
76            return None;
77        }
78
79        let mut total = 0usize;
80        for (u, v) in self.graph.edges() {
81            let tree_u = mapping[u];
82            let tree_v = mapping[v];
83            if !are_ancestor_comparable(parent, tree_u, tree_v) {
84                return None;
85            }
86            total += tree.depth[tree_u].abs_diff(tree.depth[tree_v]);
87        }
88
89        Some(total)
90    }
91
92    fn split_config<'a>(&self, config: &'a [usize]) -> Option<(&'a [usize], &'a [usize])> {
93        let n = self.graph.num_vertices();
94        (config.len() == 2 * n).then(|| config.split_at(n))
95    }
96}
97
98impl<G> Problem for RootedTreeArrangement<G>
99where
100    G: Graph + VariantParam,
101{
102    const NAME: &'static str = "RootedTreeArrangement";
103    type Value = crate::types::Or;
104
105    fn variant() -> Vec<(&'static str, &'static str)> {
106        crate::variant_params![G]
107    }
108
109    fn dims(&self) -> Vec<usize> {
110        let n = self.graph.num_vertices();
111        vec![n; 2 * n]
112    }
113
114    fn evaluate(&self, config: &[usize]) -> crate::types::Or {
115        crate::types::Or(self.is_valid_solution(config))
116    }
117}
118
119fn analyze_parent_array(parent: &[usize]) -> Option<TreeInfo> {
120    let n = parent.len();
121    if n == 0 {
122        return Some(TreeInfo { depth: vec![] });
123    }
124
125    if parent.iter().any(|&p| p >= n) {
126        return None;
127    }
128
129    let roots = parent
130        .iter()
131        .enumerate()
132        .filter_map(|(node, &p)| (node == p).then_some(node))
133        .collect::<Vec<_>>();
134    if roots.len() != 1 {
135        return None;
136    }
137    let root = roots[0];
138
139    let mut state = vec![0u8; n];
140    let mut depth = vec![0usize; n];
141
142    fn visit(
143        node: usize,
144        root: usize,
145        parent: &[usize],
146        state: &mut [u8],
147        depth: &mut [usize],
148    ) -> Option<usize> {
149        match state[node] {
150            1 => return None,
151            2 => return Some(depth[node]),
152            _ => {}
153        }
154
155        state[node] = 1;
156        let d = if node == root {
157            0
158        } else {
159            let next = parent[node];
160            if next == node {
161                return None;
162            }
163            visit(next, root, parent, state, depth)? + 1
164        };
165        depth[node] = d;
166        state[node] = 2;
167        Some(d)
168    }
169
170    for node in 0..n {
171        visit(node, root, parent, &mut state, &mut depth)?;
172    }
173
174    Some(TreeInfo { depth })
175}
176
177fn is_valid_permutation(mapping: &[usize]) -> bool {
178    let n = mapping.len();
179    let mut seen = vec![false; n];
180    for &image in mapping {
181        if image >= n || seen[image] {
182            return false;
183        }
184        seen[image] = true;
185    }
186    true
187}
188
189fn is_ancestor(parent: &[usize], ancestor: usize, descendant: usize) -> bool {
190    let mut current = descendant;
191    loop {
192        if current == ancestor {
193            return true;
194        }
195        let next = parent[current];
196        if next == current {
197            return false;
198        }
199        current = next;
200    }
201}
202
203fn are_ancestor_comparable(parent: &[usize], u: usize, v: usize) -> bool {
204    is_ancestor(parent, u, v) || is_ancestor(parent, v, u)
205}
206
207crate::declare_variants! {
208    default RootedTreeArrangement<SimpleGraph> => "2^num_vertices",
209}
210
211#[cfg(feature = "example-db")]
212pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
213    vec![crate::example_db::specs::ModelExampleSpec {
214        id: "rooted_tree_arrangement_simplegraph",
215        instance: Box::new(RootedTreeArrangement::new(
216            SimpleGraph::new(4, vec![(0, 1), (0, 2), (1, 2), (2, 3)]),
217            5,
218        )),
219        optimal_config: vec![0, 0, 1, 2, 0, 1, 2, 3],
220        optimal_value: serde_json::json!(true),
221    }]
222}
223
224#[cfg(test)]
225#[path = "../../unit_tests/models/graph/rooted_tree_arrangement.rs"]
226mod tests;