quantrs2_tytan/optimize/
mod.rs

1//! Optimization utilities for QUBO/HOBO problems.
2//!
3//! This module provides optimization utilities and algorithms for
4//! solving QUBO and HOBO problems, with optional SciRS2 integration.
5
6use scirs2_core::ndarray::{Array, ArrayD, Ix2};
7use scirs2_core::random::prelude::*;
8use std::collections::HashMap;
9
10use crate::sampler::SampleResult;
11
12#[cfg(feature = "scirs")]
13use crate::scirs_stub;
14
15/// Enhanced QUBO optimization using SciRS2 (when available)
16///
17/// This function provides enhanced optimization for QUBO problems,
18/// using advanced techniques from SciRS2 when available.
19#[cfg(feature = "advanced_optimization")]
20pub fn optimize_qubo(
21    matrix: &Array<f64, Ix2>,
22    var_map: &HashMap<String, usize>,
23    initial_guess: Option<Vec<bool>>,
24    max_iterations: usize,
25) -> Vec<SampleResult> {
26    // Use SciRS2 enhanced parallel sampling
27    let enhanced_matrix = scirs_stub::enhance_qubo_matrix(matrix);
28    let samples = scirs_stub::parallel_sample_qubo(&enhanced_matrix, max_iterations);
29
30    // Map from indices back to variable names
31    let idx_to_var: HashMap<usize, String> = var_map
32        .iter()
33        .map(|(var, &idx)| (idx, var.clone()))
34        .collect();
35
36    // Convert to SampleResults
37    let mut results: Vec<SampleResult> = samples
38        .into_iter()
39        .map(|(solution, energy)| {
40            let assignments: HashMap<String, bool> = solution
41                .iter()
42                .enumerate()
43                .filter_map(|(idx, &value)| {
44                    idx_to_var
45                        .get(&idx)
46                        .map(|var_name| (var_name.clone(), value))
47                })
48                .collect();
49
50            SampleResult {
51                assignments,
52                energy,
53                occurrences: 1,
54            }
55        })
56        .collect();
57
58    // Sort by energy and return best solutions
59    results.sort_by(|a, b| {
60        a.energy
61            .partial_cmp(&b.energy)
62            .unwrap_or(std::cmp::Ordering::Equal)
63    });
64    results.truncate(10); // Return top 10 solutions
65
66    results
67}
68
69/// Fallback QUBO optimization implementation
70#[cfg(not(feature = "advanced_optimization"))]
71pub fn optimize_qubo(
72    matrix: &Array<f64, Ix2>,
73    var_map: &HashMap<String, usize>,
74    initial_guess: Option<Vec<bool>>,
75    max_iterations: usize,
76) -> Vec<SampleResult> {
77    // Use basic simulated annealing for fallback
78    let n_vars = var_map.len();
79
80    // Map from indices back to variable names
81    let idx_to_var: HashMap<usize, String> = var_map
82        .iter()
83        .map(|(var, &idx)| (idx, var.clone()))
84        .collect();
85
86    // Create initial solution (either provided or random)
87    let mut solution: Vec<bool> = if let Some(guess) = initial_guess {
88        guess
89    } else {
90        use scirs2_core::random::prelude::*;
91        let mut rng = thread_rng();
92        (0..n_vars).map(|_| rng.gen_bool(0.5)).collect()
93    };
94
95    // Calculate initial energy
96    let mut energy = calculate_energy(&solution, matrix);
97
98    // Basic simulated annealing parameters
99    let mut temperature = 10.0;
100    let cooling_rate = 0.99;
101
102    // Simulated annealing loop
103    let mut rng = thread_rng();
104
105    for _ in 0..max_iterations {
106        // Generate a neighbor by flipping a random bit
107        let flip_idx = rng.gen_range(0..n_vars);
108        solution[flip_idx] = !solution[flip_idx];
109
110        // Calculate new energy
111        let new_energy = calculate_energy(&solution, matrix);
112
113        // Determine if we accept the move
114        let accept = if new_energy < energy {
115            true
116        } else {
117            let p = ((energy - new_energy) / temperature).exp();
118            rng.gen::<f64>() < p
119        };
120
121        if accept {
122            energy = new_energy;
123        } else {
124            // Undo the flip if not accepted
125            solution[flip_idx] = !solution[flip_idx];
126        }
127
128        // Cool down
129        temperature *= cooling_rate;
130    }
131
132    // Convert to SampleResult
133    let assignments: HashMap<String, bool> = solution
134        .iter()
135        .enumerate()
136        .filter_map(|(idx, &value)| {
137            idx_to_var
138                .get(&idx)
139                .map(|var_name| (var_name.clone(), value))
140        })
141        .collect();
142
143    // Create result
144    let sample_result = SampleResult {
145        assignments,
146        energy,
147        occurrences: 1,
148    };
149
150    vec![sample_result]
151}
152
153/// Calculate the energy of a solution for a QUBO problem
154pub fn calculate_energy(solution: &[bool], matrix: &Array<f64, Ix2>) -> f64 {
155    calculate_energy_standard(solution, matrix)
156}
157
158/// Standard energy calculation without SciRS2
159fn calculate_energy_standard(solution: &[bool], matrix: &Array<f64, Ix2>) -> f64 {
160    let n = solution.len();
161    let mut energy = 0.0;
162
163    // Calculate from diagonal terms (linear)
164    for i in 0..n {
165        if solution[i] {
166            energy += matrix[[i, i]];
167        }
168    }
169
170    // Calculate from off-diagonal terms (quadratic)
171    for i in 0..n {
172        if solution[i] {
173            for j in (i + 1)..n {
174                if solution[j] {
175                    energy += matrix[[i, j]];
176                }
177            }
178        }
179    }
180
181    energy
182}
183
184/// Advanced HOBO tensor optimization using SciRS2
185#[cfg(feature = "scirs")]
186pub fn optimize_hobo(
187    tensor: &ArrayD<f64>,
188    var_map: &HashMap<String, usize>,
189    initial_guess: Option<Vec<bool>>,
190    max_iterations: usize,
191) -> Vec<SampleResult> {
192    // Apply SciRS2 tensor optimizations (placeholder)
193    let _enhanced = scirs_stub::optimize_hobo_tensor(tensor);
194
195    // For now, return a simple result
196    // In a full implementation, this would use tensor decomposition
197    optimize_hobo_basic(tensor, var_map, initial_guess, max_iterations)
198}
199
200/// Basic HOBO optimization for when SciRS2 is not available
201#[cfg(not(feature = "scirs"))]
202pub fn optimize_hobo(
203    tensor: &ArrayD<f64>,
204    var_map: &HashMap<String, usize>,
205    initial_guess: Option<Vec<bool>>,
206    max_iterations: usize,
207) -> Vec<SampleResult> {
208    optimize_hobo_basic(tensor, var_map, initial_guess, max_iterations)
209}
210
211/// Basic HOBO optimization implementation
212fn optimize_hobo_basic(
213    _tensor: &ArrayD<f64>,
214    var_map: &HashMap<String, usize>,
215    _initial_guess: Option<Vec<bool>>,
216    _max_iterations: usize,
217) -> Vec<SampleResult> {
218    // For now, implement a simple fallback
219    // In a full implementation, this would handle arbitrary tensor orders
220
221    // Return placeholder
222    let assignments: HashMap<String, bool> =
223        var_map.keys().map(|name| (name.clone(), false)).collect();
224
225    vec![SampleResult {
226        assignments,
227        energy: 0.0,
228        occurrences: 1,
229    }]
230}