//! Quantum mechanics invariants: any valid circuit must preserve unitarity and normalisation. use proptest::prelude::*; use quantum_bridge_mcp::executor::{CanExecute, LocalSimulator}; use quantum_bridge_mcp::types::{CircuitSource, ShotCount}; proptest! { /// Normalisation: shot counts always sum to exactly the requested number of shots. #[test] fn shot_counts_always_sum_to_requested_shots( shots in 1u32..=1_000u32 ) { let circuit = r#"OPENQASM 3.0; include "stdgates.inc"; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;"#; let sim = LocalSimulator::new(); let result = sim .run(&CircuitSource(circuit.to_string()), ShotCount(shots), false) .unwrap(); let total: u64 = result.counts.values().sum(); prop_assert_eq!(total, shots as u64); } /// Statevector norm is always 1.0 within floating-point tolerance. #[test] fn statevector_norm_is_one( angle in -std::f64::consts::PI..=std::f64::consts::PI ) { let circuit = format!( "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[1] q;\nbit[1] c;\nrx({angle}) q[0];\nc = measure q;" ); let sim = LocalSimulator::new(); let result = sim .run(&CircuitSource(circuit), ShotCount(1), true) .unwrap(); let sv = result.statevector.unwrap(); let norm: f64 = sv.iter().map(|(r, i)| r * r + i * i).sum(); prop_assert!((norm - 1.0).abs() < 1e-9, "norm={norm}"); } /// Bitstrings in counts have correct length (= number of qubits). #[test] fn count_bitstrings_have_correct_length( n_qubits in 1usize..=5usize, shots in 10u32..=100u32 ) { let qubit_decls: String = (0..n_qubits).map(|i| format!("h q[{i}];\n")).collect(); let circuit = format!( "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[{n_qubits}] q;\nbit[{n_qubits}] c;\n{qubit_decls}c = measure q;\n" ); let sim = LocalSimulator::new(); let result = sim .run(&CircuitSource(circuit), ShotCount(shots), false) .unwrap(); for key in result.counts.keys() { prop_assert_eq!( key.len(), n_qubits, "bitstring '{}' has wrong length for {}-qubit circuit", key, n_qubits ); } } }