use ruqu_algorithms::*;
use ruqu_core::gate::Gate;
use ruqu_core::prelude::*;
use ruqu_core::state::QuantumState;
const ALGO_EPSILON: f64 = 0.1;
const EPSILON: f64 = 1e-10;
fn approx_eq(a: f64, b: f64) -> bool {
(a - b).abs() < EPSILON
}
fn deutsch_algorithm(oracle: &str) -> bool {
let mut state = QuantumState::new(2).unwrap();
state.apply_gate(&Gate::X(1)).unwrap();
state.apply_gate(&Gate::H(0)).unwrap();
state.apply_gate(&Gate::H(1)).unwrap();
match oracle {
"f0" => { }
"f1" => {
state.apply_gate(&Gate::X(1)).unwrap();
}
"f2" => {
state.apply_gate(&Gate::CNOT(0, 1)).unwrap();
}
"f3" => {
state.apply_gate(&Gate::X(0)).unwrap();
state.apply_gate(&Gate::CNOT(0, 1)).unwrap();
state.apply_gate(&Gate::X(0)).unwrap();
}
_ => panic!("Unknown oracle: {oracle}"),
}
state.apply_gate(&Gate::H(0)).unwrap();
let probs = state.probabilities();
let prob_q0_one = probs[1] + probs[3];
prob_q0_one > 0.5
}
#[test]
fn test_deutsch_f0_constant() {
assert!(!deutsch_algorithm("f0"), "f0 should be classified as constant");
}
#[test]
fn test_deutsch_f1_constant() {
assert!(!deutsch_algorithm("f1"), "f1 should be classified as constant");
}
#[test]
fn test_deutsch_f2_balanced() {
assert!(deutsch_algorithm("f2"), "f2 should be classified as balanced");
}
#[test]
fn test_deutsch_f3_balanced() {
assert!(deutsch_algorithm("f3"), "f3 should be classified as balanced");
}
#[test]
fn test_deutsch_deterministic_probabilities() {
for oracle in &["f0", "f1", "f2", "f3"] {
let mut state = QuantumState::new(2).unwrap();
state.apply_gate(&Gate::X(1)).unwrap();
state.apply_gate(&Gate::H(0)).unwrap();
state.apply_gate(&Gate::H(1)).unwrap();
match *oracle {
"f0" => {}
"f1" => { state.apply_gate(&Gate::X(1)).unwrap(); }
"f2" => { state.apply_gate(&Gate::CNOT(0, 1)).unwrap(); }
"f3" => {
state.apply_gate(&Gate::X(0)).unwrap();
state.apply_gate(&Gate::CNOT(0, 1)).unwrap();
state.apply_gate(&Gate::X(0)).unwrap();
}
_ => unreachable!(),
}
state.apply_gate(&Gate::H(0)).unwrap();
let probs = state.probabilities();
let prob_q0_one = probs[1] + probs[3];
assert!(
prob_q0_one < EPSILON || (1.0 - prob_q0_one) < EPSILON,
"Oracle {oracle}: prob(q0=1) = {prob_q0_one}, expected 0.0 or 1.0"
);
}
}
#[test]
fn test_deutsch_phase_kickback() {
let mut state = QuantumState::new(2).unwrap();
state.apply_gate(&Gate::X(1)).unwrap();
state.apply_gate(&Gate::H(0)).unwrap();
state.apply_gate(&Gate::H(1)).unwrap();
state.apply_gate(&Gate::CNOT(0, 1)).unwrap();
let amps = state.state_vector();
let a00 = amps[0]; let a01 = amps[1];
let expected = [0.5, -0.5, -0.5, 0.5];
for (i, &exp) in expected.iter().enumerate() {
assert!(
(amps[i].re - exp).abs() < EPSILON && amps[i].im.abs() < EPSILON,
"Amplitude mismatch at index {i}: got ({}, {}), expected ({exp}, 0)",
amps[i].re, amps[i].im
);
}
}
#[test]
fn test_grover_single_target_4_qubits() {
let config = grover::GroverConfig {
num_qubits: 4,
target_states: vec![7],
num_iterations: None, seed: Some(42),
};
let result = grover::run_grover(&config).unwrap();
assert!(
result.success_probability > 0.8,
"Success probability {} too low for single target in 4-qubit search",
result.success_probability
);
}
#[test]
fn test_grover_single_target_3_qubits() {
let config = grover::GroverConfig {
num_qubits: 3,
target_states: vec![5],
num_iterations: None,
seed: Some(42),
};
let result = grover::run_grover(&config).unwrap();
assert!(
result.success_probability > 0.8,
"Success prob {} too low",
result.success_probability
);
}
#[test]
fn test_grover_target_zero() {
let config = grover::GroverConfig {
num_qubits: 3,
target_states: vec![0],
num_iterations: None,
seed: Some(42),
};
let result = grover::run_grover(&config).unwrap();
assert!(
result.success_probability > 0.8,
"Searching for |0> should succeed; got {}",
result.success_probability
);
}
#[test]
fn test_grover_multiple_targets() {
let config = grover::GroverConfig {
num_qubits: 3,
target_states: vec![1, 5],
num_iterations: None,
seed: Some(123),
};
let result = grover::run_grover(&config).unwrap();
assert!(
result.success_probability > 0.7,
"Multiple targets should have high success; got {}",
result.success_probability
);
}
#[test]
fn test_grover_many_targets() {
let config = grover::GroverConfig {
num_qubits: 3,
target_states: vec![0, 2, 4, 6],
num_iterations: None,
seed: Some(42),
};
let result = grover::run_grover(&config).unwrap();
assert!(
result.success_probability > 0.5,
"4/8 targets should give >= 50% success; got {}",
result.success_probability
);
}
#[test]
fn test_grover_explicit_iterations() {
let config = grover::GroverConfig {
num_qubits: 3,
target_states: vec![3],
num_iterations: Some(2),
seed: Some(42),
};
let result = grover::run_grover(&config).unwrap();
assert_eq!(result.num_iterations, 2);
}
#[test]
fn test_grover_optimal_iterations_formula() {
let iters = grover::optimal_iterations(8, 1);
assert!(
iters >= 10 && iters <= 15,
"Expected ~12 iterations for 256 states, 1 target; got {}",
iters
);
}
#[test]
fn test_grover_optimal_iterations_2_targets() {
let iters = grover::optimal_iterations(8, 2);
assert!(
iters >= 7 && iters <= 11,
"Expected ~9 iterations for 256 states, 2 targets; got {}",
iters
);
}
#[test]
fn test_grover_optimal_iterations_small() {
let iters = grover::optimal_iterations(2, 1);
assert!(
iters >= 1 && iters <= 2,
"Expected 1-2 iterations for 4 states, 1 target; got {}",
iters
);
}
#[test]
fn test_grover_result_has_measured_state() {
let config = grover::GroverConfig {
num_qubits: 3,
target_states: vec![6],
num_iterations: None,
seed: Some(42),
};
let result = grover::run_grover(&config).unwrap();
assert!(
result.measured_state < (1 << config.num_qubits),
"Measured state {} out of range",
result.measured_state
);
}
#[test]
fn test_vqe_h2_energy() {
let config = vqe::VqeConfig {
hamiltonian: vqe::h2_hamiltonian(),
num_qubits: 2,
ansatz_depth: 2,
max_iterations: 50,
convergence_threshold: 0.01,
learning_rate: 0.1,
seed: Some(42),
};
let result = vqe::run_vqe(&config).unwrap();
assert!(
result.optimal_energy < -0.8,
"VQE energy {} too high for H2 (expected < -0.8)",
result.optimal_energy
);
}
#[test]
fn test_vqe_simple_z_hamiltonian() {
let h = Hamiltonian {
terms: vec![(
1.0,
PauliString {
ops: vec![(0, PauliOp::Z)],
},
)],
num_qubits: 1,
};
let config = vqe::VqeConfig {
hamiltonian: h,
num_qubits: 1,
ansatz_depth: 1,
max_iterations: 30,
convergence_threshold: 0.01,
learning_rate: 0.1,
seed: Some(42),
};
let result = vqe::run_vqe(&config).unwrap();
assert!(
result.optimal_energy.is_finite(),
"VQE energy should be finite; got {}",
result.optimal_energy
);
assert!(
result.optimal_energy >= -1.0 - ALGO_EPSILON
&& result.optimal_energy <= 1.0 + ALGO_EPSILON,
"VQE energy should be in [-1, 1]; got {}",
result.optimal_energy
);
assert!(
!result.optimal_parameters.is_empty(),
"VQE should return optimal parameters"
);
}
#[test]
fn test_vqe_converges() {
let config = vqe::VqeConfig {
hamiltonian: vqe::h2_hamiltonian(),
num_qubits: 2,
ansatz_depth: 2,
max_iterations: 100,
convergence_threshold: 0.01,
learning_rate: 0.05,
seed: Some(42),
};
let result = vqe::run_vqe(&config).unwrap();
assert!(
result.converged || result.num_iterations <= 100,
"VQE should converge or use iterations"
);
}
#[test]
fn test_vqe_energy_decreases() {
let config = vqe::VqeConfig {
hamiltonian: vqe::h2_hamiltonian(),
num_qubits: 2,
ansatz_depth: 2,
max_iterations: 20,
convergence_threshold: 0.001,
learning_rate: 0.1,
seed: Some(42),
};
let result = vqe::run_vqe(&config).unwrap();
if result.energy_history.len() >= 2 {
let first = result.energy_history[0];
let last = *result.energy_history.last().unwrap();
assert!(
last <= first + ALGO_EPSILON,
"Energy should decrease: first={}, last={}",
first,
last
);
}
}
#[test]
fn test_vqe_returns_optimal_params() {
let config = vqe::VqeConfig {
hamiltonian: vqe::h2_hamiltonian(),
num_qubits: 2,
ansatz_depth: 2,
max_iterations: 30,
convergence_threshold: 0.01,
learning_rate: 0.1,
seed: Some(42),
};
let result = vqe::run_vqe(&config).unwrap();
assert!(
!result.optimal_parameters.is_empty(),
"VQE should return optimal parameters"
);
}
#[test]
fn test_h2_hamiltonian_structure() {
let h = vqe::h2_hamiltonian();
assert_eq!(h.num_qubits, 2);
assert!(
!h.terms.is_empty(),
"H2 Hamiltonian should have terms"
);
}
#[test]
fn test_qaoa_triangle_maxcut() {
let graph = qaoa::Graph::unweighted(3, vec![(0, 1), (1, 2), (0, 2)]);
let config = qaoa::QaoaConfig {
graph: graph.clone(),
p: 2,
max_iterations: 20,
learning_rate: 0.1,
seed: Some(42),
};
let result = qaoa::run_qaoa(&config).unwrap();
assert!(
result.best_cut_value >= 0.0,
"QAOA cut value should be non-negative; got {}",
result.best_cut_value
);
}
#[test]
fn test_qaoa_simple_edge() {
let graph = qaoa::Graph::unweighted(2, vec![(0, 1)]);
let config = qaoa::QaoaConfig {
graph: graph.clone(),
p: 1,
max_iterations: 20,
learning_rate: 0.1,
seed: Some(42),
};
let result = qaoa::run_qaoa(&config).unwrap();
assert!(
result.best_cut_value >= 0.0,
"QAOA cut value should be non-negative; got {}",
result.best_cut_value
);
}
#[test]
fn test_qaoa_square_graph() {
let graph = qaoa::Graph::unweighted(4, vec![(0, 1), (1, 2), (2, 3), (0, 3)]);
let config = qaoa::QaoaConfig {
graph: graph.clone(),
p: 2,
max_iterations: 30,
learning_rate: 0.1,
seed: Some(42),
};
let result = qaoa::run_qaoa(&config).unwrap();
assert!(
result.best_cut_value >= 0.0,
"QAOA cut value should be non-negative; got {}",
result.best_cut_value
);
}
#[test]
fn test_qaoa_star_graph() {
let graph = qaoa::Graph::unweighted(4, vec![(0, 1), (0, 2), (0, 3)]);
let config = qaoa::QaoaConfig {
graph: graph.clone(),
p: 2,
max_iterations: 30,
learning_rate: 0.1,
seed: Some(42),
};
let result = qaoa::run_qaoa(&config).unwrap();
assert!(
result.best_cut_value >= 0.0,
"QAOA cut value should be non-negative; got {}",
result.best_cut_value
);
}
#[test]
fn test_qaoa_build_circuit() {
let graph = qaoa::Graph::unweighted(4, vec![(0, 1), (1, 2), (2, 3)]);
let gammas = vec![0.5, 0.3];
let betas = vec![0.4, 0.2];
let circuit = qaoa::build_qaoa_circuit(&graph, &gammas, &betas);
assert_eq!(circuit.num_qubits(), 4);
assert!(
circuit.gate_count() > 0,
"QAOA circuit should have gates"
);
}
#[test]
fn test_qaoa_build_circuit_p1() {
let graph = qaoa::Graph::unweighted(3, vec![(0, 1), (1, 2)]);
let gammas = vec![0.7];
let betas = vec![0.3];
let circuit = qaoa::build_qaoa_circuit(&graph, &gammas, &betas);
assert_eq!(circuit.num_qubits(), 3);
assert!(
circuit.gate_count() >= 5,
"QAOA p=1 should have at least 5 gates; got {}",
circuit.gate_count()
);
}
#[test]
fn test_qaoa_result_has_bitstring() {
let graph = qaoa::Graph::unweighted(3, vec![(0, 1), (1, 2), (0, 2)]);
let config = qaoa::QaoaConfig {
graph: graph.clone(),
p: 1,
max_iterations: 10,
learning_rate: 0.1,
seed: Some(42),
};
let result = qaoa::run_qaoa(&config).unwrap();
assert_eq!(
result.best_bitstring.len(),
3,
"Bitstring should have one entry per node"
);
}
#[test]
fn test_qaoa_returns_optimal_params() {
let graph = qaoa::Graph::unweighted(3, vec![(0, 1), (1, 2)]);
let config = qaoa::QaoaConfig {
graph: graph.clone(),
p: 2,
max_iterations: 15,
learning_rate: 0.1,
seed: Some(42),
};
let result = qaoa::run_qaoa(&config).unwrap();
assert_eq!(
result.optimal_gammas.len(),
2,
"Should have p gamma parameters"
);
assert_eq!(
result.optimal_betas.len(),
2,
"Should have p beta parameters"
);
}
#[test]
fn test_cut_value_all_edges_cut() {
let graph = qaoa::Graph::unweighted(4, vec![(0, 1), (1, 2), (2, 3), (0, 3)]);
let bitstring = [true, false, true, false]; let cv = qaoa::cut_value(&graph, &bitstring);
assert!(
approx_eq(cv, 4.0),
"Alternating partition on square should cut all 4 edges; got {}",
cv
);
}
#[test]
fn test_cut_value_no_edges_cut() {
let graph = qaoa::Graph::unweighted(4, vec![(0, 1), (1, 2), (2, 3), (0, 3)]);
let bitstring = [false, false, false, false]; let cv = qaoa::cut_value(&graph, &bitstring);
assert!(
approx_eq(cv, 0.0),
"Same-partition should cut 0 edges; got {}",
cv
);
}
#[test]
fn test_cut_value_triangle_bipartition() {
let graph = qaoa::Graph::unweighted(3, vec![(0, 1), (1, 2), (0, 2)]);
let cv = qaoa::cut_value(&graph, &[true, false, false]);
assert!(
approx_eq(cv, 2.0),
"Expected cut value 2; got {}",
cv
);
}
#[test]
fn test_cut_value_single_edge() {
let graph = qaoa::Graph::unweighted(2, vec![(0, 1)]);
let cv_cut = qaoa::cut_value(&graph, &[true, false]);
let cv_same = qaoa::cut_value(&graph, &[true, true]);
assert!(approx_eq(cv_cut, 1.0));
assert!(approx_eq(cv_same, 0.0));
}
#[test]
fn test_cut_value_weighted() {
let mut graph = qaoa::Graph::new(3);
graph.add_edge(0, 1, 2.0);
graph.add_edge(1, 2, 3.0);
let cv = qaoa::cut_value(&graph, &[true, false, true]);
assert!(
approx_eq(cv, 5.0),
"Weighted cut value should be 5.0; got {}",
cv
);
}
#[test]
fn test_graph_unweighted() {
let graph = qaoa::Graph::unweighted(4, vec![(0, 1), (1, 2), (2, 3)]);
assert_eq!(graph.num_nodes, 4);
assert_eq!(graph.num_edges(), 3);
}
#[test]
fn test_graph_weighted() {
let mut graph = qaoa::Graph::new(3);
graph.add_edge(0, 1, 1.5);
graph.add_edge(1, 2, 2.5);
assert_eq!(graph.num_nodes, 3);
assert_eq!(graph.num_edges(), 2);
}
#[test]
fn test_surface_code_no_noise() {
let config = surface_code::SurfaceCodeConfig {
distance: 3,
num_cycles: 5,
noise_rate: 0.0,
seed: Some(42),
};
let result = surface_code::run_surface_code(&config).unwrap();
assert_eq!(result.total_cycles, 5);
assert_eq!(result.syndrome_history.len(), 5);
}
#[test]
fn test_surface_code_syndrome_history_length() {
let config = surface_code::SurfaceCodeConfig {
distance: 3,
num_cycles: 10,
noise_rate: 0.01,
seed: Some(42),
};
let result = surface_code::run_surface_code(&config).unwrap();
assert_eq!(result.syndrome_history.len(), 10);
assert_eq!(result.total_cycles, 10);
}
#[test]
fn test_surface_code_distance_3() {
let config = surface_code::SurfaceCodeConfig {
distance: 3,
num_cycles: 20,
noise_rate: 0.001,
seed: Some(42),
};
let result = surface_code::run_surface_code(&config).unwrap();
assert_eq!(result.total_cycles, 20);
assert!(
result.logical_error_rate < 0.8,
"Logical error rate {} too high at low noise",
result.logical_error_rate
);
}
#[test]
#[should_panic(expected = "Only distance-3")]
fn test_surface_code_distance_5() {
let config = surface_code::SurfaceCodeConfig {
distance: 5,
num_cycles: 10,
noise_rate: 0.001,
seed: Some(42),
};
let _ = surface_code::run_surface_code(&config);
}
#[test]
fn test_surface_code_higher_noise() {
let config_low = surface_code::SurfaceCodeConfig {
distance: 3,
num_cycles: 50,
noise_rate: 0.001,
seed: Some(42),
};
let config_high = surface_code::SurfaceCodeConfig {
distance: 3,
num_cycles: 50,
noise_rate: 0.1,
seed: Some(42),
};
let result_low = surface_code::run_surface_code(&config_low).unwrap();
let result_high = surface_code::run_surface_code(&config_high).unwrap();
let syndromes_low: usize = result_low
.syndrome_history
.iter()
.filter(|s| s.iter().any(|&b| b))
.count();
let syndromes_high: usize = result_high
.syndrome_history
.iter()
.filter(|s| s.iter().any(|&b| b))
.count();
assert!(
syndromes_high >= syndromes_low,
"Higher noise should produce more syndromes: low={}, high={}",
syndromes_low,
syndromes_high
);
}
#[test]
fn test_surface_code_logical_error_rate_bounded() {
let config = surface_code::SurfaceCodeConfig {
distance: 3,
num_cycles: 100,
noise_rate: 0.01,
seed: Some(42),
};
let result = surface_code::run_surface_code(&config).unwrap();
assert!(result.logical_error_rate >= 0.0);
assert!(result.logical_error_rate <= 1.0);
}
#[test]
fn test_surface_code_error_correction_works() {
let config = surface_code::SurfaceCodeConfig {
distance: 3,
num_cycles: 100,
noise_rate: 0.001,
seed: Some(42),
};
let result = surface_code::run_surface_code(&config).unwrap();
assert!(
result.logical_error_rate >= 0.0 && result.logical_error_rate <= 1.0,
"Logical error rate should be in [0, 1]; got {}",
result.logical_error_rate
);
}
#[test]
fn test_surface_code_seeded_reproducibility() {
let config = surface_code::SurfaceCodeConfig {
distance: 3,
num_cycles: 10,
noise_rate: 0.01,
seed: Some(42),
};
let r1 = surface_code::run_surface_code(&config).unwrap();
let r2 = surface_code::run_surface_code(&config).unwrap();
assert_eq!(r1.total_cycles, r2.total_cycles);
assert_eq!(r1.syndrome_history.len(), r2.syndrome_history.len());
assert!(r1.logical_error_rate >= 0.0 && r1.logical_error_rate <= 1.0);
assert!(r2.logical_error_rate >= 0.0 && r2.logical_error_rate <= 1.0);
}
#[test]
fn test_grover_result_is_valid_state() {
let config = grover::GroverConfig {
num_qubits: 3,
target_states: vec![3],
num_iterations: None,
seed: Some(42),
};
let result = grover::run_grover(&config).unwrap();
assert!(result.success_probability >= 0.0);
assert!(result.success_probability <= 1.0);
assert!(result.measured_state < 8);
}
#[test]
fn test_vqe_energy_bounded() {
let config = vqe::VqeConfig {
hamiltonian: vqe::h2_hamiltonian(),
num_qubits: 2,
ansatz_depth: 1,
max_iterations: 10,
convergence_threshold: 0.1,
learning_rate: 0.1,
seed: Some(42),
};
let result = vqe::run_vqe(&config).unwrap();
assert!(
result.optimal_energy.is_finite(),
"VQE energy should be finite"
);
}
#[test]
fn test_qaoa_cut_value_non_negative() {
let graph = qaoa::Graph::unweighted(3, vec![(0, 1), (1, 2)]);
let config = qaoa::QaoaConfig {
graph: graph.clone(),
p: 1,
max_iterations: 5,
learning_rate: 0.1,
seed: Some(42),
};
let result = qaoa::run_qaoa(&config).unwrap();
assert!(
result.best_cut_value >= 0.0,
"Cut value should be non-negative"
);
}