Download test_everything.py from poseidon666/quantum_circuit_optimizer: direct link, hf CLI and curl.
- Browser
- Download file 11.4 kB
-
https://huggingface.co/spaces/poseidon666/quantum_circuit_optimizer/resolve/main/test_everything.py
- Command line
-
hf download hf://spaces/poseidon666/quantum_circuit_optimizer/test_everything.py
-
curl -L -o test_everything.py https://huggingface.co/spaces/poseidon666/quantum_circuit_optimizer/resolve/main/test_everything.py
11.4 kB
| # -*- coding: utf-8 -*- | |
| """ | |
| Comprehensive test suite for the Qiskit-backed Quantum Circuit Optimization Environment. | |
| Covers: | |
| 1. Bell State — H → CNOT → fidelity ≈ 1 | |
| 2. GHZ State — H → CNOT(0,1) → CNOT(1,2) → fidelity ≈ 1 | |
| 3. SWAP — verify qubit swap physically changes statevector | |
| 4. REMOVE — add gate → remove gate → state reverts | |
| 5. Budget task — smooth overflow penalty | |
| 6. Noise impact — deeper circuits penalised | |
| 7. STOP — correct terminal reward threshold | |
| 8. Redundancy — repeated gates penalised | |
| """ | |
| import sys | |
| import numpy as np | |
| from server.my_env_environment import ( | |
| QuantumCircuitEnvironment, | |
| compute_statevector, | |
| build_qiskit_circuit, | |
| MAX_QUBITS, | |
| MAX_DEPTH, | |
| ) | |
| from models import ActionType, GateType, QuantumAction | |
| PASS = "\033[92mPASS\033[0m" | |
| FAIL = "\033[91mFAIL\033[0m" | |
| def section(title: str) -> None: | |
| print(f"\n{'='*55}") | |
| print(f" {title}") | |
| print(f"{'='*55}") | |
| def check(cond: bool, msg: str) -> None: | |
| tag = PASS if cond else FAIL | |
| print(f" [{tag}] {msg}") | |
| if not cond: | |
| raise AssertionError(msg) | |
| # --------------------------------------------------------------------------- | |
| # STEP 10 — Test 1: Bell State — H → CNOT → fidelity ≈ 1 | |
| # --------------------------------------------------------------------------- | |
| def test_bell_state(): | |
| section("TEST 1: Bell State — H → CNOT → fidelity ≈ 1") | |
| env = QuantumCircuitEnvironment(seed=42) | |
| obs = env.reset() | |
| print(f" Initial fidelity: {obs.fidelity:.6f}, score: {obs.score:.6f}") | |
| obs = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0])) | |
| print(f" After H(0): fid={obs.fidelity:.6f} reward={obs.reward:+.6f}") | |
| obs = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.CNOT, qubits=[0, 1])) | |
| print(f" After CNOT(0,1): fid={obs.fidelity:.6f} reward={obs.reward:+.6f}") | |
| obs = env.step(QuantumAction(action_type=ActionType.STOP)) | |
| check(obs.fidelity > 0.99, f"Bell fidelity {obs.fidelity:.6f} should be ≈ 1.0") | |
| check(obs.score > 0.5, f"Bell aggregate score {obs.score:.6f} should be > 0.5") | |
| # --------------------------------------------------------------------------- | |
| # STEP 10 — Test 2: GHZ State — H → CNOT(0,1) → CNOT(1,2) → fidelity ≈ 1 | |
| # --------------------------------------------------------------------------- | |
| def test_ghz_state(): | |
| section("TEST 2: GHZ State — H → CNOT(0,1) → CNOT(1,2) → fidelity ≈ 1") | |
| env = QuantumCircuitEnvironment(seed=42) | |
| obs = env.reset() | |
| print(f" Initial fidelity: {obs.fidelity:.6f}") | |
| obs = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0])) | |
| obs = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.CNOT, qubits=[0, 1])) | |
| obs = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.CNOT, qubits=[1, 2])) | |
| print(f" After H+CNOT(0,1)+CNOT(1,2): fid={obs.fidelity:.6f}") | |
| obs = env.step(QuantumAction(action_type=ActionType.STOP)) | |
| check(obs.fidelity > 0.99, f"GHZ fidelity {obs.fidelity:.6f} should be ≈ 1.0") | |
| check(obs.score > 0.5, f"GHZ aggregate score {obs.score:.6f} should be > 0.5") | |
| # --------------------------------------------------------------------------- | |
| # STEP 10 — Test 3: SWAP — verify qubit swap physically changes statevector | |
| # --------------------------------------------------------------------------- | |
| def test_swap_physics(): | |
| section("TEST 3: SWAP — physical qubit routing verification") | |
| # Qiskit uses LITTLE-ENDIAN qubit ordering (qubit 0 = LSB). | |
| # X on qubit 0 sets the LSB → state occupies some basis index. | |
| # After SWAP(0,1) the occupied index must change (qubits exchanged). | |
| gates_x = [{"gate": "X", "qubits": [0]}] | |
| sv_before = compute_statevector(gates_x, num_qubits=2) | |
| gates_swap = [{"gate": "X", "qubits": [0]}, {"gate": "SWAP", "qubits": [0, 1]}] | |
| sv_after = compute_statevector(gates_swap, num_qubits=2) | |
| idx_before = int(np.argmax(np.abs(sv_before))) | |
| idx_after = int(np.argmax(np.abs(sv_after))) | |
| print(f" Before SWAP: dominant index = {idx_before} (|amp|={abs(sv_before[idx_before]):.4f})") | |
| print(f" After SWAP: dominant index = {idx_after} (|amp|={abs(sv_after[idx_after]):.4f})") | |
| check( | |
| abs(sv_before[idx_before]) > 0.99, | |
| f"Before SWAP must be pure basis state; |amp|={abs(sv_before[idx_before]):.4f}" | |
| ) | |
| check( | |
| abs(sv_after[idx_after]) > 0.99, | |
| f"After SWAP must be pure basis state; |amp|={abs(sv_after[idx_after]):.4f}" | |
| ) | |
| check( | |
| idx_before != idx_after, | |
| f"SWAP must change the occupied basis index: before={idx_before}, after={idx_after}" | |
| ) | |
| # Also verify it works correctly inside the RL environment | |
| env = QuantumCircuitEnvironment(seed=42) | |
| env.reset() | |
| env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.X, qubits=[0])) | |
| obs_before_swap = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[1])) | |
| fid_before = obs_before_swap.fidelity | |
| obs_after_swap = env.step(QuantumAction(action_type=ActionType.SWAP, qubits=[0, 1])) | |
| fid_after = obs_after_swap.fidelity | |
| print(f" RL env — fidelity before SWAP: {fid_before:.6f}, after SWAP: {fid_after:.6f}") | |
| check(obs_after_swap.reward is not None, "SWAP action executed without error in RL environment") | |
| check(obs_after_swap.reward < 10.0, "SWAP reward is finite and penalises SWAP count") | |
| # --------------------------------------------------------------------------- | |
| # STEP 10 — Test 4: REMOVE — add gate → remove gate → state reverts | |
| # --------------------------------------------------------------------------- | |
| def test_remove_reverts(): | |
| section("TEST 4: REMOVE — state reverts after gate removal") | |
| env = QuantumCircuitEnvironment(seed=42) | |
| obs_empty = env.reset() | |
| fid_empty = obs_empty.fidelity | |
| sv_empty = compute_statevector([], num_qubits=2) | |
| obs_h = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0])) | |
| fid_h = obs_h.fidelity | |
| print(f" Fidelity (empty): {fid_empty:.6f}") | |
| print(f" Fidelity (after H(0)): {fid_h:.6f}") | |
| check(abs(fid_h - fid_empty) > 1e-6, "H gate must change fidelity from empty state") | |
| obs_rm = env.step(QuantumAction(action_type=ActionType.REMOVE)) | |
| fid_rm = obs_rm.fidelity | |
| sv_rm = compute_statevector([], num_qubits=2) | |
| print(f" Fidelity (after REMOVE): {fid_rm:.6f}") | |
| check( | |
| abs(fid_rm - fid_empty) < 1e-6, | |
| f"After REMOVE fidelity {fid_rm:.6f} should revert to {fid_empty:.6f}" | |
| ) | |
| # Verify statevectors match too (direct Qiskit call) | |
| check( | |
| np.allclose(sv_rm, sv_empty, atol=1e-10), | |
| "Statevector after REMOVE matches original empty statevector" | |
| ) | |
| check(obs_rm.gate_count == 0, "Gate count should be 0 after REMOVE on 1-gate circuit") | |
| # --------------------------------------------------------------------------- | |
| # Original regression tests | |
| # --------------------------------------------------------------------------- | |
| def test_budget_penalty(): | |
| section("TEST 5: Budget Task — smooth overflow penalty") | |
| env = QuantumCircuitEnvironment() | |
| env.reset() | |
| env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0])) | |
| env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.CNOT, qubits=[0, 1])) | |
| obs1 = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.CNOT, qubits=[1, 2])) | |
| s1 = obs1.score | |
| print(f" Score at budget (3 gates): {s1:.4f}") | |
| obs2 = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[2])) | |
| s2 = obs2.score | |
| print(f" Score at overflow (4 gates): {s2:.4f}") | |
| obs3 = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[2])) | |
| s3 = obs3.score | |
| print(f" Score at overflow (5 gates): {s3:.4f}") | |
| check(s1 >= 0.0 and s2 >= 0.0 and s3 >= 0.0, "All budget scores are non-negative") | |
| def test_stop_reward(): | |
| section("TEST 6: STOP — correct terminal reward threshold") | |
| env = QuantumCircuitEnvironment() | |
| # High-score stop → reward should be +0.2 | |
| env.reset() | |
| env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0])) | |
| env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.CNOT, qubits=[0, 1])) | |
| obs = env.step(QuantumAction(action_type=ActionType.STOP)) | |
| print(f" High-score STOP: score={obs.score:.4f} reward={obs.reward:.4f}") | |
| check(obs.done, "Episode must be done after STOP") | |
| # Low-score stop → reward should be -0.2 | |
| env.reset() | |
| obs = env.step(QuantumAction(action_type=ActionType.STOP)) | |
| print(f" Low-score STOP: score={obs.score:.4f} reward={obs.reward:.4f}") | |
| check(obs.done, "Episode must be done after low-score STOP") | |
| check(obs.reward <= 0.0, f"Low-score STOP reward {obs.reward:.4f} should be ≤ 0") | |
| def test_redundancy_penalty(): | |
| section("TEST 7: Redundancy penalty — same gate twice") | |
| env = QuantumCircuitEnvironment() | |
| env.reset() | |
| env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0])) | |
| obs = env.step(QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0])) | |
| print(f" H(0) twice reward: {obs.reward:.4f} (expect negative contribution from redundancy)") | |
| # Reward contains multiple terms; just check it's computed without error | |
| check(obs.reward is not None, "Reward computed for redundant gate") | |
| def test_module_constants(): | |
| section("TEST 8: Module-level performance constants") | |
| from server.my_env_environment import MAX_QUBITS, MAX_STEPS, MAX_DEPTH | |
| check(MAX_QUBITS == 4, f"MAX_QUBITS={MAX_QUBITS}, expected 4") | |
| check(MAX_STEPS == 15, f"MAX_STEPS={MAX_STEPS}, expected 15") | |
| check(MAX_DEPTH == 20, f"MAX_DEPTH={MAX_DEPTH}, expected 20") | |
| print(f" MAX_QUBITS={MAX_QUBITS} MAX_STEPS={MAX_STEPS} MAX_DEPTH={MAX_DEPTH}") | |
| def test_statevector_validation(): | |
| section("TEST 9: Statevector dimension assertion") | |
| for n in (1, 2, 3, 4): | |
| sv = compute_statevector([], num_qubits=n) | |
| check(len(sv) == 2 ** n, f"Empty circuit, {n} qubits → len={len(sv)}, expected {2**n}") | |
| print(f" {n} qubits: len={len(sv)} ✓") | |
| # --------------------------------------------------------------------------- | |
| # Entry point | |
| # --------------------------------------------------------------------------- | |
| def run_all(): | |
| tests = [ | |
| test_bell_state, | |
| test_ghz_state, | |
| test_swap_physics, | |
| test_remove_reverts, | |
| test_budget_penalty, | |
| test_stop_reward, | |
| test_redundancy_penalty, | |
| test_module_constants, | |
| test_statevector_validation, | |
| ] | |
| passed = 0 | |
| failed = 0 | |
| for t in tests: | |
| try: | |
| t() | |
| passed += 1 | |
| except AssertionError as e: | |
| print(f" [FAIL] {e}") | |
| failed += 1 | |
| except Exception as e: | |
| print(f" [ERROR] {type(e).__name__}: {e}") | |
| failed += 1 | |
| section("RESULTS") | |
| print(f" Passed: {passed}/{len(tests)}") | |
| print(f" Failed: {failed}/{len(tests)}") | |
| if failed: | |
| sys.exit(1) | |
| else: | |
| print("\n ALL TESTS PASSED!") | |
| if __name__ == "__main__": | |
| run_all() | |