1use crate::sampler::{SampleResult, Sampler, SamplerError, SamplerResult};
19use scirs2_core::ndarray::{Array1, Array2, ArrayD};
20use scirs2_core::random::prelude::*;
21use serde::{Deserialize, Serialize};
22use std::collections::HashMap;
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct GroverAmplificationConfig {
27 pub grover_iterations: usize,
29 pub population_size: usize,
31 pub elite_count: usize,
33 pub temperature: f64,
35 pub amplification_factor: f64,
37 pub adaptive_amplification: bool,
39 pub diversity_weight: f64,
41}
42
43impl Default for GroverAmplificationConfig {
44 fn default() -> Self {
45 Self {
46 grover_iterations: 20,
47 population_size: 100,
48 elite_count: 10,
49 temperature: 1.0,
50 amplification_factor: 2.0,
51 adaptive_amplification: true,
52 diversity_weight: 0.1,
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
59struct AmplitudeState {
60 state: Vec<bool>,
62 energy: f64,
64 amplitude: f64,
66 distance_to_best: usize,
68}
69
70#[derive(Debug, Clone)]
72pub struct GroverAmplifiedSolver {
73 config: GroverAmplificationConfig,
74 best_solution: Option<Vec<bool>>,
76 best_energy: f64,
78 energy_history: Vec<f64>,
80}
81
82impl GroverAmplifiedSolver {
83 pub const fn new(config: GroverAmplificationConfig) -> Self {
85 Self {
86 config,
87 best_solution: None,
88 best_energy: f64::INFINITY,
89 energy_history: Vec::new(),
90 }
91 }
92
93 fn initialize_population(&self, n_vars: usize, qubo: &Array2<f64>) -> Vec<AmplitudeState> {
95 let mut rng = thread_rng();
96 let mut population = Vec::new();
97
98 for _ in 0..self.config.population_size {
99 let state: Vec<bool> = (0..n_vars).map(|_| rng.gen_bool(0.5)).collect();
100 let energy = self.compute_energy(&state, qubo);
101
102 population.push(AmplitudeState {
103 state,
104 energy,
105 amplitude: 1.0 / self.config.population_size as f64,
106 distance_to_best: 0,
107 });
108 }
109
110 population
111 }
112
113 fn compute_energy(&self, state: &[bool], qubo: &Array2<f64>) -> f64 {
115 let n = state.len();
116 let mut energy = 0.0;
117
118 for i in 0..n {
119 if state[i] {
120 energy += qubo[[i, i]];
121 }
122 for j in (i + 1)..n {
123 if state[i] && state[j] {
124 energy += qubo[[i, j]];
125 }
126 }
127 }
128
129 energy
130 }
131
132 fn oracle_marking(&self, population: &mut [AmplitudeState]) {
134 let min_energy = population
136 .iter()
137 .map(|s| s.energy)
138 .fold(f64::INFINITY, f64::min);
139 let max_energy = population
140 .iter()
141 .map(|s| s.energy)
142 .fold(f64::NEG_INFINITY, f64::max);
143
144 let energy_range = max_energy - min_energy;
145
146 for state in population.iter_mut() {
148 let normalized_energy = if energy_range > 1e-10 {
149 (state.energy - min_energy) / energy_range
150 } else {
151 0.5
152 };
153
154 let marking_factor = (-normalized_energy * self.config.amplification_factor).exp();
157 state.amplitude *= marking_factor;
158 }
159 }
160
161 fn diffusion_operator(&self, population: &mut [AmplitudeState]) {
163 let avg_amplitude: f64 =
165 population.iter().map(|s| s.amplitude).sum::<f64>() / population.len() as f64;
166
167 for state in population.iter_mut() {
169 state.amplitude = 2.0f64.mul_add(avg_amplitude, -state.amplitude);
170 state.amplitude = state.amplitude.max(0.0); }
172
173 let total: f64 = population.iter().map(|s| s.amplitude).sum();
175 if total > 1e-10 {
176 for state in population.iter_mut() {
177 state.amplitude /= total;
178 }
179 }
180 }
181
182 fn amplitude_sampling(
184 &mut self,
185 population: &[AmplitudeState],
186 n_vars: usize,
187 qubo: &Array2<f64>,
188 ) -> Vec<AmplitudeState> {
189 let mut rng = thread_rng();
190 let mut new_population = Vec::new();
191
192 let mut sorted_pop = population.to_vec();
194 sorted_pop.sort_by(|a, b| {
195 a.energy
196 .partial_cmp(&b.energy)
197 .unwrap_or(std::cmp::Ordering::Equal)
198 });
199
200 for state in sorted_pop.iter().take(self.config.elite_count) {
201 new_population.push(state.clone());
202 }
203
204 let cumulative: Vec<f64> = population
206 .iter()
207 .scan(0.0, |acc, s| {
208 *acc += s.amplitude;
209 Some(*acc)
210 })
211 .collect();
212
213 while new_population.len() < self.config.population_size {
214 let r: f64 = rng.gen();
216 let parent_idx = cumulative
217 .iter()
218 .position(|&c| r <= c)
219 .unwrap_or(population.len() - 1);
220
221 let mut offspring = population[parent_idx].state.clone();
223
224 let mutation_prob = 0.1 * (1.0 - population[parent_idx].amplitude);
226 for bit in &mut offspring {
227 if rng.gen_bool(mutation_prob) {
228 *bit = !*bit;
229 }
230 }
231
232 let energy = self.compute_energy(&offspring, qubo);
233 let distance = if let Some(ref best) = self.best_solution {
234 offspring
235 .iter()
236 .zip(best.iter())
237 .filter(|(a, b)| a != b)
238 .count()
239 } else {
240 0
241 };
242
243 new_population.push(AmplitudeState {
244 state: offspring,
245 energy,
246 amplitude: 1.0 / self.config.population_size as f64,
247 distance_to_best: distance,
248 });
249 }
250
251 new_population
252 }
253
254 fn adaptive_adjustment(&mut self, iteration: usize) {
256 if !self.config.adaptive_amplification {
257 return;
258 }
259
260 if self.energy_history.len() > 5 {
262 let last_energy = self.energy_history.last().copied().unwrap_or(f64::INFINITY);
263 let recent_improvement =
264 self.energy_history[self.energy_history.len() - 5] - last_energy;
265
266 if recent_improvement.abs() < 0.01 {
268 self.config.amplification_factor *= 1.1;
269 } else {
270 self.config.amplification_factor *= 0.95;
272 }
273
274 self.config.amplification_factor = self.config.amplification_factor.clamp(1.5, 5.0);
276 }
277 }
278
279 pub fn optimize(&mut self, qubo: &Array2<f64>) -> Result<Vec<HashMap<String, bool>>, String> {
281 let n_vars = qubo.nrows();
282
283 let mut population = self.initialize_population(n_vars, qubo);
285
286 println!("Starting Grover-inspired amplitude amplification...");
287
288 for iteration in 0..self.config.grover_iterations {
289 self.oracle_marking(&mut population);
291
292 self.diffusion_operator(&mut population);
294
295 population = self.amplitude_sampling(&population, n_vars, qubo);
297
298 for state in &population {
300 if state.energy < self.best_energy {
301 self.best_energy = state.energy;
302 self.best_solution = Some(state.state.clone());
303 }
304 }
305
306 self.energy_history.push(self.best_energy);
307
308 self.adaptive_adjustment(iteration);
310
311 if (iteration + 1) % 5 == 0 {
312 println!(
313 "Iteration {}/{}: Best energy = {:.4}, Amplification factor = {:.2}",
314 iteration + 1,
315 self.config.grover_iterations,
316 self.best_energy,
317 self.config.amplification_factor
318 );
319 }
320 }
321
322 let mut results = Vec::new();
324 let mut seen_energies = std::collections::HashSet::new();
325
326 for state in &population {
327 let energy_key = (state.energy * 1000.0) as i64;
329 if seen_energies.insert(energy_key) {
330 let mut solution = HashMap::new();
331 for (i, &bit) in state.state.iter().enumerate() {
332 solution.insert(format!("x{i}"), bit);
333 }
334 results.push(solution);
335 }
336 }
337
338 results.sort_by_key(|sol| {
340 let state: Vec<bool> = (0..n_vars)
341 .map(|i| *sol.get(&format!("x{i}")).unwrap_or(&false))
342 .collect();
343 (self.compute_energy(&state, qubo) * 1000.0) as i64
344 });
345
346 Ok(results)
347 }
348
349 pub fn get_statistics(&self) -> OptimizationStatistics {
351 OptimizationStatistics {
352 best_energy: self.best_energy,
353 iterations: self.energy_history.len(),
354 final_amplification_factor: self.config.amplification_factor,
355 convergence_rate: if self.energy_history.len() > 1 {
356 (self.energy_history[0] - self.best_energy) / self.energy_history.len() as f64
357 } else {
358 0.0
359 },
360 }
361 }
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct OptimizationStatistics {
367 pub best_energy: f64,
368 pub iterations: usize,
369 pub final_amplification_factor: f64,
370 pub convergence_rate: f64,
371}
372
373#[derive(Debug, Clone)]
375pub struct GroverAmplifiedSampler {
376 config: GroverAmplificationConfig,
377}
378
379impl GroverAmplifiedSampler {
380 pub const fn new(config: GroverAmplificationConfig) -> Self {
382 Self { config }
383 }
384}
385
386impl Sampler for GroverAmplifiedSampler {
387 fn run_qubo(
388 &self,
389 problem: &(Array2<f64>, HashMap<String, usize>),
390 shots: usize,
391 ) -> SamplerResult<Vec<SampleResult>> {
392 let (matrix, _var_map) = problem;
393
394 let mut solver = GroverAmplifiedSolver::new(self.config.clone());
396
397 let solutions = solver
399 .optimize(matrix)
400 .map_err(SamplerError::InvalidParameter)?;
401
402 let results: Vec<SampleResult> = solutions
404 .into_iter()
405 .take(shots)
406 .map(|solution| {
407 let state: Vec<bool> = (0..matrix.nrows())
408 .map(|i| *solution.get(&format!("x{i}")).unwrap_or(&false))
409 .collect();
410 let energy = solver.compute_energy(&state, matrix);
411
412 SampleResult {
413 assignments: solution,
414 energy,
415 occurrences: 1,
416 }
417 })
418 .collect();
419
420 Ok(results)
421 }
422
423 fn run_hobo(
424 &self,
425 _problem: &(ArrayD<f64>, HashMap<String, usize>),
426 _shots: usize,
427 ) -> SamplerResult<Vec<SampleResult>> {
428 Err(SamplerError::UnsupportedOperation(
429 "HOBO sampling not yet implemented for Grover-amplified sampler".to_string(),
430 ))
431 }
432}
433
434#[cfg(test)]
435mod tests {
436 use super::*;
437
438 #[test]
439 fn test_grover_solver_creation() {
440 let config = GroverAmplificationConfig::default();
441 let solver = GroverAmplifiedSolver::new(config);
442 assert_eq!(solver.best_energy, f64::INFINITY);
443 }
444
445 #[test]
446 fn test_energy_computation() {
447 let config = GroverAmplificationConfig::default();
448 let solver = GroverAmplifiedSolver::new(config);
449
450 let mut qubo = Array2::zeros((3, 3));
451 qubo[[0, 0]] = -1.0;
452 qubo[[1, 1]] = -1.0;
453 qubo[[2, 2]] = -1.0;
454
455 let state = vec![true, true, false];
456 let energy = solver.compute_energy(&state, &qubo);
457 assert_eq!(energy, -2.0);
458 }
459
460 #[test]
461 fn test_small_problem_optimization() {
462 let config = GroverAmplificationConfig {
463 grover_iterations: 10,
464 population_size: 20,
465 ..Default::default()
466 };
467 let mut solver = GroverAmplifiedSolver::new(config);
468
469 let mut qubo = Array2::zeros((2, 2));
471 qubo[[0, 0]] = 1.0;
472 qubo[[1, 1]] = 1.0;
473
474 let _results = solver
475 .optimize(&qubo)
476 .expect("optimization should succeed for simple problem");
477
478 assert!(solver.best_energy <= 0.1);
480 }
481
482 #[test]
483 fn test_oracle_marking() {
484 let config = GroverAmplificationConfig::default();
485 let solver = GroverAmplifiedSolver::new(config);
486
487 let mut population = vec![
488 AmplitudeState {
489 state: vec![true, false],
490 energy: -2.0,
491 amplitude: 0.5,
492 distance_to_best: 0,
493 },
494 AmplitudeState {
495 state: vec![false, false],
496 energy: 0.0,
497 amplitude: 0.5,
498 distance_to_best: 1,
499 },
500 ];
501
502 solver.oracle_marking(&mut population);
503
504 assert!(population[0].amplitude > population[1].amplitude);
506 }
507
508 #[test]
509 fn test_diffusion_operator() {
510 let config = GroverAmplificationConfig::default();
511 let solver = GroverAmplifiedSolver::new(config);
512
513 let mut population = vec![
514 AmplitudeState {
515 state: vec![true, false],
516 energy: -2.0,
517 amplitude: 0.8,
518 distance_to_best: 0,
519 },
520 AmplitudeState {
521 state: vec![false, false],
522 energy: 0.0,
523 amplitude: 0.2,
524 distance_to_best: 1,
525 },
526 ];
527
528 solver.diffusion_operator(&mut population);
529
530 let total: f64 = population.iter().map(|s| s.amplitude).sum();
532 assert!((total - 1.0).abs() < 0.01);
533 }
534}