use super::types::*;
use crate::sampler::{SampleResult, Sampler};
use scirs2_core::ndarray::Array2;
use std::collections::HashMap;
pub struct HierarchicalSolver<S: Sampler> {
base_sampler: S,
strategy: HierarchicalStrategy,
coarsening: CoarseningStrategy,
min_problem_size: usize,
max_levels: usize,
refinement_iterations: usize,
}
impl<S: Sampler> HierarchicalSolver<S> {
pub const fn new(base_sampler: S) -> Self {
Self {
base_sampler,
strategy: HierarchicalStrategy::CoarsenSolve,
coarsening: CoarseningStrategy::VariableClustering,
min_problem_size: 10,
max_levels: 10,
refinement_iterations: 5,
}
}
pub const fn with_strategy(mut self, strategy: HierarchicalStrategy) -> Self {
self.strategy = strategy;
self
}
pub const fn with_coarsening(mut self, coarsening: CoarseningStrategy) -> Self {
self.coarsening = coarsening;
self
}
pub const fn with_min_problem_size(mut self, size: usize) -> Self {
self.min_problem_size = size;
self
}
pub fn solve(
&mut self,
qubo: &Array2<f64>,
var_map: &HashMap<String, usize>,
shots: usize,
) -> Result<SampleResult, String> {
match self.strategy {
HierarchicalStrategy::CoarsenSolve => self.coarsen_solve_approach(qubo, var_map, shots),
HierarchicalStrategy::MultiGrid => self.multigrid_approach(qubo, var_map, shots),
HierarchicalStrategy::VCycle => self.v_cycle_approach(qubo, var_map, shots),
}
}
fn coarsen_solve_approach(
&mut self,
qubo: &Array2<f64>,
var_map: &HashMap<String, usize>,
shots: usize,
) -> Result<SampleResult, String> {
let hierarchy = self.build_hierarchy(qubo, var_map)?;
let coarsest_level = hierarchy.levels.last().ok_or("Empty hierarchy")?;
let coarse_results = self
.base_sampler
.run_qubo(
&(coarsest_level.qubo.clone(), coarsest_level.var_map.clone()),
shots,
)
.map_err(|e| format!("Sampler error: {e:?}"))?;
let coarse_result = coarse_results
.into_iter()
.next()
.ok_or_else(|| "No solutions found".to_string())?;
self.refine_through_hierarchy(&hierarchy, coarse_result)
}
fn multigrid_approach(
&mut self,
qubo: &Array2<f64>,
var_map: &HashMap<String, usize>,
shots: usize,
) -> Result<SampleResult, String> {
let initial_results = self
.base_sampler
.run_qubo(&(qubo.clone(), var_map.clone()), shots / 4)
.map_err(|e| format!("Initial sampler error: {e:?}"))?;
let mut current_solution = initial_results
.into_iter()
.next()
.ok_or_else(|| "No initial solutions found".to_string())?;
for _cycle in 0..3 {
current_solution =
self.v_cycle_refinement(qubo, var_map, ¤t_solution, shots / 4)?;
}
Ok(current_solution)
}
fn v_cycle_approach(
&mut self,
qubo: &Array2<f64>,
var_map: &HashMap<String, usize>,
shots: usize,
) -> Result<SampleResult, String> {
self.v_cycle_refinement(qubo, var_map, &SampleResult::default(), shots)
}
fn v_cycle_refinement(
&mut self,
qubo: &Array2<f64>,
var_map: &HashMap<String, usize>,
initial_solution: &SampleResult,
shots: usize,
) -> Result<SampleResult, String> {
let hierarchy = self.build_hierarchy(qubo, var_map)?;
let mut current_solution = initial_solution.clone();
for level in 1..hierarchy.levels.len() {
current_solution = self.restrict_solution(&hierarchy, level - 1, ¤t_solution)?;
}
if let Some(coarsest_level) = hierarchy.levels.last() {
let coarse_results = self
.base_sampler
.run_qubo(
&(coarsest_level.qubo.clone(), coarsest_level.var_map.clone()),
shots,
)
.map_err(|e| format!("Coarse sampler error: {e:?}"))?;
current_solution = coarse_results
.into_iter()
.next()
.ok_or_else(|| "No coarse solutions found".to_string())?;
}
for level in (0..hierarchy.levels.len() - 1).rev() {
current_solution =
self.interpolate_and_refine(&hierarchy, level, ¤t_solution, shots / 4)?;
}
Ok(current_solution)
}
fn build_hierarchy(
&self,
qubo: &Array2<f64>,
var_map: &HashMap<String, usize>,
) -> Result<Hierarchy, String> {
let mut levels = Vec::new();
let mut projections = Vec::new();
let mut current_qubo = qubo.clone();
let mut current_var_map = var_map.clone();
let mut current_size = current_qubo.shape()[0];
let mut level = 0;
levels.push(HierarchyLevel {
level,
qubo: current_qubo.clone(),
var_map: current_var_map.clone(),
size: current_size,
});
while current_size > self.min_problem_size && level < self.max_levels {
let (coarse_qubo, coarse_var_map, projection) =
self.coarsen_problem(¤t_qubo, ¤t_var_map)?;
current_qubo = coarse_qubo;
current_var_map = coarse_var_map;
current_size = current_qubo.shape()[0];
level += 1;
levels.push(HierarchyLevel {
level,
qubo: current_qubo.clone(),
var_map: current_var_map.clone(),
size: current_size,
});
projections.push(projection);
}
Ok(Hierarchy {
levels,
projections,
})
}
fn coarsen_problem(
&self,
qubo: &Array2<f64>,
var_map: &HashMap<String, usize>,
) -> Result<(Array2<f64>, HashMap<String, usize>, Projection), String> {
match self.coarsening {
CoarseningStrategy::VariableClustering => {
self.variable_clustering_coarsen(qubo, var_map)
}
_ => {
self.variable_clustering_coarsen(qubo, var_map)
}
}
}
fn variable_clustering_coarsen(
&self,
qubo: &Array2<f64>,
_var_map: &HashMap<String, usize>,
) -> Result<(Array2<f64>, HashMap<String, usize>, Projection), String> {
let n = qubo.shape()[0];
let mut clusters = Vec::new();
let mut assigned = vec![false; n];
for i in 0..n {
if !assigned[i] {
let mut cluster = vec![i];
assigned[i] = true;
for j in i + 1..n {
if !assigned[j] && qubo[[i, j]].abs() > 0.5 {
cluster.push(j);
assigned[j] = true;
}
}
clusters.push(cluster);
}
}
let num_clusters = clusters.len();
let mut coarse_qubo = Array2::zeros((num_clusters, num_clusters));
for (ci, cluster_i) in clusters.iter().enumerate() {
for (cj, cluster_j) in clusters.iter().enumerate() {
let mut weight = 0.0;
for &i in cluster_i {
for &j in cluster_j {
weight += qubo[[i, j]];
}
}
coarse_qubo[[ci, cj]] = weight;
}
}
let mut coarse_var_map = HashMap::new();
for (ci, _cluster) in clusters.iter().enumerate() {
let var_name = format!("cluster_{ci}");
coarse_var_map.insert(var_name, ci);
}
let projection = Projection {
fine_to_coarse: (0..n)
.map(|i| clusters.iter().position(|c| c.contains(&i)).unwrap_or(0))
.collect(),
coarse_to_fine: clusters,
};
Ok((coarse_qubo, coarse_var_map, projection))
}
fn refine_through_hierarchy(
&mut self,
hierarchy: &Hierarchy,
coarse_solution: SampleResult,
) -> Result<SampleResult, String> {
let mut current_solution = coarse_solution;
for level in (0..hierarchy.levels.len() - 1).rev() {
current_solution = self.interpolate_solution(hierarchy, level, ¤t_solution)?;
for _iter in 0..self.refinement_iterations {
current_solution = self.refine_solution(
&hierarchy.levels[level].qubo,
&hierarchy.levels[level].var_map,
¤t_solution,
10, )?;
}
}
Ok(current_solution)
}
fn restrict_solution(
&self,
hierarchy: &Hierarchy,
level: usize,
solution: &SampleResult,
) -> Result<SampleResult, String> {
if level >= hierarchy.projections.len() {
return Ok(solution.clone());
}
let projection = &hierarchy.projections[level];
let coarse_level = &hierarchy.levels[level + 1];
let restricted_solution = SampleResult::default();
for (var_name, &coarse_idx) in &coarse_level.var_map {
let fine_vars = &projection.coarse_to_fine[coarse_idx];
let mut votes = 0i32;
for &fine_idx in fine_vars {
if let Some(fine_var_name) = hierarchy.levels[level]
.var_map
.iter()
.find(|(_, &idx)| idx == fine_idx)
.map(|(name, _)| name)
{
if let Some(sample) = solution.best_sample() {
if let Some(&value) = sample.get(fine_var_name) {
votes += if value { 1 } else { -1 };
}
}
}
}
if let Some(mut best_sample) = restricted_solution.best_sample().cloned() {
best_sample.insert(var_name.clone(), votes > 0);
} else {
let mut new_sample = HashMap::new();
new_sample.insert(var_name.clone(), votes > 0);
}
}
Ok(restricted_solution)
}
fn interpolate_solution(
&self,
hierarchy: &Hierarchy,
level: usize,
coarse_solution: &SampleResult,
) -> Result<SampleResult, String> {
if level >= hierarchy.projections.len() {
return Ok(coarse_solution.clone());
}
let projection = &hierarchy.projections[level];
let fine_level = &hierarchy.levels[level];
let coarse_level = &hierarchy.levels[level + 1];
let interpolated_solution = SampleResult::default();
for (fine_var_name, &fine_idx) in &fine_level.var_map {
let coarse_idx = projection.fine_to_coarse[fine_idx];
if let Some((coarse_var_name, _)) = coarse_level
.var_map
.iter()
.find(|(_, &idx)| idx == coarse_idx)
{
if let Some(coarse_sample) = coarse_solution.best_sample() {
if let Some(&coarse_value) = coarse_sample.get(coarse_var_name) {
if let Some(mut fine_sample) = interpolated_solution.best_sample().cloned()
{
fine_sample.insert(fine_var_name.clone(), coarse_value);
} else {
let mut new_sample = HashMap::new();
new_sample.insert(fine_var_name.clone(), coarse_value);
}
}
}
}
}
Ok(interpolated_solution)
}
fn interpolate_and_refine(
&mut self,
hierarchy: &Hierarchy,
level: usize,
coarse_solution: &SampleResult,
shots: usize,
) -> Result<SampleResult, String> {
let mut interpolated = self.interpolate_solution(hierarchy, level, coarse_solution)?;
for _iter in 0..self.refinement_iterations {
interpolated = self.refine_solution(
&hierarchy.levels[level].qubo,
&hierarchy.levels[level].var_map,
&interpolated,
shots,
)?;
}
Ok(interpolated)
}
fn refine_solution(
&mut self,
qubo: &Array2<f64>,
var_map: &HashMap<String, usize>,
_current_solution: &SampleResult,
shots: usize,
) -> Result<SampleResult, String> {
let results = self
.base_sampler
.run_qubo(&(qubo.clone(), var_map.clone()), shots)
.map_err(|e| format!("Refinement sampler error: {e:?}"))?;
results
.into_iter()
.next()
.ok_or_else(|| "No refinement results found".to_string())
}
}
impl Default for SampleResult {
fn default() -> Self {
Self {
assignments: HashMap::new(),
energy: 0.0,
occurrences: 0,
}
}
}
impl SampleResult {
pub const fn best_sample(&self) -> Option<&HashMap<String, bool>> {
Some(&self.assignments)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sampler::simulated_annealing::SASampler;
use scirs2_core::ndarray::Array2;
use std::collections::HashMap;
#[test]
fn test_hierarchical_solver_creation() {
let base_sampler = SASampler::new(None);
let solver = HierarchicalSolver::new(base_sampler);
assert_eq!(solver.min_problem_size, 10);
assert_eq!(solver.max_levels, 10);
}
#[test]
fn test_hierarchy_building() {
let base_sampler = SASampler::new(None);
let solver = HierarchicalSolver::new(base_sampler);
let mut qubo = Array2::from_shape_vec(
(4, 4),
vec![
1.0, 0.5, 0.1, 0.0, 0.5, 1.0, 0.0, 0.1, 0.1, 0.0, 1.0, 0.5, 0.0, 0.1, 0.5, 1.0,
],
)
.expect("QUBO matrix construction should succeed");
let mut var_map = HashMap::new();
for i in 0..4 {
var_map.insert(format!("x{i}"), i);
}
let hierarchy = solver.build_hierarchy(&qubo, &var_map);
assert!(hierarchy.is_ok());
let h = hierarchy.expect("Hierarchy building should succeed");
assert!(!h.levels.is_empty());
assert_eq!(h.levels[0].size, 4); }
}