-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathhamiltoniancircuit_satisfiability.rs
More file actions
152 lines (136 loc) · 5.41 KB
/
Copy pathhamiltoniancircuit_satisfiability.rs
File metadata and controls
152 lines (136 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! Reduction from HamiltonianCircuit to Satisfiability.
//!
//! The construction uses one Boolean variable for each `(vertex, position)` pair.
//! Exactly-one constraints make satisfying assignments permutation matrices, and
//! forbidden-successor clauses require consecutive vertices, including the last
//! and first positions, to be adjacent in the source graph.
use crate::models::formula::{CNFClause, Satisfiability};
use crate::models::graph::HamiltonianCircuit;
use crate::reduction;
use crate::rules::traits::{ReduceTo, ReductionResult};
use crate::topology::{Graph, SimpleGraph};
/// Result of reducing HamiltonianCircuit to Satisfiability.
#[derive(Debug, Clone)]
pub struct ReductionHamiltonianCircuitToSatisfiability {
target: Satisfiability,
num_vertices: usize,
}
impl ReductionResult for ReductionHamiltonianCircuitToSatisfiability {
type Source = HamiltonianCircuit<SimpleGraph>;
type Target = Satisfiability;
fn target_problem(&self) -> &Self::Target {
&self.target
}
fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
let n = self.num_vertices;
(0..n)
.map(|position| {
(0..n)
.find(|&vertex| target_solution[vertex * n + position] == 1)
.expect("satisfying assignment has one vertex at every position")
})
.collect()
}
}
fn variable(vertex: usize, position: usize, n: usize) -> i32 {
(vertex * n + position + 1) as i32
}
#[reduction(overhead = {
num_vars = "num_vertices * num_vertices + 1",
num_clauses = "2 * num_vertices + num_vertices * num_vertices * (num_vertices - 1) + num_vertices^3 + 2",
num_literals = "4 * num_vertices^3 + 2",
})]
impl ReduceTo<Satisfiability> for HamiltonianCircuit<SimpleGraph> {
type Result = ReductionHamiltonianCircuitToSatisfiability;
fn reduce_to(&self) -> Self::Result {
let n = self.num_vertices();
if n < 3 {
return ReductionHamiltonianCircuitToSatisfiability {
target: Satisfiability::new(
1,
vec![CNFClause::new(vec![1]), CNFClause::new(vec![-1])],
),
num_vertices: n,
};
}
let mut clauses = Vec::new();
// Every position contains exactly one vertex.
for position in 0..n {
clauses.push(CNFClause::new(
(0..n).map(|vertex| variable(vertex, position, n)).collect(),
));
for first_vertex in 0..n {
for second_vertex in first_vertex + 1..n {
clauses.push(CNFClause::new(vec![
-variable(first_vertex, position, n),
-variable(second_vertex, position, n),
]));
}
}
}
// Every vertex occurs at exactly one position.
for vertex in 0..n {
clauses.push(CNFClause::new(
(0..n)
.map(|position| variable(vertex, position, n))
.collect(),
));
for first_position in 0..n {
for second_position in first_position + 1..n {
clauses.push(CNFClause::new(vec![
-variable(vertex, first_position, n),
-variable(vertex, second_position, n),
]));
}
}
}
// Consecutive positions contain distinct adjacent vertices. The successor
// position is cyclic, so this also enforces the closing edge.
for position in 0..n {
let successor = (position + 1) % n;
for vertex in 0..n {
for next_vertex in 0..n {
if vertex == next_vertex || !self.graph().has_edge(vertex, next_vertex) {
clauses.push(CNFClause::new(vec![
-variable(vertex, position, n),
-variable(next_vertex, successor, n),
]));
}
}
}
}
ReductionHamiltonianCircuitToSatisfiability {
target: Satisfiability::new(n * n, clauses),
num_vertices: n,
}
}
}
#[cfg(feature = "example-db")]
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
use crate::export::SolutionPair;
vec![crate::example_db::specs::RuleExampleSpec {
id: "hamiltoniancircuit_to_satisfiability",
build: || {
let source = HamiltonianCircuit::new(SimpleGraph::new(
5,
vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (0, 2)],
));
crate::example_db::specs::rule_example_with_witness::<_, Satisfiability>(
source,
SolutionPair {
source_config: vec![0, 1, 2, 3, 4],
target_config: vec![
1, 0, 0, 0, 0, // vertex 0 is at position 0
0, 1, 0, 0, 0, // vertex 1 is at position 1
0, 0, 1, 0, 0, // vertex 2 is at position 2
0, 0, 0, 1, 0, // vertex 3 is at position 3
0, 0, 0, 0, 1, // vertex 4 is at position 4
],
},
)
},
}]
}
#[cfg(test)]
#[path = "../unit_tests/rules/hamiltoniancircuit_satisfiability.rs"]
mod tests;