problemreductions/models/graph/
optimal_linear_arrangement.rs1use crate::models::decision::Decision;
8use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
9use crate::topology::{Graph, SimpleGraph};
10use crate::traits::Problem;
11use crate::types::Min;
12use serde::{Deserialize, Serialize};
13
14inventory::submit! {
15 ProblemSchemaEntry {
16 name: "OptimalLinearArrangement",
17 display_name: "Optimal Linear Arrangement",
18 aliases: &["OLA"],
19 dimensions: &[
20 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
21 ],
22 module_path: module_path!(),
23 description: "Find a vertex ordering on a line minimizing total edge length",
24 fields: &[
25 FieldInfo { name: "graph", type_name: "G", description: "The undirected graph G=(V,E)" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
64#[serde(bound(deserialize = "G: serde::Deserialize<'de>"))]
65pub struct OptimalLinearArrangement<G> {
66 graph: G,
68}
69
70impl<G: Graph> OptimalLinearArrangement<G> {
71 pub fn new(graph: G) -> Self {
76 Self { graph }
77 }
78
79 pub fn graph(&self) -> &G {
81 &self.graph
82 }
83
84 pub fn num_vertices(&self) -> usize {
86 self.graph.num_vertices()
87 }
88
89 pub fn num_edges(&self) -> usize {
91 self.graph.num_edges()
92 }
93
94 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
96 self.is_valid_permutation(config)
97 }
98
99 fn is_valid_permutation(&self, config: &[usize]) -> bool {
101 let n = self.graph.num_vertices();
102 if config.len() != n {
103 return false;
104 }
105 let mut seen = vec![false; n];
106 for &pos in config {
107 if pos >= n || seen[pos] {
108 return false;
109 }
110 seen[pos] = true;
111 }
112 true
113 }
114
115 pub fn total_edge_length(&self, config: &[usize]) -> Option<usize> {
119 if !self.is_valid_permutation(config) {
120 return None;
121 }
122 let mut total = 0usize;
123 for (u, v) in self.graph.edges() {
124 let fu = config[u];
125 let fv = config[v];
126 total += fu.abs_diff(fv);
127 }
128 Some(total)
129 }
130}
131
132impl<G> Problem for OptimalLinearArrangement<G>
133where
134 G: Graph + crate::variant::VariantParam,
135{
136 const NAME: &'static str = "OptimalLinearArrangement";
137 type Value = Min<usize>;
138
139 fn variant() -> Vec<(&'static str, &'static str)> {
140 crate::variant_params![G]
141 }
142
143 fn dims(&self) -> Vec<usize> {
144 let n = self.graph.num_vertices();
145 vec![n; n]
146 }
147
148 fn evaluate(&self, config: &[usize]) -> Min<usize> {
149 match self.total_edge_length(config) {
150 Some(cost) => Min(Some(cost)),
151 None => Min(None),
152 }
153 }
154}
155
156crate::declare_variants! {
157 default OptimalLinearArrangement<SimpleGraph> => "2^num_vertices",
158}
159
160impl<G> crate::models::decision::DecisionProblemMeta for OptimalLinearArrangement<G>
161where
162 G: Graph + crate::variant::VariantParam,
163{
164 const DECISION_NAME: &'static str = "DecisionOptimalLinearArrangement";
165}
166
167impl Decision<OptimalLinearArrangement<SimpleGraph>> {
168 pub fn num_vertices(&self) -> usize {
170 self.inner().num_vertices()
171 }
172
173 pub fn num_edges(&self) -> usize {
175 self.inner().num_edges()
176 }
177
178 pub fn k(&self) -> usize {
180 *self.bound()
181 }
182}
183
184crate::register_decision_variant!(
185 OptimalLinearArrangement<SimpleGraph>,
186 "DecisionOptimalLinearArrangement",
187 "2^num_vertices",
188 &["DOLA"],
189 "Decision version: does a linear arrangement of total edge length <= bound exist?",
190 dims: [
191 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
192 ],
193 fields: [
194 FieldInfo { name: "graph", type_name: "G", description: "The undirected graph G=(V,E)" },
195 FieldInfo { name: "bound", type_name: "usize", description: "Decision bound (maximum allowed total edge length)" },
196 ],
197 size_getters: [("num_vertices", num_vertices), ("num_edges", num_edges)]
198);
199
200#[cfg(feature = "example-db")]
201pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
202 use crate::topology::SimpleGraph;
203 vec![crate::example_db::specs::ModelExampleSpec {
206 id: "optimal_linear_arrangement",
207 instance: Box::new(OptimalLinearArrangement::new(SimpleGraph::new(
208 6,
209 vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (0, 3), (2, 5)],
210 ))),
211 optimal_config: vec![0, 1, 2, 3, 4, 5],
212 optimal_value: serde_json::json!(11),
213 }]
214}
215
216#[cfg(feature = "example-db")]
217pub(crate) fn decision_canonical_model_example_specs(
218) -> Vec<crate::example_db::specs::ModelExampleSpec> {
219 use crate::topology::SimpleGraph;
220 vec![crate::example_db::specs::ModelExampleSpec {
222 id: "decision_optimal_linear_arrangement_simplegraph",
223 instance: Box::new(Decision::new(
224 OptimalLinearArrangement::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])),
225 3,
226 )),
227 optimal_config: vec![0, 1, 2, 3],
228 optimal_value: serde_json::json!(true),
229 }]
230}
231
232#[cfg(feature = "example-db")]
233pub(crate) fn decision_canonical_rule_example_specs(
234) -> Vec<crate::example_db::specs::RuleExampleSpec> {
235 vec![crate::example_db::specs::RuleExampleSpec {
236 id: "decision_optimal_linear_arrangement_to_optimal_linear_arrangement",
237 build: || {
238 use crate::example_db::specs::assemble_rule_example;
239 use crate::export::SolutionPair;
240 use crate::rules::{AggregateReductionResult, ReduceToAggregate};
241 use crate::topology::SimpleGraph;
242
243 let source = Decision::new(
245 OptimalLinearArrangement::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])),
246 3,
247 );
248 let result = source.reduce_to_aggregate();
249 let target = result.target_problem();
250 let config = vec![0, 1, 2, 3];
251 assemble_rule_example(
252 &source,
253 target,
254 vec![SolutionPair {
255 source_config: config.clone(),
256 target_config: config,
257 }],
258 )
259 },
260 }]
261}
262
263#[cfg(test)]
264#[path = "../../unit_tests/models/graph/optimal_linear_arrangement.rs"]
265mod tests;