use crate::error::QuantRS2Error;
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::random::prelude::*;
use scirs2_core::Complex64;
use std::f64::consts::PI;
pub trait AmplitudeOracle {
fn state_preparation(&self) -> Array1<Complex64>;
fn grover_oracle(&self, state: &mut Array1<Complex64>);
fn num_qubits(&self) -> usize;
fn is_good_state(&self, basis_index: usize) -> bool;
}
#[derive(Debug, Clone)]
pub struct GroverOperator {
num_qubits: usize,
}
impl GroverOperator {
pub const fn new(num_qubits: usize) -> Self {
Self { num_qubits }
}
pub fn apply(
&self,
state: &mut Array1<Complex64>,
oracle: &dyn AmplitudeOracle,
) -> Result<(), QuantRS2Error> {
let dim = 1 << self.num_qubits;
if state.len() != dim {
return Err(QuantRS2Error::InvalidInput(format!(
"State dimension {} doesn't match 2^{}",
state.len(),
self.num_qubits
)));
}
oracle.grover_oracle(state);
self.apply_diffusion(state);
Ok(())
}
fn apply_diffusion(&self, state: &mut Array1<Complex64>) {
let avg: Complex64 = state.iter().sum::<Complex64>() / (state.len() as f64);
for amplitude in state.iter_mut() {
*amplitude = Complex64::new(2.0, 0.0) * avg - *amplitude;
}
}
}
#[derive(Debug)]
pub struct CanonicalQAE {
pub num_eval_qubits: usize,
grover_operator: GroverOperator,
}
impl CanonicalQAE {
pub const fn new(num_eval_qubits: usize, num_state_qubits: usize) -> Self {
Self {
num_eval_qubits,
grover_operator: GroverOperator::new(num_state_qubits),
}
}
pub fn estimate(
&self,
oracle: &dyn AmplitudeOracle,
) -> Result<(f64, (f64, f64)), QuantRS2Error> {
let mut state = oracle.state_preparation();
let phase = self.quantum_phase_estimation(&state, oracle)?;
let theta = phase * PI;
let amplitude = (theta / 2.0).sin().powi(2);
let precision = PI / (1 << self.num_eval_qubits) as f64;
let lower_bound = ((theta - precision) / 2.0).sin().powi(2).max(0.0);
let upper_bound = f64::midpoint(theta, precision).sin().powi(2).min(1.0);
Ok((amplitude, (lower_bound, upper_bound)))
}
fn quantum_phase_estimation(
&self,
state: &Array1<Complex64>,
oracle: &dyn AmplitudeOracle,
) -> Result<f64, QuantRS2Error> {
let num_measurements = 1 << self.num_eval_qubits;
let mut phase_estimates = Vec::new();
for k in 0..num_measurements {
let mut temp_state = state.clone();
for _ in 0..k {
self.grover_operator.apply(&mut temp_state, oracle)?;
}
let measurement = self.measure_phase(&temp_state);
phase_estimates.push(measurement);
}
let avg_phase = phase_estimates.iter().sum::<f64>() / phase_estimates.len() as f64;
Ok(avg_phase)
}
fn measure_phase(&self, state: &Array1<Complex64>) -> f64 {
let mut max_amplitude = 0.0;
let mut max_phase = 0.0;
for amp in state {
let magnitude = amp.norm();
if magnitude > max_amplitude {
max_amplitude = magnitude;
max_phase = amp.arg();
}
}
max_phase / (2.0 * PI)
}
}
#[derive(Debug)]
pub struct MaximumLikelihoodAE {
pub schedule: Vec<usize>,
grover_operator: GroverOperator,
}
impl MaximumLikelihoodAE {
pub const fn new(schedule: Vec<usize>, num_qubits: usize) -> Self {
Self {
schedule,
grover_operator: GroverOperator::new(num_qubits),
}
}
pub fn with_exponential_schedule(max_power: usize, num_qubits: usize) -> Self {
let schedule: Vec<usize> = (0..=max_power).map(|k| 1 << k).collect();
Self::new(schedule, num_qubits)
}
pub fn estimate(
&self,
oracle: &dyn AmplitudeOracle,
shots_per_iteration: usize,
) -> Result<(f64, f64), QuantRS2Error> {
let mut observations = Vec::new();
for &num_grover in &self.schedule {
let good_state_count =
self.run_measurements(oracle, num_grover, shots_per_iteration)?;
let success_probability = good_state_count as f64 / shots_per_iteration as f64;
observations.push((num_grover, success_probability));
}
let (estimated_amplitude, fisher_info) = self.maximum_likelihood(&observations)?;
let std_dev = 1.0 / fisher_info.sqrt();
Ok((estimated_amplitude, std_dev))
}
fn run_measurements(
&self,
oracle: &dyn AmplitudeOracle,
num_grover: usize,
shots: usize,
) -> Result<usize, QuantRS2Error> {
let mut good_count = 0;
for _ in 0..shots {
let mut state = oracle.state_preparation();
for _ in 0..num_grover {
self.grover_operator.apply(&mut state, oracle)?;
}
let measurement = self.measure_computational_basis(&state);
if oracle.is_good_state(measurement) {
good_count += 1;
}
}
Ok(good_count)
}
fn measure_computational_basis(&self, state: &Array1<Complex64>) -> usize {
let mut rng = thread_rng();
let random: f64 = rng.random();
let mut cumulative_prob = 0.0;
for (idx, amp) in state.iter().enumerate() {
cumulative_prob += amp.norm_sqr();
if random <= cumulative_prob {
return idx;
}
}
state.len() - 1
}
fn maximum_likelihood(
&self,
observations: &[(usize, f64)],
) -> Result<(f64, f64), QuantRS2Error> {
let mut best_amplitude = 0.0;
let mut best_likelihood = f64::NEG_INFINITY;
const GRID_POINTS: usize = 1000;
for i in 0..=GRID_POINTS {
let a = i as f64 / GRID_POINTS as f64;
let likelihood = self.compute_log_likelihood(a, observations);
if likelihood > best_likelihood {
best_likelihood = likelihood;
best_amplitude = a;
}
}
let fisher_info = self.compute_fisher_information(best_amplitude, observations);
Ok((best_amplitude, fisher_info))
}
fn compute_log_likelihood(&self, amplitude: f64, observations: &[(usize, f64)]) -> f64 {
let theta = (amplitude.sqrt()).asin() * 2.0;
let mut log_likelihood = 0.0;
for &(m, p_obs) in observations {
let p_theory = (2.0f64.mul_add(m as f64, 1.0) * theta / 2.0).sin().powi(2);
log_likelihood += p_obs.mul_add(p_theory.ln(), (1.0 - p_obs) * (1.0 - p_theory).ln());
}
log_likelihood
}
fn compute_fisher_information(&self, amplitude: f64, observations: &[(usize, f64)]) -> f64 {
let theta = (amplitude.sqrt()).asin() * 2.0;
let mut fisher_info = 0.0;
for &(m, _) in observations {
let phase = 2.0f64.mul_add(m as f64, 1.0) * theta / 2.0;
let derivative = 2.0f64.mul_add(m as f64, 1.0) * phase.sin() * phase.cos();
let p = phase.sin().powi(2);
fisher_info += derivative.powi(2) / (p * (1.0 - p)).max(1e-10);
}
fisher_info
}
}
#[derive(Debug)]
pub struct IterativeQAE {
pub target_accuracy: f64,
pub confidence_level: f64,
grover_operator: GroverOperator,
}
impl IterativeQAE {
pub const fn new(target_accuracy: f64, confidence_level: f64, num_qubits: usize) -> Self {
Self {
target_accuracy,
confidence_level,
grover_operator: GroverOperator::new(num_qubits),
}
}
pub fn estimate(&mut self, oracle: &dyn AmplitudeOracle) -> Result<IQAEResult, QuantRS2Error> {
let mut lower_bound = 0.0;
let mut upper_bound = 1.0;
let mut num_oracle_calls = 0;
let mut iteration = 0;
while (upper_bound - lower_bound) > self.target_accuracy {
let k = self.choose_grover_iterations(lower_bound, upper_bound);
let success_count = self.run_adaptive_measurements(oracle, k, 100)?;
let success_rate = success_count as f64 / 100.0;
num_oracle_calls += 100 * (k + 1);
(lower_bound, upper_bound) =
self.update_interval(lower_bound, upper_bound, k, success_rate);
iteration += 1;
}
let estimated_amplitude = f64::midpoint(lower_bound, upper_bound);
Ok(IQAEResult {
amplitude: estimated_amplitude,
lower_bound,
upper_bound,
num_iterations: iteration,
num_oracle_calls,
})
}
fn choose_grover_iterations(&self, lower: f64, upper: f64) -> usize {
let theta_lower = (lower.sqrt()).asin() * 2.0;
let theta_upper = (upper.sqrt()).asin() * 2.0;
let theta_mid = f64::midpoint(theta_lower, theta_upper);
((PI / 2.0) / theta_mid - 0.5).max(0.0) as usize
}
fn run_adaptive_measurements(
&self,
oracle: &dyn AmplitudeOracle,
num_grover: usize,
shots: usize,
) -> Result<usize, QuantRS2Error> {
let mut success_count = 0;
for _ in 0..shots {
let mut state = oracle.state_preparation();
for _ in 0..num_grover {
self.grover_operator.apply(&mut state, oracle)?;
}
let measurement = self.measure_good_state(&state, oracle);
if measurement {
success_count += 1;
}
}
Ok(success_count)
}
fn measure_good_state(&self, state: &Array1<Complex64>, oracle: &dyn AmplitudeOracle) -> bool {
let mut rng = thread_rng();
let random: f64 = rng.random();
let mut cumulative_prob = 0.0;
for (idx, amp) in state.iter().enumerate() {
cumulative_prob += amp.norm_sqr();
if random <= cumulative_prob {
return oracle.is_good_state(idx);
}
}
false
}
fn update_interval(
&self,
lower: f64,
upper: f64,
k: usize,
observed_success_rate: f64,
) -> (f64, f64) {
const GRID_SIZE: usize = 100;
let mut likelihoods = vec![0.0; GRID_SIZE];
let mut max_likelihood = f64::NEG_INFINITY;
for i in 0..GRID_SIZE {
let a = lower + (upper - lower) * i as f64 / (GRID_SIZE - 1) as f64;
let theta = (a.sqrt()).asin() * 2.0;
let p_theory = ((2 * k + 1) as f64 * theta / 2.0).sin().powi(2);
let likelihood = -((observed_success_rate - p_theory).powi(2));
likelihoods[i] = likelihood;
max_likelihood = max_likelihood.max(likelihood);
}
let threshold = max_likelihood - 2.0; let mut new_lower = lower;
let mut new_upper = upper;
for (i, &likelihood) in likelihoods.iter().enumerate() {
if likelihood >= threshold {
let a = lower + (upper - lower) * i as f64 / (GRID_SIZE - 1) as f64;
if a < new_lower || new_lower == lower {
new_lower = a;
}
new_upper = a;
}
}
(new_lower, new_upper)
}
}
#[derive(Debug, Clone)]
pub struct IQAEResult {
pub amplitude: f64,
pub lower_bound: f64,
pub upper_bound: f64,
pub num_iterations: usize,
pub num_oracle_calls: usize,
}
impl IQAEResult {
pub fn interval_width(&self) -> f64 {
self.upper_bound - self.lower_bound
}
pub fn relative_error(&self) -> f64 {
self.interval_width() / self.amplitude.max(1e-10)
}
}
pub struct OptionPricingOracle {
num_qubits: usize,
strike_price: f64,
risk_free_rate: f64,
volatility: f64,
time_to_maturity: f64,
}
impl OptionPricingOracle {
pub const fn new(
num_qubits: usize,
strike_price: f64,
risk_free_rate: f64,
volatility: f64,
time_to_maturity: f64,
) -> Self {
Self {
num_qubits,
strike_price,
risk_free_rate,
volatility,
time_to_maturity,
}
}
fn payoff(&self, price_index: usize) -> f64 {
let s_t = self.index_to_price(price_index);
(s_t - self.strike_price).max(0.0)
}
fn index_to_price(&self, index: usize) -> f64 {
let num_levels = 1 << self.num_qubits;
let normalized = index as f64 / num_levels as f64;
let z = (normalized * 6.0) - 3.0; let s_0 = self.strike_price; s_0 * 0.5f64
.mul_add(-self.volatility.powi(2), self.risk_free_rate)
.mul_add(
self.time_to_maturity,
self.volatility * self.time_to_maturity.sqrt() * z,
)
.exp()
}
}
impl AmplitudeOracle for OptionPricingOracle {
fn state_preparation(&self) -> Array1<Complex64> {
let dim = 1 << self.num_qubits;
let mut state = Array1::<Complex64>::zeros(dim);
let amplitude = Complex64::new(1.0 / (dim as f64).sqrt(), 0.0);
state.fill(amplitude);
state
}
fn grover_oracle(&self, state: &mut Array1<Complex64>) {
for (idx, amplitude) in state.iter_mut().enumerate() {
if self.is_good_state(idx) {
*amplitude = -*amplitude; }
}
}
fn num_qubits(&self) -> usize {
self.num_qubits
}
fn is_good_state(&self, basis_index: usize) -> bool {
self.payoff(basis_index) > 0.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grover_operator() {
let grover = GroverOperator::new(2);
struct TestOracle;
impl AmplitudeOracle for TestOracle {
fn state_preparation(&self) -> Array1<Complex64> {
Array1::from_vec(vec![
Complex64::new(0.5, 0.0),
Complex64::new(0.5, 0.0),
Complex64::new(0.5, 0.0),
Complex64::new(0.5, 0.0),
])
}
fn grover_oracle(&self, state: &mut Array1<Complex64>) {
state[3] = -state[3]; }
fn num_qubits(&self) -> usize {
2
}
fn is_good_state(&self, basis_index: usize) -> bool {
basis_index == 3
}
}
let oracle = TestOracle;
let mut state = oracle.state_preparation();
grover
.apply(&mut state, &oracle)
.expect("Grover operator application should succeed");
assert!(state[3].norm() > 0.5);
}
#[test]
fn test_mlae_exponential_schedule() {
let mlae = MaximumLikelihoodAE::with_exponential_schedule(3, 2);
assert_eq!(mlae.schedule, vec![1, 2, 4, 8]);
}
#[test]
fn test_iqae_interval_update() {
let iqae = IterativeQAE::new(0.01, 0.95, 2);
let (lower, upper) = iqae.update_interval(0.0, 1.0, 1, 0.5);
assert!(upper - lower < 1.0);
assert!(lower >= 0.0 && upper <= 1.0);
}
#[test]
fn test_option_pricing_oracle() {
let oracle = OptionPricingOracle::new(3, 100.0, 0.05, 0.2, 1.0);
assert_eq!(oracle.num_qubits(), 3);
let state = oracle.state_preparation();
assert_eq!(state.len(), 8);
let norm: f64 = state.iter().map(|c| c.norm_sqr()).sum();
assert!((norm - 1.0).abs() < 1e-6);
}
}