git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
447 lines
13 KiB
Rust
447 lines
13 KiB
Rust
//! Tests for ruqu_core::simulator — high-level simulator, circuits, shots, reproducibility.
|
|
|
|
use ruqu_core::prelude::*;
|
|
|
|
const EPSILON: f64 = 1e-10;
|
|
|
|
fn approx_eq(a: f64, b: f64) -> bool {
|
|
(a - b).abs() < EPSILON
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Basic circuit construction
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_circuit_new() {
|
|
let circuit = QuantumCircuit::new(3);
|
|
assert_eq!(circuit.num_qubits(), 3);
|
|
assert_eq!(circuit.gate_count(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_add_gates() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.h(0).cnot(0, 1);
|
|
assert_eq!(circuit.num_qubits(), 2);
|
|
assert_eq!(circuit.gate_count(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_chaining() {
|
|
let mut circuit = QuantumCircuit::new(3);
|
|
circuit.h(0).h(1).h(2).cnot(0, 1).cnot(1, 2);
|
|
assert_eq!(circuit.gate_count(), 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_gates_ref() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.h(0).x(0);
|
|
let gates = circuit.gates();
|
|
assert_eq!(gates.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_all_single_qubit_gates() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit
|
|
.h(0)
|
|
.x(0)
|
|
.y(0)
|
|
.z(0)
|
|
.s(0)
|
|
.t(0)
|
|
.rx(0, 0.5)
|
|
.ry(0, 0.5)
|
|
.rz(0, 0.5);
|
|
assert_eq!(circuit.gate_count(), 9);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_two_qubit_gates() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.cnot(0, 1).cz(0, 1).swap(0, 1).rzz(0, 1, 0.5);
|
|
assert_eq!(circuit.gate_count(), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_measure() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.h(0).cnot(0, 1).measure(0).measure(1);
|
|
assert_eq!(circuit.gate_count(), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_measure_all() {
|
|
let mut circuit = QuantumCircuit::new(3);
|
|
circuit.h(0).h(1).h(2).measure_all();
|
|
// measure_all adds a measure for each qubit
|
|
assert!(circuit.gate_count() >= 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_reset() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.x(0).reset(0);
|
|
assert_eq!(circuit.gate_count(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_barrier() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.h(0).barrier().cnot(0, 1);
|
|
assert_eq!(circuit.gate_count(), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_add_gate_directly() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.add_gate(Gate::H(0));
|
|
circuit.add_gate(Gate::CNOT(0, 1));
|
|
assert_eq!(circuit.gate_count(), 2);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Circuit depth
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_circuit_depth_single_gate() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.h(0);
|
|
assert_eq!(circuit.depth(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_depth_parallel_gates() {
|
|
let mut circuit = QuantumCircuit::new(3);
|
|
circuit.h(0).h(1).h(2);
|
|
// All H gates act on different qubits, so depth = 1
|
|
assert!(circuit.depth() >= 1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_depth_sequential() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.h(0).x(0).y(0);
|
|
// All on same qubit, depth = 3
|
|
assert!(circuit.depth() >= 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circuit_depth_mixed() {
|
|
let mut circuit = QuantumCircuit::new(3);
|
|
circuit.h(0).h(1).h(2).cnot(0, 1).cnot(1, 2);
|
|
// H gates parallel (depth 1), then CNOT(0,1) (depth 2), then CNOT(1,2) (depth 3)
|
|
assert!(circuit.depth() >= 2);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Simulator::run
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_simulator_basic() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.h(0).cnot(0, 1);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
assert_eq!(result.metrics.num_qubits, 2);
|
|
assert!(result.metrics.gate_count >= 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_simulator_bell_state_probabilities() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.h(0).cnot(0, 1);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
let probs = result.state.probabilities();
|
|
assert!(approx_eq(probs[0], 0.5)); // |00>
|
|
assert!(approx_eq(probs[1], 0.0)); // |01>
|
|
assert!(approx_eq(probs[2], 0.0)); // |10>
|
|
assert!(approx_eq(probs[3], 0.5)); // |11>
|
|
}
|
|
|
|
#[test]
|
|
fn test_simulator_identity_circuit() {
|
|
// No gates at all: state should remain |0>
|
|
let circuit = QuantumCircuit::new(1);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
let probs = result.state.probabilities();
|
|
assert!(approx_eq(probs[0], 1.0));
|
|
assert!(approx_eq(probs[1], 0.0));
|
|
}
|
|
|
|
#[test]
|
|
fn test_simulator_x_gate() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.x(0);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
let probs = result.state.probabilities();
|
|
assert!(approx_eq(probs[0], 0.0));
|
|
assert!(approx_eq(probs[1], 1.0));
|
|
}
|
|
|
|
#[test]
|
|
fn test_simulator_ghz() {
|
|
let mut circuit = QuantumCircuit::new(3);
|
|
circuit.h(0).cnot(0, 1).cnot(1, 2);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
let probs = result.state.probabilities();
|
|
assert!(approx_eq(probs[0], 0.5));
|
|
assert!(approx_eq(probs[7], 0.5));
|
|
for i in 1..7 {
|
|
assert!(approx_eq(probs[i], 0.0));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_simulator_with_measurement() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.x(0).measure(0);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
assert!(!result.measurements.is_empty());
|
|
// X|0> = |1>, so measurement should be 1
|
|
assert!(result.measurements[0].result);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Simulator::run_with_config (seeded)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_seeded_reproducibility() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.h(0).measure(0);
|
|
|
|
let config = SimConfig {
|
|
seed: Some(42),
|
|
noise: None,
|
|
shots: None,
|
|
};
|
|
|
|
let r1 = Simulator::run_with_config(&circuit, &config).unwrap();
|
|
let r2 = Simulator::run_with_config(&circuit, &config).unwrap();
|
|
|
|
assert_eq!(r1.measurements.len(), r2.measurements.len());
|
|
if !r1.measurements.is_empty() && !r2.measurements.is_empty() {
|
|
assert_eq!(r1.measurements[0].result, r2.measurements[0].result);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_different_seeds_may_differ() {
|
|
// With different seeds and a probabilistic circuit, results may differ
|
|
// (not guaranteed per single run, but validates that config is used)
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.h(0).measure(0);
|
|
|
|
let c1 = SimConfig {
|
|
seed: Some(42),
|
|
noise: None,
|
|
shots: None,
|
|
};
|
|
let c2 = SimConfig {
|
|
seed: Some(99),
|
|
noise: None,
|
|
shots: None,
|
|
};
|
|
|
|
let _r1 = Simulator::run_with_config(&circuit, &c1).unwrap();
|
|
let _r2 = Simulator::run_with_config(&circuit, &c2).unwrap();
|
|
// We just verify both complete without error.
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_no_seed() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.h(0).measure(0);
|
|
|
|
let config = SimConfig {
|
|
seed: None,
|
|
noise: None,
|
|
shots: None,
|
|
};
|
|
|
|
let result = Simulator::run_with_config(&circuit, &config).unwrap();
|
|
assert!(!result.measurements.is_empty());
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Simulator::run_shots
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_run_shots_basic() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.x(0).measure(0);
|
|
|
|
let result = Simulator::run_shots(&circuit, 100, Some(42)).unwrap();
|
|
// X|0> = |1>, every shot should measure 1
|
|
let total: usize = result.counts.values().sum();
|
|
assert_eq!(total, 100);
|
|
// All shots should give outcome |1> -> vec![true]
|
|
let count_one = result.counts.get(&vec![true]).copied().unwrap_or(0);
|
|
assert_eq!(count_one, 100, "All shots should measure |1>");
|
|
}
|
|
|
|
#[test]
|
|
fn test_run_shots_superposition() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.h(0).measure(0);
|
|
|
|
let result = Simulator::run_shots(&circuit, 1000, Some(42)).unwrap();
|
|
let total: usize = result.counts.values().sum();
|
|
assert_eq!(total, 1000);
|
|
|
|
let count_zero = result.counts.get(&vec![false]).copied().unwrap_or(0);
|
|
let count_one = result.counts.get(&vec![true]).copied().unwrap_or(0);
|
|
assert_eq!(count_zero + count_one, 1000);
|
|
|
|
// Expect roughly 50/50 with tolerance
|
|
let ratio = count_zero as f64 / 1000.0;
|
|
assert!(
|
|
ratio > 0.4 && ratio < 0.6,
|
|
"Expected ~50% zeros, got {:.1}%",
|
|
ratio * 100.0
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_run_shots_bell_state() {
|
|
let mut circuit = QuantumCircuit::new(2);
|
|
circuit.h(0).cnot(0, 1).measure(0).measure(1);
|
|
|
|
let result = Simulator::run_shots(&circuit, 500, Some(42)).unwrap();
|
|
let total: usize = result.counts.values().sum();
|
|
assert_eq!(total, 500);
|
|
|
|
// Bell state: only |00> and |11> outcomes
|
|
let count_00 = result.counts.get(&vec![false, false]).copied().unwrap_or(0);
|
|
let count_11 = result.counts.get(&vec![true, true]).copied().unwrap_or(0);
|
|
let count_01 = result.counts.get(&vec![false, true]).copied().unwrap_or(0);
|
|
let count_10 = result.counts.get(&vec![true, false]).copied().unwrap_or(0);
|
|
|
|
assert_eq!(
|
|
count_01 + count_10,
|
|
0,
|
|
"Bell state should never produce |01> or |10>"
|
|
);
|
|
assert_eq!(count_00 + count_11, 500);
|
|
}
|
|
|
|
#[test]
|
|
fn test_run_shots_seeded_reproducibility() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.h(0).measure(0);
|
|
|
|
let r1 = Simulator::run_shots(&circuit, 100, Some(42)).unwrap();
|
|
let r2 = Simulator::run_shots(&circuit, 100, Some(42)).unwrap();
|
|
|
|
assert_eq!(r1.counts, r2.counts);
|
|
}
|
|
|
|
#[test]
|
|
fn test_run_shots_single_shot() {
|
|
let mut circuit = QuantumCircuit::new(1);
|
|
circuit.h(0).measure(0);
|
|
|
|
let result = Simulator::run_shots(&circuit, 1, Some(42)).unwrap();
|
|
let total: usize = result.counts.values().sum();
|
|
assert_eq!(total, 1);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Memory estimation
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_memory_estimate_1_qubit() {
|
|
// 2^1 = 2 complex numbers * 16 bytes (re: f64 + im: f64) = 32
|
|
assert_eq!(QuantumState::estimate_memory(1), 32);
|
|
}
|
|
|
|
#[test]
|
|
fn test_memory_estimate_10_qubits() {
|
|
// 2^10 = 1024 * 16 = 16384
|
|
assert_eq!(QuantumState::estimate_memory(10), 16384);
|
|
}
|
|
|
|
#[test]
|
|
fn test_memory_estimate_scales_exponentially() {
|
|
let m5 = QuantumState::estimate_memory(5);
|
|
let m6 = QuantumState::estimate_memory(6);
|
|
assert_eq!(m6, m5 * 2);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Metrics from simulation result
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_simulation_metrics_qubit_count() {
|
|
let mut circuit = QuantumCircuit::new(5);
|
|
circuit.h(0);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
assert_eq!(result.metrics.num_qubits, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_simulation_metrics_gate_count() {
|
|
let mut circuit = QuantumCircuit::new(3);
|
|
circuit.h(0).cnot(0, 1).cnot(1, 2).x(2);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
assert!(result.metrics.gate_count >= 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_simulation_result_state_vector_length() {
|
|
let mut circuit = QuantumCircuit::new(3);
|
|
circuit.h(0);
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
// 2^3 = 8 probabilities
|
|
let probs = result.state.probabilities();
|
|
assert_eq!(probs.len(), 8);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Complex circuits
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn test_qft_like_circuit() {
|
|
// Simplified QFT-like pattern: H on each, controlled rotations
|
|
let mut circuit = QuantumCircuit::new(3);
|
|
circuit
|
|
.h(0)
|
|
.rz(0, std::f64::consts::FRAC_PI_4)
|
|
.rz(0, std::f64::consts::FRAC_PI_2)
|
|
.h(1)
|
|
.rz(1, std::f64::consts::FRAC_PI_4)
|
|
.h(2)
|
|
.swap(0, 2);
|
|
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
let total: f64 = result.state.probabilities().iter().sum();
|
|
assert!(approx_eq(total, 1.0));
|
|
}
|
|
|
|
#[test]
|
|
fn test_many_gate_circuit() {
|
|
// Stress test: many gates, verify normalization
|
|
let n = 5;
|
|
let mut circuit = QuantumCircuit::new(n);
|
|
for i in 0..n {
|
|
circuit.h(i);
|
|
}
|
|
for i in 0..(n - 1) {
|
|
circuit.cnot(i, i + 1);
|
|
}
|
|
for i in 0..n {
|
|
circuit.rz(i, 0.3 * (i as f64));
|
|
}
|
|
let result = Simulator::run(&circuit).unwrap();
|
|
let total: f64 = result.state.probabilities().iter().sum();
|
|
assert!(approx_eq(total, 1.0));
|
|
}
|