Skip to content

Commit c622d69

Browse files
Add files via upload
1 parent 024f240 commit c622d69

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

test/validation_tools.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# test/validation_tools.py
2+
"""
3+
Injection-Test Validation Tools
4+
================================
5+
Shared helpers for all injection-style tests:
6+
- TMST Gaussian entanglement (point 4.2 / 4.5)
7+
- Qiskit 2-qubit Bell vs noise (point 4.1 / 4.2)
8+
9+
These functions implement the "inject known signal vs pure noise,
10+
then verify metrics respond correctly" pattern described in the
11+
BasQ conversations.
12+
"""
13+
14+
from __future__ import annotations
15+
import numpy as np
16+
import numpy.typing as npt
17+
18+
# ─────────────────────────────────────────────
19+
# TMST injection helpers
20+
# ─────────────────────────────────────────────
21+
22+
def make_tmst_signal(
23+
T: float,
24+
delta_above: float = 0.3,
25+
omega: float = 1.0,
26+
) -> tuple[float, float]:
27+
"""
28+
Return (r_signal, n_bar) for a squeezing value ABOVE the threshold.
29+
'delta_above' sets how far above r_c we place the signal.
30+
"""
31+
from src.seemps_vortex.tmst_threshold import bose_einstein, critical_squeezing
32+
n_bar = float(bose_einstein(np.array([T]), omega=omega)[0])
33+
rc = float(critical_squeezing(n_bar))
34+
return rc + delta_above, n_bar
35+
36+
37+
def make_tmst_noise(
38+
T: float,
39+
alpha_below: float = 0.5,
40+
omega: float = 1.0,
41+
) -> tuple[float, float]:
42+
"""
43+
Return (r_noise, n_bar) for a squeezing value BELOW the threshold.
44+
'alpha_below' is the fraction of r_c to use (< 1 → separable).
45+
"""
46+
from src.seemps_vortex.tmst_threshold import bose_einstein, critical_squeezing
47+
n_bar = float(bose_einstein(np.array([T]), omega=omega)[0])
48+
rc = float(critical_squeezing(n_bar))
49+
return alpha_below * rc, n_bar
50+
51+
52+
# ─────────────────────────────────────────────
53+
# Qiskit 2-qubit injection helpers
54+
# ─────────────────────────────────────────────
55+
56+
def bell_state_circuit():
57+
"""
58+
Returns a 2-qubit QuantumCircuit preparing |Φ+⟩ = (|00⟩+|11⟩)/√2.
59+
This is the 'maximum entanglement' injection signal.
60+
Requires qiskit.
61+
"""
62+
from qiskit import QuantumCircuit
63+
qc = QuantumCircuit(2)
64+
qc.h(0)
65+
qc.cx(0, 1)
66+
return qc
67+
68+
69+
def product_noise_circuit(seed: int | None = None):
70+
"""
71+
Returns a 2-qubit QuantumCircuit with independent random RX rotations.
72+
This is the 'no entanglement / pure noise' injection.
73+
"""
74+
from qiskit import QuantumCircuit
75+
rng = np.random.default_rng(seed)
76+
qc = QuantumCircuit(2)
77+
qc.rx(rng.uniform(0, np.pi), 0)
78+
qc.rx(rng.uniform(0, np.pi), 1)
79+
return qc
80+
81+
82+
def log_negativity_from_statevector(qc) -> float:
83+
"""
84+
Compute log-negativity E_N for a pure 2-qubit state via partial transpose.
85+
Works directly on a QuantumCircuit (no measurement needed).
86+
"""
87+
from qiskit.quantum_info import Statevector
88+
sv = Statevector.from_instruction(qc)
89+
rho = np.outer(sv.data, sv.data.conj())
90+
91+
# Partial transpose on qubit 1 (reshape → swap indices → reshape back)
92+
rho_TB = rho.reshape(2, 2, 2, 2).transpose(0, 3, 2, 1).reshape(4, 4)
93+
evals = np.linalg.eigvalsh(rho_TB)
94+
negativity = np.sum(np.abs(evals[evals < 0.0]))
95+
EN = np.log2(2.0 * negativity + 1.0)
96+
return float(max(0.0, EN))
97+
98+
99+
def state_fidelity_to_bell(qc) -> float:
100+
"""
101+
Fidelity of the state in qc with the ideal Bell state |Φ+⟩.
102+
"""
103+
from qiskit.quantum_info import Statevector, state_fidelity
104+
ideal = Statevector.from_instruction(bell_state_circuit())
105+
actual = Statevector.from_instruction(qc)
106+
return float(state_fidelity(ideal, actual))

0 commit comments

Comments
 (0)