A physics-accurate simulator for spin dynamics, T₁/T₂ relaxation, pulse sequences, inhomogeneous ensemble behaviour, Rabi oscillations, Ramsey interference, open quantum systems, and two-qubit entanglement — built in Python with a Streamlit interface for interactive exploration and experimental data fitting.
- Physics Background
- The Bloch Vector
- The Bloch Equations
- T₁ and T₂ Relaxation
- Larmor Precession
- Pulse Sequences
- Inhomogeneous Broadening and T₂*
- The Hahn Echo and Refocusing
- CPMG Multi-Echo Trains
- Rabi Oscillations
- Ramsey Interference
- Open Quantum Systems and the Lindblad Master Equation
- Two-Qubit Systems and Entanglement
- Noise and Entanglement Decay
- Project Structure
- Installation
- Running the App
- Running the Tests
- Prototype Walkthrough
- Module Reference
- References
A single spin-½ particle in a magnetic field is described by its magnetisation vector M = (Mₓ, Mᵧ, M_z). In thermal equilibrium M points along the applied field (+z by convention). A radio-frequency pulse tips the spin into the transverse plane, after which the vector precesses and decays back toward equilibrium. All single-qubit physics in this simulator is built around tracking how this three-component vector evolves in time.
The full phenomenological equations of motion are:
dMx/dt = γ(M × B)_x - Mx / T₂
dMy/dt = γ(M × B)_y - My / T₂
dMz/dt = γ(M × B)_z - (Mz - M₀) / T₁
where γ is the gyromagnetic ratio, B is the applied field vector, M₀ is the thermal equilibrium magnetisation, T₁ is the longitudinal relaxation time, and T₂ is the transverse relaxation time.
For a static field B = (0, 0, B₀) the cross product gives precession
around the z-axis at the Larmor frequency ω₀ = γB₀, superimposed on
relaxation. This simulator integrates these equations numerically using
scipy.integrate.solve_ivp with an adaptive Runge-Kutta (RK45) solver.
These two timescales govern fundamentally different physical processes.
T₁ - Longitudinal (spin-lattice) relaxation. The z-component of magnetisation recovers toward M₀ as energy is exchanged between the spin system and its environment. Recovery from zero follows:
Mz(t) = M₀ · (1 - exp(-t / T₁))
T₁ is always greater than or equal to T₂.
T₂ - Transverse (spin-spin) relaxation. The transverse components Mₓ and Mᵧ decay as spins lose phase coherence with one another. The envelope is:
|M⊥(t)| = M₀ · exp(-t / T₂)
T₂ sets the intrinsic coherence lifetime. It is irreversible — no pulse sequence can recover it.
A spin tipped into the transverse plane precesses at the Larmor frequency ω₀ = γB₀. The analytic solution with T₂ decay is:
Mx(t) = M₀ · cos(ω₀t) · exp(-t / T₂)
My(t) = -M₀ · sin(ω₀t) · exp(-t / T₂)
The sign of Mᵧ follows from the M × B cross-product convention with B along +z. This is the free induction decay (FID) for a single homogeneous spin.
Radio-frequency pulses rotate the Bloch vector by a precise angle around a chosen axis. In the hard-pulse approximation (pulse duration negligible compared to T₁, T₂, and the Larmor period) the rotation is exact and implemented via SO(3) rotation matrices:
- π/2 pulse — tips the spin from equilibrium (+z) into the transverse plane
- π pulse — inverts (z → -z) or flips the phase of a transverse spin
A pulse sequence chains alternating pulses and free evolution segments. Each segment is integrated with the full Bloch equations and the results are concatenated to produce the complete time-domain signal.
In real samples, local field variations mean each spin has a slightly different Larmor frequency ω_i. After a π/2 pulse the spins fan out in the transverse plane as they accumulate different phases — a process called dephasing.
For a Gaussian distribution of frequency offsets with standard deviation σ, the ensemble-averaged FID envelope is:
|⟨M⊥⟩(t)| = M₀ · exp(-t / T₂) · exp(-σ²t² / 2)
The Gaussian factor decays much faster than T₂ alone. The apparent coherence time T₂* satisfies:
1 / T₂* ≈ 1 / T₂ + σ
This is why FID signals in NMR and quantum sensing decay so rapidly. Measuring the intrinsic T₂ requires a refocusing pulse sequence.
The Hahn echo (1950) separates T₂ from T₂*. The sequence is:
π/2 pulse → free evolve τ → π pulse → free evolve τ → echo at t = 2τ
After the π/2 pulse, spins with different frequencies fan out. The π pulse reverses each spin's accumulated phase. After another τ all spins reconverge — this is the echo. Static field inhomogeneity is fully refocused. Only irreversible T₂ decay remains. The echo amplitude is:
|M_echo(2τ)| = M₀ · exp(-2τ / T₂)
independent of σ. By measuring echo amplitude as a function of τ, one can extract the intrinsic T₂ directly.
The Carr-Purcell-Meiboom-Gill (CPMG) sequence extends the Hahn echo to multiple refocusing pulses:
π/2 → [τ → π → τ → echo] × N
The k-th echo appears at t = 2kτ with amplitude:
A_k = M₀ · exp(-2kτ / T₂)
CPMG is the standard technique in NMR relaxometry and MRI for T₂ measurement. The Meiboom-Gill modification corrects for systematic pulse imperfections that would otherwise accumulate across the echo train.
When a resonant drive of amplitude Ω (the Rabi frequency) is applied, the spin oscillates between ground and excited states at the effective frequency:
Ω_eff = √(Ω² + Δ²)
where Δ is the detuning from resonance. The excited-state population is:
P↑(t) = (Ω / Ω_eff)² · sin²(Ω_eff · t / 2)
At resonance (Δ = 0), P↑ reaches 1 at the π-pulse time t_π = π / Ω. The chevron pattern — P↑ plotted as a two-dimensional heatmap over (t, Δ) — is a key characterisation tool in superconducting and spin qubit platforms.
Ramsey interferometry is the standard technique for measuring qubit frequency and T₂*. The sequence is:
π/2 → free evolve T → π/2 → measure
During free evolution the qubit accumulates phase at the detuning frequency Δ. The final population is:
P↑(T) = 0.5 + 0.5 · cos(Δ · T) · exp(-T / T₂)
The oscillation frequency gives the detuning; the envelope gives T₂*. The minimum detectable frequency shift (sensitivity) scales as:
δf_min = 1 / (2π · T₂* · √N_shots)
For a qubit coupled to an environment, the density matrix ρ evolves according to the Lindblad master equation:
dρ/dt = -i[H, ρ] + Σ_k γ_k · D[L_k]ρ
where D[L]ρ = LρL† - ½{L†L, ρ} is the dissipator for Lindblad operator L_k and rate γ_k. This formalism captures:
- Amplitude damping (T₁): L = σ₋, γ = 1/T₁
- Pure dephasing (T₂): L = σ_z, γ = 1/(2T₂) - 1/(4T₁)
- Depolarising noise: three Pauli operators each at γ_dep/4
The Bloch vector components extracted from ρ exactly reproduce the Bloch equations in the absence of driving, confirming consistency. The density matrix formulation is essential for mixed states, non-trivial initial conditions, and the two-qubit extensions below.
The two-qubit Hilbert space ℋ_A ⊗ ℋ_B is four-dimensional, spanned by the computational basis |00⟩, |01⟩, |10⟩, |11⟩. The maximally entangled Bell states form an orthonormal basis:
|Φ+⟩ = (|00⟩ + |11⟩) / √2 |Φ-⟩ = (|00⟩ - |11⟩) / √2
|Ψ+⟩ = (|01⟩ + |10⟩) / √2 |Ψ-⟩ = (|01⟩ - |10⟩) / √2
Entanglement is quantified by several independent measures implemented here:
| Measure | Formula | Range |
|---|---|---|
| Concurrence (Wootters) | C = max(0, λ₁ - λ₂ - λ₃ - λ₄) | [0, 1] |
| Entanglement entropy | S(ρ_A) = -Tr(ρ_A log₂ ρ_A) | [0, 1] ebit |
| Entanglement of formation | E_F = h((1+√(1-C²))/2) | [0, 1] |
| Negativity | N = Σ | λᵢ⁻ |
| Logarithmic negativity | E_N = log₂(‖ρ^{T_B}‖₁) | [0, 1] |
| CHSH value | B = Tr(B̂ρ) with optimal settings | [0, 2√2] |
Classical correlations satisfy B ≤ 2. Entangled states can reach the Tsirelson bound B = 2√2 ≈ 2.828.
The Schmidt decomposition of a pure bipartite state |ψ⟩ = Σ_k λ_k |α_k⟩ ⊗ |β_k⟩ gives Schmidt rank 1 for separable states and rank 2 for entangled states. The entanglement entropy equals S(ρ_A) = S(ρ_B) = -Σ λ_k² log₂(λ_k²) for any pure bipartite state.
Entanglement decays under decoherence differently from classical correlations, and the specific noise channel — not just its strength — determines which states survive.
T₁/T₂ effects on Bell states. Under independent amplitude damping on each qubit, all four Bell states lose entanglement. The concurrence decays from 1 toward 0. Under pure dephasing, |Φ±⟩ and |Ψ±⟩ decay differently.
Entanglement sudden death (ESD). Under local amplitude damping, the concurrence can reach exactly zero in finite time t_ESD, even though the single-qubit purity Tr(ρ_A²) is still above 0.5 at that moment. This is qualitatively different from classical correlations, which decay only asymptotically. ESD was first predicted by Yu and Eberly (2004) and demonstrated experimentally in 2007.
Correlated vs local dephasing. |Φ+⟩ = (|00⟩ + |11⟩)/√2 is an eigenstate of σ_z ⊗ σ_z. Correlated ZZ noise — both qubits seeing the same bath fluctuation — therefore leaves its concurrence exactly at 1: the state lies in a decoherence-free subspace. Independent (local) dephasing, which is not diagonal in the same basis, destroys it completely. This connects directly to hardware: in superconducting processors, correlated flux noise can be engineered to protect certain entangled states.
The two-qubit Lindblad master equation used here is:
dρ/dt = -i[H, ρ] + Σ_k γ_k · D[L_k]ρ
with Lindblad operators for: local amplitude damping (qubit A), local amplitude damping (qubit B), local dephasing (A), local dephasing (B), local depolarising (A), local depolarising (B), and correlated ZZ dephasing L_ZZ = √γ_ZZ · (σ_z ⊗ σ_z).
spin-coherence-simulator/
│
├── README.md
├── requirements.txt
├── app.py # Streamlit frontend — no physics here
│
├── src/
│ ├── __init__.py
│ ├── core.py # Bloch ODE solver + analytic spin solutions
│ ├── sequences.py # Pulse sequences: Hahn echo, CPMG
│ ├── ensemble.py # Inhomogeneous spin ensemble
│ ├── rabi.py # Rabi oscillations, chevron, resonance scan
│ ├── ramsey.py # Ramsey fringes, fitting, sensitivity, detuning scan
│ ├── density_matrix.py # Single-qubit Lindblad master equation
│ ├── fitting.py # Parameter extraction via curve_fit
│ ├── visualization.py # 3D Bloch sphere rendering
│ │
│ └── two_qubit/ # Two-qubit quantum module
│ ├── __init__.py
│ ├── states.py # Basis, Bell states, tensor products
│ ├── gates.py # Single/two-qubit gates, circuit runner, XX sweep
│ ├── entanglement.py # Six entanglement measures + CHSH
│ ├── lindblad2q.py # Two-qubit Lindblad ODE + ESD + noise models
│ └── visualization.py # Density matrix heatmaps, Bloch pairs, ESD plots
│
├── tests/
│ ├── test_core.py # Prototype 1: T₂ decay (18 tests)
│ ├── test_bloch.py # Prototype 2: Bloch precession (25 tests)
│ ├── test_bloch_full.py # Prototype 3: Full Bloch equations (35 tests)
│ ├── test_sequences.py # Prototype 4: Pulse sequences (35 tests)
│ └── test_ensemble.py # Prototype 5: Ensemble (29 tests)
│
├── examples/
│ ├── run_prototype1.py # Simple T₂ decay
│ ├── run_prototype2.py # Bloch vector + Larmor precession
│ ├── run_prototype3.py # Full T₁/T₂ relaxation
│ ├── run_prototype4.py # Hahn echo + CPMG
│ └── run_prototype5.py # Inhomogeneous ensemble + FID vs echo
│
└── assets/
└── *.png # Generated output figures
Requirements: Python 3.10 or later.
git clone https://github.com/your-username/spin-coherence-simulator.git
cd spin-coherence-simulator
pip install -r requirements.txtNo compiled extensions or GPU dependencies. Runs on any platform.
streamlit run app.pyOpens at http://localhost:8501. At the top of the sidebar, a Mode radio
button switches between the two independent sections of the app:
- ⚛️ Single Qubit — all original single-spin experiments plus density matrix
- 🔗 Two Qubits — Bell states, gates, entanglement, noise and ESD
The Simulation tab runs physics and plots results. The Fitting tab extracts T₂, T₁, σ, or β from experimental or synthetic data.
Each test file is self-contained and prints pass/fail to stdout.
python tests/test_core.py
python tests/test_bloch.py
python tests/test_bloch_full.py
python tests/test_sequences.py
python tests/test_ensemble.pyOr with pytest:
pytest tests/ -v142 tests across 5 suites. Every test verifies a physics invariant
directly — not just that the code runs, but that results are correct:
|M⊥|(T₂) = 1/e, echo amplitude = exp(-2τ/T₂), equilibrium state
produces zero derivative, fitted T₂ matches input to within 5–10%.
The codebase was built in stages. Each examples/run_prototypeN.py
script is standalone and generates the corresponding figures saved in
assets/. Reading them in order traces the full physics development.
Script: examples/run_prototype1.py
Tests: tests/test_core.py — 18 tests
The entry point. Models single-spin coherence as a pure exponential decay:
L(t) = exp(-t / T₂)
Establishes the time_axis and simulate_simple_coherence functions and
the testing pattern used throughout the project. Verified: L(0) = 1 exactly,
L(T₂) = 1/e, monotone decrease, correct exception on invalid input.
Script: examples/run_prototype2.py
Tests: tests/test_bloch.py — 25 tests
Extends to the full three-component Bloch vector with analytic precession and
T₂ decay. Introduces bloch_precession, a 4-panel time series, and a 3D
Bloch sphere trajectory rendered with a plasma colormap (bright = early time,
faded = late). The sphere shows the spin spiralling inward as coherence decays.
Script: examples/run_prototype3.py
Tests: tests/test_bloch_full.py — 35 tests
Replaces the analytic solution with numerical ODE integration via
scipy.integrate.solve_ivp (RK45). Both T₁ longitudinal recovery and T₂
transverse dephasing are active simultaneously. Key verified results:
| Check | Result |
|---|---|
| Equilibrium derivative | 0.000 (stable fixed point) |
|M⊥|(T₂) |
0.367879 = 1/e |
| Mz(T₁) from inversion | 0.632121 = 1 - 1/e |
| Inversion recovery error vs analytic | 3.7 × 10⁻⁹ |
| Agreement with Prototype 2 analytic | rtol = 10⁻⁴ |
Script: examples/run_prototype4.py
Tests: tests/test_sequences.py — 35 tests
Implements the pulse sequence engine in src/sequences.py. Pulses are exact
SO(3) rotations (zero numerical error). Free evolution segments use the full
Bloch ODE solver. Key physics verified: echo amplitude = M₀·exp(-2τ/T₂) to
rtol = 2% for τ = 1, 2, 5 µs; T₂ extracted from a 30-point echo sweep
matches the true value to 5%; CPMG echo times are spaced at exactly 2τ.
Script: examples/run_prototype5.py
Tests: tests/test_ensemble.py — 29 tests
Simulates N spins with Larmor frequencies drawn from N(ω₀, σ²). The ensemble-averaged signal is computed by running the full Bloch ODE for each spin independently and averaging. Key results:
- FID envelope matches M₀·exp(-t/T₂)·exp(-σ²t²/2) to rtol = 10%
- Echo amplitude at 2τ matches exp(-2τ/T₂) for σ = 0, 0.3, 0.8 rad/µs — refocusing confirmed across all tested inhomogeneity levels
- FID amplitude at 2τ < echo amplitude at 2τ — T₂* effect directly demonstrated
- Fitted T₂ from echo sweep is independent of σ to rtol = 10%
Script: app.py
Brings all modules together into an interactive tool. app.py is a pure
frontend: it contains no simulation logic, no physics formulas, and no ODE
integration. All physics lives in src/.
⚛️ Single Qubit mode — nine experiments selectable from the sidebar:
| Experiment | Physics |
|---|---|
| Single Spin (Bloch) | Analytic Mₓ, Mᵧ, M_z with T₁/T₂ |
| Ensemble FID | Gaussian inhomogeneous broadening, T₂* |
| Hahn Echo | Analytic echo with π-pulse refocusing |
| CPMG Train | N-echo train via full Bloch ODE (requires Run button) |
| Echo Sweep | Echo amplitude vs 2τ with T₂ fit |
| FID vs Echo (T₂* comparison) | Side-by-side FID envelope and echo amplitude |
| Rabi Oscillation | P↑(t) with chevron and resonance scan |
| Ramsey Interference | Fringe fitting, T₂* extraction, sensitivity |
| Density Matrix | Single-qubit Lindblad, purity, DM vs Bloch validation |
🔗 Two Qubits mode — two experiments:
| Experiment | Physics |
|---|---|
| Bell States & Gates | Live density matrix heatmaps, Bell fidelities, reduced Bloch spheres, CHSH, XX gate sweep, entanglement table for all four Bell states |
| Noise & Entanglement Decay | Two-qubit Lindblad with independent T₁/T₂ per qubit, optional depolarising and correlated ZZ noise, entanglement evolution plots, ESD detection, correlated vs local dephasing comparison |
Fitting tab — extract parameters from data:
- Five models: simple T₂, Gaussian FID envelope, Hahn echo, T₁ inversion recovery, stretched exponential
- Upload experimental CSV (two columns: time, coherence) or use synthetic noisy data
- Displays best-fit curve, residuals panel, RMSE, χ²
- Results persist in session state — changing sidebar values does not wipe results
- CSV export of fit curve and residuals
Module: src/two_qubit/
A self-contained two-qubit quantum mechanics package with five submodules and ~2,600 lines of physics code. All validated at floating-point precision.
- Computational basis vectors:
KET_00,KET_01,KET_10,KET_11 - All four Bell states:
bell_state(name),all_bell_states() - Tensor product utilities:
tensor(A, B),tensor_op_on_qubit(O, q, n) - State constructors:
product_state(θ_A, φ_A, θ_B, φ_B),random_pure_state() - Diagnostics:
is_entangled(),state_summary(),ket_to_dm()
Single-qubit: I, X, Y, Z, H, S, T, Rx(θ), Ry(θ), Rz(θ), U3(θ,φ,λ)
Two-qubit: CNOT, CZ, SWAP, iSWAP, XX(θ) (Mølmer-Sørensen), CP(φ)
prepare_bell_state(name)— prepares any Bell state via H ⊗ I → CNOT circuitrun_circuit(psi0, ops)— executes a list of(gate_type, *args)tuplestwo_qubit_hamiltonian(Jx, Jy, Jz, hA, hB)— Heisenberg exchange couplingxx_sweep(n_angles)— concurrence of XX(θ)|00⟩ as θ sweeps 0→π
partial_trace(rho, keep)— trace out one qubitvon_neumann_entropy(rho)— S(ρ) = -Tr(ρ log₂ ρ)schmidt_decomposition(psi)— SVD of coefficient matrixentanglement_entropy(psi)— S(ρ_A) for pure bipartite statesconcurrence(rho)— Wootters formula for mixed two-qubit statesentanglement_of_formation(rho)— exact closed form via concurrencenegativity(rho)/logarithmic_negativity(rho)— PPT criterionchsh_value(rho)— CHSH inequality with Tsirelson-optimal settingsentanglement_summary(state)— all measures in one dicttrack_entanglement(rho_t)— time-series of all measures
Validation (Bell state |Φ+⟩):
| Measure | Value | Expected |
|---|---|---|
| Concurrence C | 1.0000 | 1 |
| Entanglement entropy | 1.0000 ebit | 1 ebit |
| Negativity N | 0.5000 | 0.5 |
| Log negativity E_N | 1.0000 | 1 |
| CHSH value B | 2.8284 | 2√2 |
| Schmidt coefficients | [0.7071, 0.7071] | [1/√2, 1/√2] |
TwoQubitNoiseModel dataclass accepts independent T₁, T₂, T_dep per qubit
plus a correlated ZZ dephasing time T_ZZ. Validates T₂ ≤ 2·T₁ automatically.
Convenience constructors: noise_identical_qubits, noise_amplitude_only,
noise_dephasing_only.
simulate_2q(rho0, H, noise, t_max, n)— full Lindblad ODE, returns(t, rho_t, ent_dict)entanglement_sudden_death(bell, T1_A, T1_B, t_max)— demonstrates finite-time ESDcorrelated_vs_local_dephasing(T2, T_ZZ, t_max)— compares C(t) under local and ZZ noise
plot_dm_heatmap(rho)— annotated Re(ρ)/Im(ρ)/|ρ| heatmapsplot_hinton(rho)— squares scaled by |ρᵢⱼ|, hue encodes phase angleplot_entanglement_evolution(t, ent)— three-panel: C + E_F, entropy, purityplot_bloch_pair(rho)— side-by-side 3D Bloch spheres for ρ_A and ρ_Bplot_bell_fidelities(rho)— bar chart of fidelity with all four Bell statesplot_esd_demo(t, C, purity_A)— ESD with t_ESD annotationplot_dm_snapshots(t, rho_t)— grid of |ρ| heatmaps at evenly-spaced timesplot_concurrence_vs_angle(angles, C)— C(θ) for the XX gate sweep
| Module | Key functions |
|---|---|
src/core.py |
simulate_bloch, bloch_rhs, bloch_precession, analytic_single_spin, analytic_ensemble_fid, analytic_hahn_echo, analytic_echo_sweep, analytic_fid_vs_echo, fit_echo_sweep_T2 |
src/sequences.py |
apply_pulse, free_evolve, hahn_echo_sequence, cpmg_sequence, sweep_echo_amplitude, measure_echo_amplitude |
src/ensemble.py |
sample_frequencies, simulate_ensemble_FID, simulate_ensemble_hahn_echo, sweep_ensemble_echo |
src/rabi.py |
run_rabi, analytic_rabi_population, analytic_chevron, pi_pulse_time, max_population_inversion, resonance_scan |
src/ramsey.py |
sweep_ramsey, analytic_ramsey_population, fit_ramsey_fringes, ramsey_sensitivity, ramsey_detuning_scan |
src/density_matrix.py |
simulate_dm, ground_state_dm, superposition_dm, mixed_state_dm, hamiltonian_rotating, dm_to_bloch, bloch_to_dm, purity, population, bloch_vs_dm_error |
src/fitting.py |
fit_T2_to_data, fit_multi_to_data, generate_synthetic_data |
src/visualization.py |
plot_bloch_sphere_trajectory, animate_bloch_trajectory |
src/two_qubit/states.py |
bell_state, all_bell_states, product_state, ket_to_dm, tensor, state_summary |
src/two_qubit/gates.py |
CNOT, CZ, SWAP_gate, iSWAP_gate, XX_gate, prepare_bell_state, run_circuit, two_qubit_hamiltonian, xx_sweep |
src/two_qubit/entanglement.py |
concurrence, entanglement_entropy, negativity, logarithmic_negativity, entanglement_of_formation, chsh_value, partial_trace, schmidt_decomposition, entanglement_summary, track_entanglement |
src/two_qubit/lindblad2q.py |
TwoQubitNoiseModel, simulate_2q, entanglement_sudden_death, correlated_vs_local_dephasing, noise_identical_qubits, noise_amplitude_only, noise_dephasing_only |
src/two_qubit/visualization.py |
plot_dm_heatmap, plot_hinton, plot_entanglement_evolution, plot_bloch_pair, plot_bell_fidelities, plot_esd_demo, plot_dm_snapshots, plot_concurrence_vs_angle |
- F. Bloch, "Nuclear Induction," Physical Review 70, 460 (1946)
- E. L. Hahn, "Spin Echoes," Physical Review 80, 580 (1950)
- H. Y. Carr and E. M. Purcell, "Effects of Diffusion on Free Precession in Nuclear Magnetic Resonance Experiments," Physical Review 94, 630 (1954)
- S. Meiboom and D. Gill, "Modified Spin-Echo Method for Measuring Nuclear Relaxation Times," Review of Scientific Instruments 29, 688 (1958)
- C. P. Slichter, Principles of Magnetic Resonance, 3rd ed., Springer (1990)
- G. Lindblad, "On the Generators of Quantum Dynamical Semigroups," Communications in Mathematical Physics 48, 119 (1976)
- W. K. Wootters, "Entanglement of Formation of an Arbitrary State of Two Qubits," Physical Review Letters 80, 2245 (1998)
- T. Yu and J. H. Eberly, "Finite-Time Disentanglement via Spontaneous Emission," Physical Review Letters 93, 140404 (2004)
- J. S. Bell, "On the Einstein Podolsky Rosen Paradox," Physics 1, 195 (1964)
- B. Cirel'son, "Quantum Generalizations of Bell's Inequality," Letters in Mathematical Physics 4, 93 (1980)








