Skip to content

Commit 3d3b020

Browse files
committed
Tighten direct QUBO bounds and replace costly encodings
1 parent 0dd7448 commit 3d3b020

13 files changed

Lines changed: 297 additions & 254 deletions

‎docs/paper/reductions.typ‎

Lines changed: 18 additions & 27 deletions
Large diffs are not rendered by default.

‎src/rules/cyclicordering_qubo.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ impl ReductionResult for ReductionCyclicOrderingToQUBO {
3232
}
3333
#[crate::aggregate_reduction(identity)]
3434
impl crate::rules::AggregateReductionResult for ReductionCyclicOrderingToQUBO {}
35-
#[crate::reduction(transform = { exact { num_vars = "num_elements * (num_elements - 1) / 2" }, upper_bound { num_quadratic_terms = "(num_elements * (num_elements - 1) / 2)^2" } })]
35+
#[crate::reduction(transform = { exact { num_vars = "num_elements * (num_elements - 1) / 2" }, upper_bound { num_quadratic_terms = "num_elements * (num_elements - 1) * (num_elements - 2) / 2" } })]
3636
impl ReduceTo<Decision<QUBO<i64>>> for CyclicOrdering {
3737
type Result = ReductionCyclicOrderingToQUBO;
3838
fn reduce_to(&self) -> Result<Self::Result, ReductionError> {

‎src/rules/mod.rs‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@ pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::Ru
349349
specs.extend(minimumtardinesssequencingweighted_qubo::canonical_rule_example_specs());
350350
specs.extend(sequencingwithreleasetimesanddeadlines_qubo::canonical_rule_example_specs());
351351
specs.extend(dynamicstorageallocation_qubo::canonical_rule_example_specs());
352-
specs.extend(shortestcommonsupersequence_qubo::canonical_rule_example_specs());
353352
specs.extend(minimumtardinesssequencing_qubo::canonical_rule_example_specs());
354353
specs.extend(quadraticassignment_qubo::canonical_rule_example_specs());
355354
specs.extend(sumofsquarespartition_qubo::canonical_rule_example_specs());
@@ -753,5 +752,4 @@ pub(crate) mod staffscheduling_qubo;
753752
pub(crate) mod dynamicstorageallocation_qubo;
754753
pub(crate) mod minimumtardinesssequencingweighted_qubo;
755754
pub(crate) mod sequencingwithreleasetimesanddeadlines_qubo;
756-
pub(crate) mod shortestcommonsupersequence_qubo;
757755
mod start_time_qubo;

‎src/rules/optimallineararrangement_qubo.rs‎

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
//! Direct quadratic encoding of OptimalLinearArrangement<SimpleGraph>.
1+
//! Tournament comparisons encode the vertices between each edge's endpoints.
22
use crate::models::algebraic::QUBO;
33
use crate::models::graph::OptimalLinearArrangement;
4-
use crate::rules::qubo_helpers::{decode_rows, ObjectiveEncoding, QuboBuilder};
4+
use crate::rules::qubo_helpers::{pair_index, tournament_ranks, ObjectiveEncoding, QuboBuilder};
55
use crate::rules::{ExtractionResult, ReduceTo, ReductionError, ReductionResult};
66
use crate::topology::{Graph, SimpleGraph};
77
use crate::types::Min;
@@ -20,10 +20,7 @@ impl ReductionResult for ReductionOptimalLinearArrangementToQUBO {
2020
}
2121
fn extract_solution(&self, bits: &Vec<bool>) -> ExtractionResult<Vec<usize>> {
2222
self.encoding.validate(bits)?;
23-
decode_rows(
24-
bits,
25-
std::iter::repeat_n(self.num_vertices, self.num_vertices),
26-
)
23+
tournament_ranks(self.num_vertices, bits)
2724
}
2825
}
2926

@@ -40,26 +37,45 @@ impl crate::rules::AggregateReductionResult for ReductionOptimalLinearArrangemen
4037
}
4138

4239
#[crate::reduction(transform = {
43-
exact { num_vars = "num_vertices^2" },
44-
upper_bound { num_quadratic_terms = "num_vertices^2 * (num_vertices^2 - 1) / 2" },
40+
exact { num_vars = "num_vertices * (num_vertices - 1) / 2" },
41+
upper_bound { num_quadratic_terms = "num_vertices * (num_vertices - 1) * (num_vertices - 2) / 2" },
4542
})]
4643
impl ReduceTo<QUBO<i64>> for OptimalLinearArrangement<SimpleGraph> {
4744
type Result = ReductionOptimalLinearArrangementToQUBO;
4845
fn reduce_to(&self) -> Result<Self::Result, ReductionError> {
4946
type Builder = QuboBuilder<OptimalLinearArrangement<SimpleGraph>, QUBO<i64>>;
5047
let n = self.num_vertices();
51-
let mut q = Builder::new(Builder::dimension(n, n)?);
48+
let mut q = Builder::new(Builder::dimension(n, n.saturating_sub(1))? / 2);
49+
let edges =
50+
<Self as ReduceTo<QUBO<i64>>>::exact_i64(self.num_edges(), "counting graph edges")?;
51+
let span = <Self as ReduceTo<QUBO<i64>>>::exact_i64(
52+
n.saturating_sub(1),
53+
"bounding rank distance",
54+
)?;
55+
let high = edges.checked_mul(span).ok_or_else(Builder::overflow)?;
56+
let penalty = high.checked_add(1).ok_or_else(Builder::overflow)?;
57+
q.add_constant(i128::from(edges))?;
5258
for (u, v) in self.graph().edges() {
53-
for p in 0..n {
54-
for s in 0..n {
55-
q.add(u * n + p, v * n + s, p.abs_diff(s) as i128)?;
59+
for w in 0..n {
60+
if w == u || w == v {
61+
continue;
5662
}
63+
let a = pair_index(n, u.min(w), u.max(w));
64+
let b = pair_index(n, v.min(w), v.max(w));
65+
let ca = i128::from(u > w);
66+
let cb = i128::from(v > w);
67+
let sa = 1 - 2 * ca;
68+
let sb = 1 - 2 * cb;
69+
// XOR of the two comparisons identifies a vertex strictly between u and v.
70+
q.add_constant(ca + cb - 2 * ca * cb)?;
71+
q.add(a, a, sa * (1 - 2 * cb))?;
72+
q.add(b, b, sb * (1 - 2 * ca))?;
73+
q.add(a, b, -2 * sa * sb)?;
5774
}
5875
}
59-
let (penalty, low, high) = q.penalty()?;
60-
q.assignment(n, n, true, penalty)?;
76+
q.tournament(n, &[], penalty)?;
6177
Ok(ReductionOptimalLinearArrangementToQUBO {
62-
encoding: q.optimization(low, high)?,
78+
encoding: q.optimization(0, high)?,
6379
num_vertices: n,
6480
})
6581
}
@@ -74,9 +90,7 @@ pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::Ru
7490
OptimalLinearArrangement::new(SimpleGraph::new(3, vec![(0, 1), (1, 2)])),
7591
crate::export::SolutionPair {
7692
source_config: serde_json::json!([0, 1, 2]),
77-
target_config: serde_json::json!([
78-
true, false, false, false, true, false, false, false, true
79-
]),
93+
target_config: serde_json::json!([true, true, true]),
8094
},
8195
)
8296
},

‎src/rules/shortestcommonsupersequence_qubo.rs‎

Lines changed: 0 additions & 125 deletions
This file was deleted.

‎src/rules/shortestcommonsuperstring_qubo.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ impl crate::rules::AggregateReductionResult for ReductionShortestCommonSuperstri
5454
self.encoding.value(value)
5555
}
5656
}
57-
#[crate::reduction(transform = upper_bound { num_vars = "num_strings^2", num_quadratic_terms = "num_strings^4" })]
57+
#[crate::reduction(transform = upper_bound { num_vars = "num_strings^2", num_quadratic_terms = "num_strings * (num_strings - 1) * (2 * num_strings - 1)" })]
5858
impl ReduceTo<QUBO<i64>> for ShortestCommonSuperstring {
5959
type Result = ReductionShortestCommonSuperstringToQUBO;
6060
fn reduce_to(&self) -> Result<Self::Result, ReductionError> {

‎src/rules/stackercrane_qubo.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl crate::rules::AggregateReductionResult for ReductionStackerCraneToQUBO {
3838
self.encoding.value(value)
3939
}
4040
}
41-
#[crate::reduction(transform = { exact { num_vars = "num_arcs^2" }, upper_bound { num_quadratic_terms = "num_arcs^4" } })]
41+
#[crate::reduction(transform = { exact { num_vars = "num_arcs^2" }, upper_bound { num_quadratic_terms = "2 * num_arcs^2 * (num_arcs - 1)" } })]
4242
impl ReduceTo<QUBO<i64>> for StackerCrane {
4343
type Result = ReductionStackerCraneToQUBO;
4444
fn reduce_to(&self) -> Result<Self::Result, ReductionError> {

‎src/rules/staffscheduling_qubo.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl crate::rules::AggregateReductionResult for ReductionStaffSchedulingToQUBO {
4949

5050
#[crate::reduction(transform = upper_bound {
5151
num_vars = "63 * (num_schedules + num_periods + 1)",
52-
num_quadratic_terms = "(63 * (num_schedules + num_periods + 1))^2",
52+
num_quadratic_terms = "63 * (num_schedules + 1) * (63 * (num_schedules + 1) - 1) / 2 + num_periods * (3969 * num_schedules + 1953)",
5353
})]
5454
impl ReduceTo<Decision<QUBO<i64>>> for StaffScheduling {
5555
type Result = ReductionStaffSchedulingToQUBO;

0 commit comments

Comments
 (0)