Probing Environmental Memory¶
Open quantum systems in YAQS couple a probe qubit (site 0) to an environment simulated by the remaining chain. Environmental memory measures how long the environment keeps past control and measurement choices relevant for future probe responses, evaluated at a temporal cut \(c\) in a sequence of interventions.
Use characterize() to probe the process: assemble the weighted response matrix \(\widetilde{V}(c)\), then read \(S_V(c)\), \(R(c)=\exp(S_V(c))\), and the mode spectrum.
For fast dynamics under control sequences, see Memory Surrogate Training and Prediction.
Setup¶
1import matplotlib.pyplot as plt
2import numpy as np
3
4from mqt.yaqs import AnalogSimParams, Hamiltonian, MemoryCharacterizer
5from mqt.yaqs.characterization.memory.shared.utils import make_zero_psi
6
7length = 4
8ham = Hamiltonian.ising(length=length, J=1.0, g=1.0)
9params = AnalogSimParams(dt=0.1, max_bond_dim=16, order=1)
10mc = MemoryCharacterizer(show_progress=False)
11psi0 = make_zero_psi(length)
Throughout, num_interventions is the probe-sequence length \(k\) and cut is the causal-break index \(c\) (the break sits at step \(c-1\); future legs use steps \(c+1,\ldots,k\)).
Use \(k>1\) and an interior cut so both past and future probe legs contribute to \(\widetilde{V}(c)\).
Characterize with the Hamiltonian backend¶
The full chain (system + environment) is simulated for each probe sequence. This is the reference memory metric when you have a microscopic open-system model.
1cut, num_interventions = 4, 6
2ham_result = mc.characterize(
3 ham,
4 params,
5 cut=cut,
6 num_interventions=num_interventions,
7 n_pasts=8,
8 n_futures=8,
9 initial_psi=psi0,
10 rng=np.random.default_rng(42),
11)
12
13sv = ham_result.singular_values(cut)
14fig, axes = plt.subplots(1, 2, figsize=(8, 3))
15axes[0].semilogy(sv, "o-")
16axes[0].set_xlabel("mode index")
17axes[0].set_ylabel("singular value")
18axes[0].set_title(r"Memory spectrum at cut $c=4$")
19
20v = ham_result.response_matrix(cut)
21im = axes[1].imshow(np.abs(v), aspect="auto", cmap="viridis")
22axes[1].set_title(r"$|\widetilde{V}(c)|$")
23axes[1].set_xlabel("future probe index")
24axes[1].set_ylabel("past probe index")
25fig.colorbar(im, ax=axes[1], fraction=0.046, pad=0.04)
26fig.suptitle(
27 rf"$S_V(c={cut})={ham_result.entropy(cut):.3f}$, "
28 rf"$R(c)={ham_result.modes(cut):.2f}$",
29 y=1.02,
30)
31fig.tight_layout()
Use preset="quick", "balanced", or "accurate" for default probe-grid sizes, or set n_pasts / n_futures explicitly.
Reading CharacterizationResult¶
Access |
Meaning |
|---|---|
|
Environmental memory entropy \(S_V(c)\) |
|
Effective memory modes \(R(c)=\exp(S_V(c))\) |
|
Mode spectrum at cut \(c\) (how many independent past branches remain visible) |
|
Cross-cut memory matrix \(\widetilde{V}(c)\) (see theory below) |
|
Probe arrays used at cut \(c\) (for reuse or inspection) |
|
Human-readable table of entropies and modes |
Theory: split-cut probing¶
Environmental memory asks: across a grid of past and future control settings on the probe, how many independent ways does the environment still correlate past choices with accessible future responses?
The split-cut protocol:
Sample past control legs \(\alpha=(U_1,\ldots,U_{c-1})\) and future legs \(\beta=(V_{c+1},\ldots,V_k)\) on the probe.
Insert a causal break at step \(c\): measure on the past side and prepare on the future side while the environment continues to evolve.
For each grid entry, simulate the open system, record probe weights and the output Pauli vector \(\mathbf{r}=(\langle X\rangle,\langle Y\rangle,\langle Z\rangle)\).
Assemble the weighted probe responses into \(\widetilde{V}(c)\) and compute \(S_V(c)\) from the normalized mode spectrum.
Hamiltonian characterize obtains weights from simulated intervention probabilities through cut \(c\) (MCWF or TJM/MPS, per representation).
Surrogate and exact-reference backends use the same probing protocol with analytic weights on the reference probe path.
Coupling strength and memory¶
Stronger Ising coupling \(J\) between the probe and the environment typically increases cross-cut memory.
Reuse one probe_set when sweeping \(J\):
1j_values = np.linspace(0.0, 2.0, 9)
2anchor = mc.characterize(
3 Hamiltonian.ising(length=length, J=0.0, g=1.0),
4 params,
5 num_interventions=num_interventions,
6 cut=cut,
7 n_pasts=8,
8 n_futures=8,
9 initial_psi=psi0,
10 rng=np.random.default_rng(42),
11)
12entropies = []
13for j in j_values:
14 result = mc.characterize(
15 Hamiltonian.ising(length=length, J=float(j), g=1.0),
16 params,
17 num_interventions=num_interventions,
18 cut=cut,
19 probe_set=anchor,
20 )
21 entropies.append(result.entropy(cut))
22
23fig, ax = plt.subplots(figsize=(5.5, 3))
24ax.plot(j_values, entropies, "o-")
25ax.set_xlabel(r"Ising coupling $J$")
26ax.set_ylabel(r"$S_V(c)$")
27ax.set_title(r"Environmental memory grows with probe-environment coupling")
28fig.tight_layout()
Intervention styles¶
characterize accepts intervention_style= (default "haar"):
"haar"— random unitaries on sequence legs; measure/prepare only at the causal cut."measure_prepare"— rank-1 measure–prepare maps on every leg."clifford"— random single-qubit Clifford gates on legs.
Pass probe_set= from a Hamiltonian run so surrogate or exact-reference backends evaluate the same probe ensemble (Memory Surrogate Training and Prediction).
Memory persistence: reset delay at the causal break¶
Pass delay=N to insert \(N\) soft-reset slots \((\lvert 0\rangle, \lvert 0\rangle)\) at the causal cut while the environment keeps evolving.
Extra reset time lets the environment decouple from the past before future controls act, so \(S_V(c)\) often decreases at strong probe-environment coupling (weaker coupling can show the opposite trend).
The logical num_interventions and cut are unchanged; the physical sequence length becomes num_interventions + delay + 1.
Reuse the same probe_set when sweeping delay.
delay > 0 is supported for Hamiltonian characterize only.
1delay_length = 6
2ham_delay = Hamiltonian.ising(length=delay_length, J=2.0, g=1.0)
3params_delay = AnalogSimParams(dt=0.1)
4mc_delay = MemoryCharacterizer(show_progress=False)
5delay_cut = 4
6delay_k = 6
7anchor_delay = mc_delay.characterize(
8 ham_delay,
9 params_delay,
10 num_interventions=delay_k,
11 cut=delay_cut,
12 delay=0,
13 n_pasts=6,
14 n_futures=6,
15 initial_psi=make_zero_psi(delay_length),
16 rng=np.random.default_rng(999_991),
17)
18delays = [0, 1, 2, 3]
19delay_entropies = []
20for delay in delays:
21 result = mc_delay.characterize(
22 ham_delay,
23 params_delay,
24 num_interventions=delay_k,
25 cut=delay_cut,
26 delay=delay,
27 probe_set=anchor_delay,
28 )
29 delay_entropies.append(result.entropy(delay_cut))
30
31fig, ax = plt.subplots(figsize=(4.5, 3))
32ax.plot(delays, delay_entropies, "s-")
33ax.set_xlabel("reset delay at causal cut")
34ax.set_ylabel(r"$S_V(c)$")
35ax.set_title(r"Strong coupling: memory erodes with longer reset delay")
36ax.set_xticks(delays)
37fig.tight_layout()
Representation¶
MemoryCharacterizer(representation="auto") mirrors Simulator: "vector" selects MCWF, "mps" selects TJM for the environment chain.
With "auto", MCWF is used when hamiltonian.length <= vector_max_qubits (default 10).