Skip to content

Commit c828d9e

Browse files
committed
feat: add mvc to minimum weight and/or graph reduction
1 parent e98b6fe commit c828d9e

3 files changed

Lines changed: 242 additions & 0 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//! Reduction from MinimumVertexCover to MinimumWeightAndOrGraph.
2+
3+
use crate::models::graph::MinimumVertexCover;
4+
use crate::models::misc::MinimumWeightAndOrGraph;
5+
use crate::reduction;
6+
use crate::rules::traits::{ReduceTo, ReductionResult};
7+
use crate::topology::Graph;
8+
use crate::topology::SimpleGraph;
9+
10+
/// Result of reducing MinimumVertexCover to MinimumWeightAndOrGraph.
11+
#[derive(Debug, Clone)]
12+
pub struct ReductionVCToAndOrGraph {
13+
target: MinimumWeightAndOrGraph,
14+
sink_arc_start: usize,
15+
num_source_vertices: usize,
16+
}
17+
18+
impl ReductionResult for ReductionVCToAndOrGraph {
19+
type Source = MinimumVertexCover<SimpleGraph, i32>;
20+
type Target = MinimumWeightAndOrGraph;
21+
22+
fn target_problem(&self) -> &Self::Target {
23+
&self.target
24+
}
25+
26+
fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
27+
(0..self.num_source_vertices)
28+
.map(|j| usize::from(target_solution.get(self.sink_arc_start + j) == Some(&1)))
29+
.collect()
30+
}
31+
}
32+
33+
#[reduction(
34+
overhead = {
35+
num_vertices = "1 + num_edges + 2 * num_vertices",
36+
num_arcs = "3 * num_edges + num_vertices",
37+
}
38+
)]
39+
impl ReduceTo<MinimumWeightAndOrGraph> for MinimumVertexCover<SimpleGraph, i32> {
40+
type Result = ReductionVCToAndOrGraph;
41+
42+
fn reduce_to(&self) -> Self::Result {
43+
let n = self.graph().num_vertices();
44+
let edges = self.graph().edges();
45+
let m = edges.len();
46+
47+
let num_target_vertices = 1 + m + (2 * n);
48+
let mut gate_types = vec![None; num_target_vertices];
49+
gate_types[0] = Some(true);
50+
for gate in gate_types.iter_mut().skip(1).take(m + n) {
51+
*gate = Some(false);
52+
}
53+
54+
let edge_vertex = |i: usize| 1 + i;
55+
let cover_vertex = |j: usize| 1 + m + j;
56+
let sink_vertex = |j: usize| 1 + m + n + j;
57+
58+
let mut arcs = Vec::with_capacity((3 * m) + n);
59+
let mut arc_weights = Vec::with_capacity((3 * m) + n);
60+
61+
for i in 0..m {
62+
arcs.push((0, edge_vertex(i)));
63+
arc_weights.push(1);
64+
}
65+
66+
for (i, &(u, v)) in edges.iter().enumerate() {
67+
arcs.push((edge_vertex(i), cover_vertex(u)));
68+
arc_weights.push(1);
69+
arcs.push((edge_vertex(i), cover_vertex(v)));
70+
arc_weights.push(1);
71+
}
72+
73+
let sink_arc_start = arcs.len();
74+
for (j, &weight) in self.weights().iter().enumerate() {
75+
arcs.push((cover_vertex(j), sink_vertex(j)));
76+
arc_weights.push(weight);
77+
}
78+
79+
let target =
80+
MinimumWeightAndOrGraph::new(num_target_vertices, arcs, 0, gate_types, arc_weights);
81+
82+
ReductionVCToAndOrGraph {
83+
target,
84+
sink_arc_start,
85+
num_source_vertices: n,
86+
}
87+
}
88+
}
89+
90+
#[cfg(any(test, feature = "example-db"))]
91+
fn issue_example_source() -> MinimumVertexCover<SimpleGraph, i32> {
92+
MinimumVertexCover::new(SimpleGraph::new(3, vec![(0, 1), (1, 2)]), vec![1i32; 3])
93+
}
94+
95+
#[cfg(feature = "example-db")]
96+
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
97+
use crate::export::SolutionPair;
98+
99+
vec![crate::example_db::specs::RuleExampleSpec {
100+
id: "minimumvertexcover_to_minimumweightandorgraph",
101+
build: || {
102+
crate::example_db::specs::rule_example_with_witness::<_, MinimumWeightAndOrGraph>(
103+
issue_example_source(),
104+
SolutionPair {
105+
source_config: vec![0, 1, 0],
106+
target_config: vec![1, 1, 0, 1, 1, 0, 0, 1, 0],
107+
},
108+
)
109+
},
110+
}]
111+
}
112+
113+
#[cfg(test)]
114+
#[path = "../unit_tests/rules/minimumvertexcover_minimumweightandorgraph.rs"]
115+
mod tests;

src/rules/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub(crate) mod minimumvertexcover_minimumfeedbackvertexset;
9393
pub(crate) mod minimumvertexcover_minimumhittingset;
9494
pub(crate) mod minimumvertexcover_minimummaximalmatching;
9595
pub(crate) mod minimumvertexcover_minimumsetcovering;
96+
pub(crate) mod minimumvertexcover_minimumweightandorgraph;
9697
pub(crate) mod naesatisfiability_maxcut;
9798
pub(crate) mod naesatisfiability_partitionintoperfectmatchings;
9899
pub(crate) mod naesatisfiability_setsplitting;
@@ -491,6 +492,7 @@ pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::Ru
491492
specs.extend(minimumvertexcover_minimumfeedbackvertexset::canonical_rule_example_specs());
492493
specs.extend(minimumvertexcover_minimumhittingset::canonical_rule_example_specs());
493494
specs.extend(minimumvertexcover_minimumsetcovering::canonical_rule_example_specs());
495+
specs.extend(minimumvertexcover_minimumweightandorgraph::canonical_rule_example_specs());
494496
specs.extend(naesatisfiability_setsplitting::canonical_rule_example_specs());
495497
specs.extend(setsplitting_betweenness::canonical_rule_example_specs());
496498
specs.extend(satisfiability_integralflowhomologousarcs::canonical_rule_example_specs());
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#[cfg(feature = "example-db")]
2+
use super::canonical_rule_example_specs;
3+
use super::{issue_example_source, ReductionVCToAndOrGraph};
4+
use crate::models::graph::MinimumVertexCover;
5+
use crate::models::misc::MinimumWeightAndOrGraph;
6+
use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target;
7+
use crate::rules::traits::ReductionResult;
8+
use crate::rules::ReduceTo;
9+
#[cfg(feature = "example-db")]
10+
use crate::solvers::BruteForce;
11+
use crate::topology::SimpleGraph;
12+
use crate::traits::Problem;
13+
use crate::types::Min;
14+
15+
fn weighted_path_source() -> MinimumVertexCover<SimpleGraph, i32> {
16+
MinimumVertexCover::new(SimpleGraph::new(3, vec![(0, 1), (1, 2)]), vec![4, 1, 3])
17+
}
18+
19+
#[test]
20+
fn test_minimumvertexcover_to_minimumweightandorgraph_closed_loop() {
21+
let source = issue_example_source();
22+
let reduction: ReductionVCToAndOrGraph =
23+
ReduceTo::<MinimumWeightAndOrGraph>::reduce_to(&source);
24+
25+
assert_optimization_round_trip_from_optimization_target(
26+
&source,
27+
&reduction,
28+
"MVC -> MinimumWeightAndOrGraph closed loop",
29+
);
30+
}
31+
32+
#[test]
33+
fn test_reduction_structure() {
34+
let source = issue_example_source();
35+
let reduction: ReductionVCToAndOrGraph =
36+
ReduceTo::<MinimumWeightAndOrGraph>::reduce_to(&source);
37+
let target = reduction.target_problem();
38+
39+
assert_eq!(target.num_vertices(), 9);
40+
assert_eq!(target.num_arcs(), 9);
41+
assert_eq!(target.source(), 0);
42+
assert_eq!(
43+
target.gate_types(),
44+
&[
45+
Some(true),
46+
Some(false),
47+
Some(false),
48+
Some(false),
49+
Some(false),
50+
Some(false),
51+
None,
52+
None,
53+
None,
54+
]
55+
);
56+
assert_eq!(
57+
target.arcs(),
58+
&[
59+
(0, 1),
60+
(0, 2),
61+
(1, 3),
62+
(1, 4),
63+
(2, 4),
64+
(2, 5),
65+
(3, 6),
66+
(4, 7),
67+
(5, 8),
68+
]
69+
);
70+
assert_eq!(target.arc_weights(), &[1, 1, 1, 1, 1, 1, 1, 1, 1]);
71+
}
72+
73+
#[test]
74+
fn test_weighted_vertices_are_charged_on_sink_arcs() {
75+
let source = weighted_path_source();
76+
let reduction: ReductionVCToAndOrGraph =
77+
ReduceTo::<MinimumWeightAndOrGraph>::reduce_to(&source);
78+
let target = reduction.target_problem();
79+
80+
let target_solution = vec![1, 1, 0, 1, 1, 0, 0, 1, 0];
81+
assert_eq!(source.evaluate(&[0, 1, 0]), Min(Some(1)));
82+
assert_eq!(target.evaluate(&target_solution), Min(Some(5)));
83+
assert_eq!(target.arc_weights(), &[1, 1, 1, 1, 1, 1, 4, 1, 3]);
84+
assert_eq!(reduction.extract_solution(&target_solution), vec![0, 1, 0]);
85+
}
86+
87+
#[cfg(feature = "example-db")]
88+
#[test]
89+
fn test_canonical_rule_example_spec_builds() {
90+
let example = (canonical_rule_example_specs()
91+
.into_iter()
92+
.find(|spec| spec.id == "minimumvertexcover_to_minimumweightandorgraph")
93+
.expect("example spec should be registered")
94+
.build)();
95+
96+
assert_eq!(example.source.problem, "MinimumVertexCover");
97+
assert_eq!(example.target.problem, "MinimumWeightAndOrGraph");
98+
assert_eq!(example.solutions.len(), 1);
99+
100+
let source: MinimumVertexCover<SimpleGraph, i32> =
101+
serde_json::from_value(example.source.instance.clone())
102+
.expect("source example deserializes");
103+
let target: MinimumWeightAndOrGraph = serde_json::from_value(example.target.instance.clone())
104+
.expect("target example deserializes");
105+
let solution = &example.solutions[0];
106+
107+
assert_eq!(source.evaluate(&solution.source_config), Min(Some(1)));
108+
assert_eq!(target.evaluate(&solution.target_config), Min(Some(5)));
109+
110+
let best_source = BruteForce::new()
111+
.find_witness(&source)
112+
.expect("source example should have an optimum");
113+
let best_target = BruteForce::new()
114+
.find_witness(&target)
115+
.expect("target example should have an optimum");
116+
117+
assert_eq!(
118+
source.evaluate(&solution.source_config),
119+
source.evaluate(&best_source)
120+
);
121+
assert_eq!(
122+
target.evaluate(&solution.target_config),
123+
target.evaluate(&best_target)
124+
);
125+
}

0 commit comments

Comments
 (0)