Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.idea/
target/
.cargo-home/
.venv/
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hercules"
version = "0.6.2"
version = "0.6.3"
edition = "2021"
authors = ["Dustin Kenefake"]
license = "BSD-3-Clause"
Expand Down Expand Up @@ -28,7 +28,7 @@ rayon = "1.10.0"
petgraph = "0.8.3"
pyo3 = { version = "0.25.0", optional = true, features = ["extension-module", "abi3-py37"] }
clarabel = "0.10.0"
mixingcut = "0.1.3"
mixingcut = "0.1.4"
herculesabqp = "0.1.2"
intel-mkl-src = "0.8.1"
rustc-hash = "2.1.1"
Expand Down
9 changes: 5 additions & 4 deletions benches/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ pub fn make_bench_data() -> BenchData {
let qubo_medium = Qubo::make_random_qubo(128, &mut prng, 0.08);
let qubo_enum = Qubo::make_random_qubo(10, &mut enum_prng, 0.25);
let qubo_solve = Qubo::make_random_qubo(96, &mut solve_prng, 0.08);
let qubo_gka1b = Qubo::read_qubo("test_data/gka1b.qubo");
let qubo_gka2b = Qubo::read_qubo("test_data/gka2b.qubo");
let qubo_gka6a = Qubo::read_qubo("test_data/gka6a.qubo");
let qubo_gka7a = Qubo::read_qubo("test_data/gka7a.qubo");
let qubo_gka1b = Qubo::read_qubo("test_data/gka/gka1b.qubo");
let qubo_gka2b = Qubo::read_qubo("test_data/gka/gka2b.qubo");
let qubo_gka6a = Qubo::read_qubo("test_data/gka/gka6a.qubo");
let qubo_gka7a = Qubo::read_qubo("test_data/gka/gka7a.qubo");
let qubo_bqp50 = Qubo::read_qubo("test_data/bqp50.qubo");
let qubo_test_large = Qubo::read_qubo("test_data/test_large.qubo");
let x_small = generate_random_binary_point(qubo_small.num_x(), &mut prng, 0.5);
Expand All @@ -73,6 +73,7 @@ pub fn make_bench_data() -> BenchData {
solution: 0.5 * Array1::ones(process_solver.qubo.num_x()),
fixed_variables: process_solver.options.fixed_variables.clone(),
run_heuristic: false,
subproblem_state: None,
};

BenchData {
Expand Down
14 changes: 13 additions & 1 deletion src/branch_node.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
use crate::FixedVarMap;
use ndarray::Array1;
use ndarray::{Array1, Array2};
use std::cmp::Ordering;

#[derive(Clone)]
pub struct MixingCutNodeState {
pub factor_matrix: Array2<f64>,
pub free_variables: Vec<usize>,
}

#[derive(Clone)]
pub enum SubProblemNodeState {
MixingCut(MixingCutNodeState),
}

/// Struct the describes the branch and bound tree nodes
#[derive(Clone)]
pub struct QuboBBNode {
pub lower_bound: f64,
pub solution: Array1<f64>,
pub fixed_variables: FixedVarMap,
pub run_heuristic: bool,
pub subproblem_state: Option<SubProblemNodeState>,
}

impl Eq for QuboBBNode {}
Expand Down
18 changes: 12 additions & 6 deletions src/branch_stratagy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,15 @@ pub fn full_strong_branching(solver: &BBSolver, node: &QuboBBNode) -> BranchResu
fixed_variables: list_0,
solution: node.solution.clone(),
run_heuristic: false,
subproblem_state: node.subproblem_state.clone(),
};

let node_1 = QuboBBNode {
lower_bound: 0.0,
fixed_variables: list_1,
solution: node.solution.clone(),
run_heuristic: false,
subproblem_state: node.subproblem_state.clone(),
};

// solve for the
Expand All @@ -384,12 +386,12 @@ pub fn full_strong_branching(solver: &BBSolver, node: &QuboBBNode) -> BranchResu
.solve_lower_bound(solver, &node_1, None);

// find the minimum of the two objectives
let score = bound_0.0.min(bound_1.0);
let score = bound_0.lower_bound().min(bound_1.lower_bound());

if bound_0.0 >= solver.best_solution_value {
if bound_0.lower_bound() >= solver.best_solution_value {
found_fixes.insert(*i, 1);
fixed_variables.insert(*i, 1);
} else if bound_1.0 >= solver.best_solution_value {
} else if bound_1.lower_bound() >= solver.best_solution_value {
found_fixes.insert(*i, 0);
fixed_variables.insert(*i, 0);
}
Expand Down Expand Up @@ -475,13 +477,15 @@ pub fn partial_strong_branching(solver: &BBSolver, node: &QuboBBNode) -> BranchR
fixed_variables: list_0,
solution: node.solution.clone(),
run_heuristic: false,
subproblem_state: node.subproblem_state.clone(),
};

let node_1 = QuboBBNode {
lower_bound: 0.0,
fixed_variables: list_1,
solution: node.solution.clone(),
run_heuristic: false,
subproblem_state: node.subproblem_state.clone(),
};

let bound_0 = solver
Expand All @@ -492,12 +496,12 @@ pub fn partial_strong_branching(solver: &BBSolver, node: &QuboBBNode) -> BranchR
.subproblem_solver
.solve_lower_bound(solver, &node_1, None);

let score_i = bound_0.0.min(bound_1.0);
let score_i = bound_0.lower_bound().min(bound_1.lower_bound());

if bound_0.0 >= solver.best_solution_value {
if bound_0.lower_bound() >= solver.best_solution_value {
found_fixes.insert(j, 1);
fixed_variables.insert(j, 1);
} else if bound_1.0 >= solver.best_solution_value {
} else if bound_1.lower_bound() >= solver.best_solution_value {
found_fixes.insert(j, 0);
fixed_variables.insert(j, 0);
}
Expand Down Expand Up @@ -704,6 +708,7 @@ mod tests {
solution: 0.5 * Array1::ones(3),
fixed_variables,
run_heuristic: false,
subproblem_state: None,
};

let result = connected_components(&solver, &node);
Expand All @@ -721,6 +726,7 @@ mod tests {
solution: 0.5 * Array1::ones(3),
fixed_variables,
run_heuristic: false,
subproblem_state: None,
};

let result = worst_approximation_second_order(&solver, &node);
Expand Down
56 changes: 53 additions & 3 deletions src/branch_subproblem.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,62 @@
use crate::branch_node::QuboBBNode;
use crate::branch_node::{QuboBBNode, SubProblemNodeState};
use crate::branchbound::BBSolver;
use crate::qubo::Qubo;
use crate::subproblemsolvers::clarabel_lp::ClarabelLPSolver;
use crate::subproblemsolvers::clarabel_qp::ClarabelQPSolver;
use crate::subproblemsolvers::hercules_abqp::HerculesABQPSolver;
use crate::subproblemsolvers::hercules_cd_qp::HerculesCDQPSolver;
use crate::subproblemsolvers::mixingcut_sdp::MixingCutSDPSolver;
use crate::subproblemsolvers::roofdual::RoofDualSolver;
use ndarray::Array1;

pub type SubProblemResult = (f64, Array1<f64>);
pub trait SubProblemResult {
fn lower_bound(&self) -> f64;
fn relaxed_solution(&self) -> Option<&Array1<f64>>;
fn candidate_primal_solution(&self) -> Option<&Array1<usize>>;
fn subproblem_state(&self) -> Option<&SubProblemNodeState>;
fn into_parts(
self: Box<Self>,
) -> (
f64,
Option<Array1<f64>>,
Option<Array1<usize>>,
Option<SubProblemNodeState>,
);
}

pub struct BasicSubProblemResult {
pub lower_bound: f64,
pub relaxed_solution: Array1<f64>,
}

impl SubProblemResult for BasicSubProblemResult {
fn lower_bound(&self) -> f64 {
self.lower_bound
}

fn relaxed_solution(&self) -> Option<&Array1<f64>> {
Some(&self.relaxed_solution)
}

fn candidate_primal_solution(&self) -> Option<&Array1<usize>> {
None
}

fn subproblem_state(&self) -> Option<&SubProblemNodeState> {
None
}

fn into_parts(
self: Box<Self>,
) -> (
f64,
Option<Array1<f64>>,
Option<Array1<usize>>,
Option<SubProblemNodeState>,
) {
(self.lower_bound, Some(self.relaxed_solution), None, None)
}
}

#[derive(Clone, Copy)]
pub struct SubProblemOptions {
Expand All @@ -33,7 +81,7 @@ pub trait SubProblemSolver {
bbsolver: &BBSolver,
node: &QuboBBNode,
sub_problem_options: Option<SubProblemOptions>,
) -> SubProblemResult;
) -> Box<dyn SubProblemResult>;
}

#[derive(Clone, Copy)]
Expand All @@ -42,6 +90,7 @@ pub enum SubProblemSelection {
ClarabelLP,
HerculesABQP,
HerculesCDQP,
MixingCutSDP,
RoofDualQPBO,
}

Expand All @@ -54,6 +103,7 @@ pub fn get_sub_problem_solver(
SubProblemSelection::ClarabelLP => Box::new(ClarabelLPSolver::new(qubo)),
SubProblemSelection::HerculesABQP => Box::new(HerculesABQPSolver::new(qubo)),
SubProblemSelection::HerculesCDQP => Box::new(HerculesCDQPSolver::new(qubo)),
SubProblemSelection::MixingCutSDP => Box::new(MixingCutSDPSolver::new(qubo)),
SubProblemSelection::RoofDualQPBO => Box::new(RoofDualSolver::new(qubo)),
}
}
Loading
Loading