use std::f64::consts::PI;
use rand::{rngs::SmallRng, SeedableRng};
use qvass::{Circuit, Gate, QuantumSimulator, QubitError};
fn build_oracle(n_qubits: u8, winning_state: usize) -> Result<Circuit, QubitError> {
let mut oracle_circuit = Circuit::new(n_qubits);
let all_qubits: Vec<u8> = (0..n_qubits).collect();
for i in 0..n_qubits {
if (winning_state >> i) & 1 == 0 {
oracle_circuit.add_gate(Gate::not(), [i])?;
}
}
let controlled_z = Gate::phase_radians(PI).multi_control(n_qubits - 1);
oracle_circuit.add_gate(controlled_z, all_qubits.clone())?;
for i in 0..n_qubits {
if (winning_state >> i) & 1 == 0 {
oracle_circuit.add_gate(Gate::not(), [i])?;
}
}
Ok(oracle_circuit)
}
fn build_diffuser(n_qubits: u8) -> Result<Circuit, QubitError> {
let mut diffuser_circuit = Circuit::new(n_qubits);
let all_qubits: Vec<u8> = (0..n_qubits).collect();
let zero_oracle = build_oracle(n_qubits, 0)?;
for i in 0..n_qubits {
diffuser_circuit.add_gate(Gate::hadamard(), [i])?;
}
diffuser_circuit.add_gate(zero_oracle.into_gate(), all_qubits.clone())?;
for i in 0..n_qubits {
diffuser_circuit.add_gate(Gate::hadamard(), [i])?;
}
Ok(diffuser_circuit)
}
fn main() -> Result<(), QubitError> {
const N_QUBITS: u8 = 4;
const WINNING_STATE: usize = 5;
let n_items = 1 << N_QUBITS;
let num_iterations = (PI / 4.0 * (n_items as f64).sqrt()).round() as usize;
println!("Grover's Search for {N_QUBITS} qubits ({n_items} items)");
println!("Searching for marked state: |{WINNING_STATE}>");
println!("Optimal number of iterations: {num_iterations}");
println!("\n=== Part 1: Step-by-Step Walkthrough ===");
let mut sim = QuantumSimulator::new(N_QUBITS);
let all_qubits: Vec<u8> = (0..N_QUBITS).collect();
let oracle = build_oracle(N_QUBITS, WINNING_STATE)?;
let diffuser = build_diffuser(N_QUBITS)?;
for i in 0..N_QUBITS {
sim.add_gate(Gate::hadamard(), [i])?;
}
sim.init_state(0);
sim.run(); println!("\n--- 1. State after Initial Superposition ---");
println!("{}", sim.state());
sim.add_gate(oracle.clone().into_gate(), all_qubits.clone())?;
sim.init_state(0);
sim.run();
println!("\n--- 2. State after Oracle (amplitude of |101> is flipped) ---");
println!("{}", sim.state());
sim.add_gate(diffuser.clone().into_gate(), all_qubits.clone())?;
sim.init_state(0);
sim.run();
println!("\n--- 3. State after 1st Grover Iteration (amplitude of |101> is amplified) ---");
println!("{}", sim.state());
println!("\n=== Part 2: State After Full Amplification ===");
if num_iterations > 1 {
for _ in 1..num_iterations {
sim.add_gate(oracle.clone().into_gate(), all_qubits.clone())?;
sim.add_gate(diffuser.clone().into_gate(), all_qubits.clone())?;
}
}
sim.init_state(0);
sim.run();
println!("\n--- State after {num_iterations} Grover Iterations ---");
println!("{}", sim.state());
println!("\n=== Part 3: Statistical Analysis ===");
println!("Running simulation 1000 times to find success rate...");
let mut rng = SmallRng::seed_from_u64(42);
let mut success_count = 0;
let n_runs = 1000;
for _ in 0..n_runs {
sim.init_state(0);
sim.run();
let outcome = sim.measure(&mut rng);
if outcome == WINNING_STATE {
success_count += 1;
}
}
let success_rate = (success_count as f64 / n_runs as f64) * 100.0;
println!("\nFound the correct state {success_count} times out of {n_runs} runs.");
println!("Success rate: {success_rate:.1}%");
assert!(success_rate > 85.0, "Success rate should be very high.");
Ok(())
}