1use crate::error::QuantRS2Error;
15use scirs2_core::ndarray::{Array1, Array2};
16use scirs2_core::random::prelude::*;
17use scirs2_core::Complex64;
18use std::f64::consts::PI;
19
20pub trait AmplitudeOracle {
25 fn state_preparation(&self) -> Array1<Complex64>;
27
28 fn grover_oracle(&self, state: &mut Array1<Complex64>);
30
31 fn num_qubits(&self) -> usize;
33
34 fn is_good_state(&self, basis_index: usize) -> bool;
36}
37
38#[derive(Debug, Clone)]
45pub struct GroverOperator {
46 num_qubits: usize,
47}
48
49impl GroverOperator {
50 pub const fn new(num_qubits: usize) -> Self {
52 Self { num_qubits }
53 }
54
55 pub fn apply(
57 &self,
58 state: &mut Array1<Complex64>,
59 oracle: &dyn AmplitudeOracle,
60 ) -> Result<(), QuantRS2Error> {
61 let dim = 1 << self.num_qubits;
62 if state.len() != dim {
63 return Err(QuantRS2Error::InvalidInput(format!(
64 "State dimension {} doesn't match 2^{}",
65 state.len(),
66 self.num_qubits
67 )));
68 }
69
70 oracle.grover_oracle(state);
72
73 self.apply_diffusion(state);
75
76 Ok(())
77 }
78
79 fn apply_diffusion(&self, state: &mut Array1<Complex64>) {
81 let avg: Complex64 = state.iter().sum::<Complex64>() / (state.len() as f64);
83
84 for amplitude in state.iter_mut() {
86 *amplitude = Complex64::new(2.0, 0.0) * avg - *amplitude;
87 }
88 }
89}
90
91#[derive(Debug)]
93pub struct CanonicalQAE {
94 pub num_eval_qubits: usize,
96 grover_operator: GroverOperator,
98}
99
100impl CanonicalQAE {
101 pub const fn new(num_eval_qubits: usize, num_state_qubits: usize) -> Self {
107 Self {
108 num_eval_qubits,
109 grover_operator: GroverOperator::new(num_state_qubits),
110 }
111 }
112
113 pub fn estimate(
117 &self,
118 oracle: &dyn AmplitudeOracle,
119 ) -> Result<(f64, (f64, f64)), QuantRS2Error> {
120 let mut state = oracle.state_preparation();
122
123 let phase = self.quantum_phase_estimation(&state, oracle)?;
125
126 let theta = phase * PI;
128 let amplitude = (theta / 2.0).sin().powi(2);
129
130 let precision = PI / (1 << self.num_eval_qubits) as f64;
132 let lower_bound = ((theta - precision) / 2.0).sin().powi(2).max(0.0);
133 let upper_bound = f64::midpoint(theta, precision).sin().powi(2).min(1.0);
134
135 Ok((amplitude, (lower_bound, upper_bound)))
136 }
137
138 fn quantum_phase_estimation(
140 &self,
141 state: &Array1<Complex64>,
142 oracle: &dyn AmplitudeOracle,
143 ) -> Result<f64, QuantRS2Error> {
144 let num_measurements = 1 << self.num_eval_qubits;
148 let mut phase_estimates = Vec::new();
149
150 for k in 0..num_measurements {
151 let mut temp_state = state.clone();
152
153 for _ in 0..k {
155 self.grover_operator.apply(&mut temp_state, oracle)?;
156 }
157
158 let measurement = self.measure_phase(&temp_state);
160 phase_estimates.push(measurement);
161 }
162
163 let avg_phase = phase_estimates.iter().sum::<f64>() / phase_estimates.len() as f64;
165
166 Ok(avg_phase)
167 }
168
169 fn measure_phase(&self, state: &Array1<Complex64>) -> f64 {
171 let mut max_amplitude = 0.0;
173 let mut max_phase = 0.0;
174
175 for amp in state {
176 let magnitude = amp.norm();
177 if magnitude > max_amplitude {
178 max_amplitude = magnitude;
179 max_phase = amp.arg();
180 }
181 }
182
183 max_phase / (2.0 * PI)
184 }
185}
186
187#[derive(Debug)]
194pub struct MaximumLikelihoodAE {
195 pub schedule: Vec<usize>,
197 grover_operator: GroverOperator,
199}
200
201impl MaximumLikelihoodAE {
202 pub const fn new(schedule: Vec<usize>, num_qubits: usize) -> Self {
204 Self {
205 schedule,
206 grover_operator: GroverOperator::new(num_qubits),
207 }
208 }
209
210 pub fn with_exponential_schedule(max_power: usize, num_qubits: usize) -> Self {
212 let schedule: Vec<usize> = (0..=max_power).map(|k| 1 << k).collect();
213 Self::new(schedule, num_qubits)
214 }
215
216 pub fn estimate(
218 &self,
219 oracle: &dyn AmplitudeOracle,
220 shots_per_iteration: usize,
221 ) -> Result<(f64, f64), QuantRS2Error> {
222 let mut observations = Vec::new();
223
224 for &num_grover in &self.schedule {
226 let good_state_count =
227 self.run_measurements(oracle, num_grover, shots_per_iteration)?;
228 let success_probability = good_state_count as f64 / shots_per_iteration as f64;
229 observations.push((num_grover, success_probability));
230 }
231
232 let (estimated_amplitude, fisher_info) = self.maximum_likelihood(&observations)?;
234
235 let std_dev = 1.0 / fisher_info.sqrt();
237
238 Ok((estimated_amplitude, std_dev))
239 }
240
241 fn run_measurements(
243 &self,
244 oracle: &dyn AmplitudeOracle,
245 num_grover: usize,
246 shots: usize,
247 ) -> Result<usize, QuantRS2Error> {
248 let mut good_count = 0;
249
250 for _ in 0..shots {
251 let mut state = oracle.state_preparation();
252
253 for _ in 0..num_grover {
255 self.grover_operator.apply(&mut state, oracle)?;
256 }
257
258 let measurement = self.measure_computational_basis(&state);
260 if oracle.is_good_state(measurement) {
261 good_count += 1;
262 }
263 }
264
265 Ok(good_count)
266 }
267
268 fn measure_computational_basis(&self, state: &Array1<Complex64>) -> usize {
270 let mut rng = thread_rng();
271 let random: f64 = rng.gen();
272
273 let mut cumulative_prob = 0.0;
274 for (idx, amp) in state.iter().enumerate() {
275 cumulative_prob += amp.norm_sqr();
276 if random <= cumulative_prob {
277 return idx;
278 }
279 }
280
281 state.len() - 1
282 }
283
284 fn maximum_likelihood(
286 &self,
287 observations: &[(usize, f64)],
288 ) -> Result<(f64, f64), QuantRS2Error> {
289 let mut best_amplitude = 0.0;
291 let mut best_likelihood = f64::NEG_INFINITY;
292
293 const GRID_POINTS: usize = 1000;
294 for i in 0..=GRID_POINTS {
295 let a = i as f64 / GRID_POINTS as f64;
296 let likelihood = self.compute_log_likelihood(a, observations);
297
298 if likelihood > best_likelihood {
299 best_likelihood = likelihood;
300 best_amplitude = a;
301 }
302 }
303
304 let fisher_info = self.compute_fisher_information(best_amplitude, observations);
306
307 Ok((best_amplitude, fisher_info))
308 }
309
310 fn compute_log_likelihood(&self, amplitude: f64, observations: &[(usize, f64)]) -> f64 {
312 let theta = (amplitude.sqrt()).asin() * 2.0;
313 let mut log_likelihood = 0.0;
314
315 for &(m, p_obs) in observations {
316 let p_theory = (2.0f64.mul_add(m as f64, 1.0) * theta / 2.0).sin().powi(2);
318
319 log_likelihood += p_obs.mul_add(p_theory.ln(), (1.0 - p_obs) * (1.0 - p_theory).ln());
321 }
322
323 log_likelihood
324 }
325
326 fn compute_fisher_information(&self, amplitude: f64, observations: &[(usize, f64)]) -> f64 {
328 let theta = (amplitude.sqrt()).asin() * 2.0;
329 let mut fisher_info = 0.0;
330
331 for &(m, _) in observations {
332 let phase = 2.0f64.mul_add(m as f64, 1.0) * theta / 2.0;
334 let derivative = 2.0f64.mul_add(m as f64, 1.0) * phase.sin() * phase.cos();
335
336 let p = phase.sin().powi(2);
337 fisher_info += derivative.powi(2) / (p * (1.0 - p)).max(1e-10);
338 }
339
340 fisher_info
341 }
342}
343
344#[derive(Debug)]
351pub struct IterativeQAE {
352 pub target_accuracy: f64,
354 pub confidence_level: f64,
356 grover_operator: GroverOperator,
358}
359
360impl IterativeQAE {
361 pub const fn new(target_accuracy: f64, confidence_level: f64, num_qubits: usize) -> Self {
363 Self {
364 target_accuracy,
365 confidence_level,
366 grover_operator: GroverOperator::new(num_qubits),
367 }
368 }
369
370 pub fn estimate(&mut self, oracle: &dyn AmplitudeOracle) -> Result<IQAEResult, QuantRS2Error> {
372 let mut lower_bound = 0.0;
373 let mut upper_bound = 1.0;
374 let mut num_oracle_calls = 0;
375 let mut iteration = 0;
376
377 while (upper_bound - lower_bound) > self.target_accuracy {
378 let k = self.choose_grover_iterations(lower_bound, upper_bound);
380
381 let success_count = self.run_adaptive_measurements(oracle, k, 100)?;
383 let success_rate = success_count as f64 / 100.0;
384 num_oracle_calls += 100 * (k + 1);
385
386 (lower_bound, upper_bound) =
388 self.update_interval(lower_bound, upper_bound, k, success_rate);
389
390 iteration += 1;
391 }
392
393 let estimated_amplitude = f64::midpoint(lower_bound, upper_bound);
394
395 Ok(IQAEResult {
396 amplitude: estimated_amplitude,
397 lower_bound,
398 upper_bound,
399 num_iterations: iteration,
400 num_oracle_calls,
401 })
402 }
403
404 fn choose_grover_iterations(&self, lower: f64, upper: f64) -> usize {
406 let theta_lower = (lower.sqrt()).asin() * 2.0;
407 let theta_upper = (upper.sqrt()).asin() * 2.0;
408 let theta_mid = f64::midpoint(theta_lower, theta_upper);
409
410 ((PI / 2.0) / theta_mid - 0.5).max(0.0) as usize
413 }
414
415 fn run_adaptive_measurements(
417 &self,
418 oracle: &dyn AmplitudeOracle,
419 num_grover: usize,
420 shots: usize,
421 ) -> Result<usize, QuantRS2Error> {
422 let mut success_count = 0;
423
424 for _ in 0..shots {
425 let mut state = oracle.state_preparation();
426
427 for _ in 0..num_grover {
428 self.grover_operator.apply(&mut state, oracle)?;
429 }
430
431 let measurement = self.measure_good_state(&state, oracle);
432 if measurement {
433 success_count += 1;
434 }
435 }
436
437 Ok(success_count)
438 }
439
440 fn measure_good_state(&self, state: &Array1<Complex64>, oracle: &dyn AmplitudeOracle) -> bool {
442 let mut rng = thread_rng();
443 let random: f64 = rng.gen();
444
445 let mut cumulative_prob = 0.0;
446 for (idx, amp) in state.iter().enumerate() {
447 cumulative_prob += amp.norm_sqr();
448 if random <= cumulative_prob {
449 return oracle.is_good_state(idx);
450 }
451 }
452
453 false
454 }
455
456 fn update_interval(
458 &self,
459 lower: f64,
460 upper: f64,
461 k: usize,
462 observed_success_rate: f64,
463 ) -> (f64, f64) {
464 const GRID_SIZE: usize = 100;
468 let mut likelihoods = vec![0.0; GRID_SIZE];
469 let mut max_likelihood = f64::NEG_INFINITY;
470
471 for i in 0..GRID_SIZE {
472 let a = lower + (upper - lower) * i as f64 / (GRID_SIZE - 1) as f64;
473 let theta = (a.sqrt()).asin() * 2.0;
474 let p_theory = ((2 * k + 1) as f64 * theta / 2.0).sin().powi(2);
475
476 let likelihood = -((observed_success_rate - p_theory).powi(2));
478 likelihoods[i] = likelihood;
479 max_likelihood = max_likelihood.max(likelihood);
480 }
481
482 let threshold = max_likelihood - 2.0; let mut new_lower = lower;
485 let mut new_upper = upper;
486
487 for (i, &likelihood) in likelihoods.iter().enumerate() {
488 if likelihood >= threshold {
489 let a = lower + (upper - lower) * i as f64 / (GRID_SIZE - 1) as f64;
490 if a < new_lower || new_lower == lower {
491 new_lower = a;
492 }
493 new_upper = a;
494 }
495 }
496
497 (new_lower, new_upper)
498 }
499}
500
501#[derive(Debug, Clone)]
503pub struct IQAEResult {
504 pub amplitude: f64,
506 pub lower_bound: f64,
508 pub upper_bound: f64,
510 pub num_iterations: usize,
512 pub num_oracle_calls: usize,
514}
515
516impl IQAEResult {
517 pub fn interval_width(&self) -> f64 {
519 self.upper_bound - self.lower_bound
520 }
521
522 pub fn relative_error(&self) -> f64 {
524 self.interval_width() / self.amplitude.max(1e-10)
525 }
526}
527
528pub struct OptionPricingOracle {
533 num_qubits: usize,
534 strike_price: f64,
535 risk_free_rate: f64,
536 volatility: f64,
537 time_to_maturity: f64,
538}
539
540impl OptionPricingOracle {
541 pub const fn new(
543 num_qubits: usize,
544 strike_price: f64,
545 risk_free_rate: f64,
546 volatility: f64,
547 time_to_maturity: f64,
548 ) -> Self {
549 Self {
550 num_qubits,
551 strike_price,
552 risk_free_rate,
553 volatility,
554 time_to_maturity,
555 }
556 }
557
558 fn payoff(&self, price_index: usize) -> f64 {
560 let s_t = self.index_to_price(price_index);
562 (s_t - self.strike_price).max(0.0)
563 }
564
565 fn index_to_price(&self, index: usize) -> f64 {
567 let num_levels = 1 << self.num_qubits;
568 let normalized = index as f64 / num_levels as f64;
569
570 let z = (normalized * 6.0) - 3.0; let s_0 = self.strike_price; s_0 * 0.5f64
574 .mul_add(-self.volatility.powi(2), self.risk_free_rate)
575 .mul_add(
576 self.time_to_maturity,
577 self.volatility * self.time_to_maturity.sqrt() * z,
578 )
579 .exp()
580 }
581}
582
583impl AmplitudeOracle for OptionPricingOracle {
584 fn state_preparation(&self) -> Array1<Complex64> {
585 let dim = 1 << self.num_qubits;
586 let mut state = Array1::<Complex64>::zeros(dim);
587
588 let amplitude = Complex64::new(1.0 / (dim as f64).sqrt(), 0.0);
590 state.fill(amplitude);
591
592 state
593 }
594
595 fn grover_oracle(&self, state: &mut Array1<Complex64>) {
596 for (idx, amplitude) in state.iter_mut().enumerate() {
597 if self.is_good_state(idx) {
598 *amplitude = -*amplitude; }
600 }
601 }
602
603 fn num_qubits(&self) -> usize {
604 self.num_qubits
605 }
606
607 fn is_good_state(&self, basis_index: usize) -> bool {
608 self.payoff(basis_index) > 0.0
609 }
610}
611
612#[cfg(test)]
613mod tests {
614 use super::*;
615
616 #[test]
617 fn test_grover_operator() {
618 let grover = GroverOperator::new(2);
619
620 struct TestOracle;
622 impl AmplitudeOracle for TestOracle {
623 fn state_preparation(&self) -> Array1<Complex64> {
624 Array1::from_vec(vec![
625 Complex64::new(0.5, 0.0),
626 Complex64::new(0.5, 0.0),
627 Complex64::new(0.5, 0.0),
628 Complex64::new(0.5, 0.0),
629 ])
630 }
631
632 fn grover_oracle(&self, state: &mut Array1<Complex64>) {
633 state[3] = -state[3]; }
635
636 fn num_qubits(&self) -> usize {
637 2
638 }
639 fn is_good_state(&self, basis_index: usize) -> bool {
640 basis_index == 3
641 }
642 }
643
644 let oracle = TestOracle;
645 let mut state = oracle.state_preparation();
646
647 grover
648 .apply(&mut state, &oracle)
649 .expect("Grover operator application should succeed");
650
651 assert!(state[3].norm() > 0.5);
653 }
654
655 #[test]
656 fn test_mlae_exponential_schedule() {
657 let mlae = MaximumLikelihoodAE::with_exponential_schedule(3, 2);
658
659 assert_eq!(mlae.schedule, vec![1, 2, 4, 8]);
660 }
661
662 #[test]
663 fn test_iqae_interval_update() {
664 let iqae = IterativeQAE::new(0.01, 0.95, 2);
665
666 let (lower, upper) = iqae.update_interval(0.0, 1.0, 1, 0.5);
667
668 assert!(upper - lower < 1.0);
670 assert!(lower >= 0.0 && upper <= 1.0);
671 }
672
673 #[test]
674 fn test_option_pricing_oracle() {
675 let oracle = OptionPricingOracle::new(3, 100.0, 0.05, 0.2, 1.0);
676
677 assert_eq!(oracle.num_qubits(), 3);
678
679 let state = oracle.state_preparation();
680 assert_eq!(state.len(), 8);
681
682 let norm: f64 = state.iter().map(|c| c.norm_sqr()).sum();
684 assert!((norm - 1.0).abs() < 1e-6);
685 }
686}