diff --git a/brainsnn-r3f-app/README.md b/brainsnn-r3f-app/README.md
index 2d58cd7..4c0b651 100644
--- a/brainsnn-r3f-app/README.md
+++ b/brainsnn-r3f-app/README.md
@@ -71,6 +71,46 @@ All optional. Copy [.env.example](.env.example) to `.env` and fill in only what
| 33 | Multimodal RAG Router | [MultimodalRagPanel.jsx](src/components/MultimodalRagPanel.jsx) + [utils/multimodalRag.js](src/utils/multimodalRag.js) |
| 34 | Vector-Graph Fusion | [VectorGraphFusionPanel.jsx](src/components/VectorGraphFusionPanel.jsx) |
| 35 | Direct Content Insertion (JSON) | [DirectInsertPanel.jsx](src/components/DirectInsertPanel.jsx) |
+| 101 | Quantum Coherence Lab | [QuantumCoherencePanel.jsx](src/components/QuantumCoherencePanel.jsx) + [utils/quantumCoherence.js](src/utils/quantumCoherence.js) |
+| 102 | Bell Pair Lab | [BellPairPanel.jsx](src/components/BellPairPanel.jsx) + [utils/bellPair.js](src/utils/bellPair.js) |
+| 103 | Quantum Sweep | [QuantumSweepPanel.jsx](src/components/QuantumSweepPanel.jsx) + [utils/quantumSweep.js](src/utils/quantumSweep.js) |
+| 104 | Quantum Glossary | [QuantumGlossaryPanel.jsx](src/components/QuantumGlossaryPanel.jsx) |
+
+## Quantum Coherence Lab
+
+**Layer 101 — Quantum Coherence Lab.** A pure-JavaScript, in-browser simulation
+of a single qubit running through `|0⟩ → H → RZ(θ) → H → M`. Slide the phase
+θ to watch interference move probability between |0⟩ and |1⟩. Add noise to
+damp the fringe. Toggle a mid-circuit observation to collapse superposition.
+Stack X·X pairs (algebraically identity) to watch decoherence eat depth.
+
+**What this is.** A teaching sandbox for the *mechanism* behind the word
+"alignment": phase coherence steers outcomes; noise and observation kill it.
+A **Scientific / Metaphor** mode toggle reframes the same numbers in
+plain English alongside the math.
+
+**What this is not.** This does **not** prove literal multiverse theory,
+consciousness collapse, Planck foam, or spiritual portals. Those are framing
+metaphors when the toggle is on, not physics claims.
+
+**Future backend.** The function surface (`runPhaseExperiment`,
+`runDecoherenceExperiment`, etc.) is intentionally compatible with the
+hardware-grade Qiskit suite at [`quantum_alignment/`](../quantum_alignment/),
+which runs the same three experiments on ideal Aer, noisy Aer, or real IBM
+Quantum hardware (and is shaped to swap in OriginQ later). **No vendor API
+keys are added to the frontend** — IBM tokens stay in the Python suite,
+read from `IBM_QUANTUM_TOKEN` at runtime only.
+
+**Cluster siblings.** Three follow-on layers extend L101 into a coherent quantum module:
+- **Layer 102 — Bell Pair Lab.** Two qubits run through `H ⊗ I → CNOT` to build the Bell state `|Φ+⟩ = (|00⟩ + |11⟩) / √2`. RY(θ) on qubit 0 lets you watch correlation slide from +1 (mirrored) to 0 (decohered) to −1 (anti-mirrored). Important framing: this is statistical correlation, *not* information transfer.
+- **Layer 103 — Quantum Sweep.** Auto-sweeps θ / noise / X·X-depth, plots P(0) and P(1) against the closed-form ideal, and exports a CSV with the same column shape as `quantum_alignment/results/results.csv` so browser-sim curves can be compared directly with the Qiskit ideal/noisy/real curves.
+- **Layer 104 — Quantum Glossary.** Searchable reference card for every term used in L101–L103 — plain language, the math, and a metaphor column explicitly framed as a teaching aid.
+
+Run the unit tests directly with Node (no extra dev deps):
+
+```bash
+npm run test:quantum
+```
## Keyboard shortcuts
diff --git a/brainsnn-r3f-app/package.json b/brainsnn-r3f-app/package.json
index 00c8c03..dc9a3e3 100644
--- a/brainsnn-r3f-app/package.json
+++ b/brainsnn-r3f-app/package.json
@@ -11,7 +11,8 @@
"build": "vite build",
"preview": "vite preview",
"start": "node server.js",
- "start:dev": "npm run build && node server.js"
+ "start:dev": "npm run build && node server.js",
+ "test:quantum": "node --test src/utils/quantumCoherence.test.mjs src/utils/bellPair.test.mjs src/utils/quantumSweep.test.mjs"
},
"dependencies": {
"@ffmpeg/ffmpeg": "^0.12.15",
diff --git a/brainsnn-r3f-app/src/App.jsx b/brainsnn-r3f-app/src/App.jsx
index afb3b3f..4b2f636 100644
--- a/brainsnn-r3f-app/src/App.jsx
+++ b/brainsnn-r3f-app/src/App.jsx
@@ -81,6 +81,10 @@ import HotkeyMap from './components/HotkeyMap';
import ThemePanel from './components/ThemePanel';
import CommunityPackPanel from './components/CommunityPackPanel';
import MilestonePanel from './components/MilestonePanel';
+import QuantumCoherencePanel from './components/QuantumCoherencePanel';
+import BellPairPanel from './components/BellPairPanel';
+import QuantumSweepPanel from './components/QuantumSweepPanel';
+import QuantumGlossaryPanel from './components/QuantumGlossaryPanel';
import { registerServiceWorker } from './utils/pwa';
import { registerTheme } from './utils/theme';
import DreamModePanel from './components/DreamModePanel';
@@ -779,6 +783,58 @@ export default function App() {
+
+ {
+ markActivity();
+ setState((prev) => {
+ const regions = { ...prev.regions };
+ for (const [region, delta] of Object.entries(deltas)) {
+ if (regions[region] === undefined) continue;
+ regions[region] = Math.max(0.04, Math.min(0.95, regions[region] + delta * 0.3));
+ }
+ return {
+ ...prev,
+ regions,
+ tick: (prev.tick ?? 0) + 1,
+ scenario: `Quantum Coherence (${score}/100)`,
+ };
+ });
+ toastSuccess(`Quantum coherence ${score}/100 mapped to brain · ${result.kind}`);
+ }}
+ />
+
+
+
+ {
+ markActivity();
+ setState((prev) => {
+ const regions = { ...prev.regions };
+ for (const [region, delta] of Object.entries(deltas)) {
+ if (regions[region] === undefined) continue;
+ regions[region] = Math.max(0.04, Math.min(0.95, regions[region] + delta * 0.3));
+ }
+ return {
+ ...prev,
+ regions,
+ tick: (prev.tick ?? 0) + 1,
+ scenario: `Bell Pair (corr ${result.correlation.toFixed(2)})`,
+ };
+ });
+ toastSuccess(`Bell correlation ${result.correlation.toFixed(2)} mapped to brain`);
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
diff --git a/brainsnn-r3f-app/src/components/BellPairPanel.jsx b/brainsnn-r3f-app/src/components/BellPairPanel.jsx
new file mode 100644
index 0000000..8f325e0
--- /dev/null
+++ b/brainsnn-r3f-app/src/components/BellPairPanel.jsx
@@ -0,0 +1,199 @@
+import React, { useMemo, useState } from 'react';
+import { runBellPairExperiment, mapBellToBrainState } from '../utils/bellPair';
+
+/**
+ * Layer 102 — Bell Pair Lab.
+ *
+ * Two-qubit entanglement, in-browser. Builds |Φ+⟩ = (|00⟩ + |11⟩)/√2 by
+ * running |00⟩ → H ⊗ I → CNOT, then rotates qubit 0 with RY(θ) so the user
+ * can watch correlation break smoothly. Shows joint outcomes |00⟩ |01⟩ |10⟩
+ * |11⟩ as four bars, plus a signed correlation strength in [-1, 1].
+ *
+ * Important framing: correlation here is a quantum statistical fact about
+ * a many-shot distribution, not "spooky action" at a distance in any
+ * mystical sense. No ER=EPR, no consciousness telepathy, no portals.
+ */
+
+const SHOTS_OPTIONS = [256, 1024, 4096];
+
+const BASIS_LABELS = ['|00⟩', '|01⟩', '|10⟩', '|11⟩'];
+const BASIS_COLORS = ['#5ad4ff', '#fdab43', '#dd6974', '#a86fdf'];
+
+function fmtPercent(p) {
+ return `${(p * 100).toFixed(1)}%`;
+}
+
+function explain({ correlation, rotationTheta, noise, mode }) {
+ const corr = correlation.toFixed(2);
+ if (mode === 'metaphor') {
+ if (correlation > 0.85) {
+ return `Two coins, perfectly mirrored (correlation ${corr}). Either both heads or both tails — never mixed. Metaphor: a pair of friends who finish each other's sentences.`;
+ }
+ if (correlation < -0.85) {
+ return `Two coins, perfectly anti-mirrored (correlation ${corr}). Always one head and one tail. Metaphor: opposites locked into balance.`;
+ }
+ if (Math.abs(correlation) < 0.15) {
+ return `The link snapped. Each coin lands independent of the other (correlation ${corr}). Metaphor: rotated the question hard enough that the shared answer dissolved.`;
+ }
+ return `Partial agreement (correlation ${corr}). Metaphor: the friendship is real but fading.`;
+ }
+ // scientific
+ if (rotationTheta === 0 && noise < 0.05) {
+ return `|Φ+⟩ measured in the computational basis. Outcomes are 50/50 split between |00⟩ and |11⟩, never |01⟩ or |10⟩. Correlation ${corr}: maximal classical-impossible correlation across two non-interacting qubits.`;
+ }
+ if (Math.abs(correlation) < 0.15) {
+ return `Rotation moved qubit 0 to a basis where the Bell state's correlations average out. Correlation ${corr}. Reading qubit 0 no longer predicts qubit 1. (No information was sent — this is the basis-mismatch lesson, not "spooky action".)`;
+ }
+ if (correlation < 0) {
+ return `RY(${rotationTheta.toFixed(2)}) flipped the in-basis correlation. Anti-correlated outcomes (|01⟩, |10⟩) now dominate. Correlation ${corr}.`;
+ }
+ return `Bell state with rotation ${rotationTheta.toFixed(2)} and noise ${noise.toFixed(2)}. Correlation ${corr}; noise ${noise > 0 ? 'is mixing in uniform randomness' : 'is zero'}.`;
+}
+
+function JointBars({ distribution, counts }) {
+ return (
+
+ {distribution.map((p, i) => (
+
+
+ {BASIS_LABELS[i]}
+ {fmtPercent(p)} · {counts[i]} shots
+
+
+
+ ))}
+
+ );
+}
+
+function CircuitDiagram({ rotationTheta }) {
+ return (
+
+
q0 |0⟩ ──[H]──●──{rotationTheta !== 0 ? `[RY(${rotationTheta.toFixed(2)})]──` : '──────────────'}M
+
q1 |0⟩ ─────────⊕────────────────M
+
+ );
+}
+
+export default function BellPairPanel({ onApplyToBrain } = {}) {
+ const [rotationTheta, setRotationTheta] = useState(0);
+ const [shots, setShots] = useState(1024);
+ const [noise, setNoise] = useState(0);
+ const [mode, setMode] = useState('scientific');
+ const [runToken, setRunToken] = useState(0);
+
+ const result = useMemo(() => {
+ void runToken;
+ return runBellPairExperiment({ rotationTheta, shots, noise });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [rotationTheta, shots, noise, runToken]);
+
+ const explanation = useMemo(
+ () => explain({ correlation: result.correlation, rotationTheta, noise, mode }),
+ [result.correlation, rotationTheta, noise, mode],
+ );
+
+ const brainDeltas = useMemo(() => mapBellToBrainState(result), [result]);
+ const corrColor = result.correlation > 0.5 ? '#5ee69a' : result.correlation < -0.5 ? '#a86fdf' : '#fdab43';
+
+ return (
+
+ Layer 102 · bell pair lab
+ Two qubits, one shared answer
+
+ Build the Bell state |Φ+⟩ = (|00⟩ + |11⟩)/√2 with{' '}
+ H ⊗ I → CNOT, then rotate qubit 0 with RY(θ) and measure
+ both qubits jointly. Correlation runs from +1 (perfect mirror) to −1
+ (perfect opposites) to 0 (noise dissolved the link).
+
+
+ This is statistical correlation across many shots, not
+ information transfer. No consciousness, no telepathy, no portals.
+
+
+
+ setMode((m) => (m === 'scientific' ? 'metaphor' : 'scientific'))}>
+ Mode: {mode === 'scientific' ? 'Scientific' : 'Metaphor'}
+
+ setRunToken((t) => t + 1)}>Re-run
+ { setRotationTheta(0); setNoise(0); setShots(1024); }}>Reset
+ {onApplyToBrain && (
+ onApplyToBrain({ result, deltas: brainDeltas })}>Apply to brain
+ )}
+
+
+
+
+
+ RY rotation θ on qubit 0 (0 → π)
+ {rotationTheta.toFixed(3)}
+
+
setRotationTheta(parseFloat(e.target.value))} style={{ width: '100%' }} />
+
+
+
+ Depolarizing noise (0 = pure, 1 = uniform)
+ {noise.toFixed(2)}
+
+
setNoise(parseFloat(e.target.value))} style={{ width: '100%' }} />
+
+
+ Shots:
+ {SHOTS_OPTIONS.map((s) => (
+ setShots(s)}>{s}
+ ))}
+
+
+
+
+
+
+
+
+
+ Correlation strength {result.correlation.toFixed(3)}
+
+
+ {result.correlation > 0.5 ? 'mirrored' : result.correlation < -0.5 ? 'anti-mirrored' : 'decohered'}
+
+
+
+ {explanation}
+
+
+ Region deltas (preview)
+
+ {Object.entries(brainDeltas).map(([region, delta]) => (
+
+
{region}
+
0 ? '#5ee69a' : '#94a3b8' }}>{delta >= 0 ? '+' : ''}{delta.toFixed(2)}
+
+ ))}
+
+
+
+ );
+}
diff --git a/brainsnn-r3f-app/src/components/QuantumCoherencePanel.jsx b/brainsnn-r3f-app/src/components/QuantumCoherencePanel.jsx
new file mode 100644
index 0000000..6d92fd0
--- /dev/null
+++ b/brainsnn-r3f-app/src/components/QuantumCoherencePanel.jsx
@@ -0,0 +1,394 @@
+import React, { useMemo, useState } from 'react';
+import {
+ runPhaseExperiment,
+ runObservationExperiment,
+ runDecoherenceExperiment,
+ coherenceScore,
+ mapQuantumToBrainState,
+} from '../utils/quantumCoherence';
+
+/**
+ * Layer 101 — Quantum Coherence Lab panel.
+ *
+ * Local, in-browser simulation of the simplest possible quantum circuit:
+ * |0⟩ → H → RZ(θ) → H → M
+ *
+ * Teaches superposition, phase, interference, observation, noise, and
+ * decoherence. The "Metaphor" toggle re-frames the same numbers in
+ * everyday language; nothing in this panel claims literal multiverse
+ * theory, consciousness collapse, Planck foam, or spiritual portals —
+ * those are framing aids, not physics claims.
+ *
+ * For an offline / hardware-grade version of the same three experiments,
+ * see /quantum_alignment/ (Qiskit + ideal/noisy/IBM-real backends).
+ */
+
+const SHOTS_OPTIONS = [256, 1024, 4096];
+
+const SHOTS_HINT = {
+ 256: 'Quick — noisy bars',
+ 1024: 'Default — balanced',
+ 4096: 'Slow — smooth bars',
+};
+
+function fmtPercent(p) {
+ return `${(p * 100).toFixed(1)}%`;
+}
+
+function ProbabilityBars({ distribution, counts }) {
+ const [p0, p1] = distribution;
+ return (
+
+ {[
+ { label: '|0⟩', p: p0, count: counts[0], color: '#5ad4ff' },
+ { label: '|1⟩', p: p1, count: counts[1], color: '#a86fdf' },
+ ].map((b) => (
+
+
+ {b.label}
+
+ {fmtPercent(b.p)} · {b.count} shots
+
+
+
+
+ ))}
+
+ );
+}
+
+function CircuitRow({ theta, observeMidway, depth }) {
+ const gates = [
+ { label: 'H', desc: 'Hadamard — make superposition' },
+ { label: `RZ(${theta.toFixed(2)})`, desc: 'Rotate the relative phase' },
+ ];
+ if (observeMidway) {
+ gates.push({ label: '👁 M', desc: 'Mid-circuit measurement' });
+ }
+ if (depth > 0) {
+ gates.push({ label: `(X·X)×${depth}`, desc: 'Identity on paper, decoherence in practice' });
+ }
+ gates.push({ label: 'H', desc: 'Hadamard — recombine for interference' });
+ gates.push({ label: 'M', desc: 'Final measurement — read out 0 or 1' });
+
+ return (
+
+ |0⟩
+ {gates.map((g, i) => (
+
+ →
+
+ {g.label}
+
+
+ ))}
+
+ );
+}
+
+function buildExplanation({ result, score, mode, theta, observeMidway, depth, noise }) {
+ const [p0, p1] = result.distribution;
+ const dominant = p0 >= p1 ? '|0⟩' : '|1⟩';
+ const dominantPct = fmtPercent(Math.max(p0, p1));
+ const balanced = Math.abs(p0 - p1) < 0.1;
+
+ if (mode === 'metaphor') {
+ if (observeMidway) {
+ return `Watching the qubit mid-flight collapsed it. Like checking a Schrödinger box too early — the second H spreads the now-classical bit back into ~50/50, score ${score}/100. Metaphor: peek at a held thought and you lose the held thought.`;
+ }
+ if (balanced) {
+ return `Phase tuned to a place where both outcomes interfere equally — ${dominantPct} either way. Metaphor: two stories with equal pull. Coherence ${score}/100.`;
+ }
+ if (noise > 0.5) {
+ return `Noise ate the interference. The qubit drifted toward ${dominant} (${dominantPct}) but without the clean fringe. Metaphor: signal in a loud room. Coherence ${score}/100.`;
+ }
+ if (depth > 0) {
+ return `Stacked ${depth} X·X pairs — algebraically a no-op, but each gate leaks. Metaphor: a long whispered chain stays the same on paper, drifts in real life. Coherence ${score}/100.`;
+ }
+ return `Phase steered the wave to ${dominant} (${dominantPct}). Metaphor: aligned attention picks one outcome out of two. Coherence ${score}/100.`;
+ }
+
+ // Scientific mode
+ if (observeMidway) {
+ return `Mid-circuit measurement collapsed |+⟩ to a basis state, killing interference at the second H. Result is ~50/50 (${dominantPct} ${dominant}). Coherence ${score}/100. This is the "watched-path kills fringe" lesson.`;
+ }
+ if (depth > 0) {
+ return `${depth} X·X pairs. Ideal: identity (P(0)=1). With noise=${noise.toFixed(2)} and dephasing per gate, you got P(${dominant})=${dominantPct}. Coherence drops with depth × noise. Score ${score}/100.`;
+ }
+ if (balanced) {
+ return `θ=${theta.toFixed(2)} sits near π/2 — H·RZ(π/2)·H gives ~50/50. P(${dominant})=${dominantPct}. Interference fringe present at low noise. Score ${score}/100.`;
+ }
+ return `H → RZ(${theta.toFixed(2)}) → H → M. Phase rotation interferes at the second H, biasing the readout to ${dominant} (${dominantPct}). Noise=${noise.toFixed(2)}, coherence ${score}/100.`;
+}
+
+export default function QuantumCoherencePanel({ onApplyToBrain } = {}) {
+ const [theta, setTheta] = useState(0);
+ const [shots, setShots] = useState(1024);
+ const [noise, setNoise] = useState(0);
+ const [depth, setDepth] = useState(0); // X-X pair count
+ const [observeMidway, setObserveMidway] = useState(false);
+ const [mode, setMode] = useState('scientific'); // 'scientific' | 'metaphor'
+ const [runToken, setRunToken] = useState(0); // bumps every Re-run click
+
+ const result = useMemo(() => {
+ void runToken;
+ if (observeMidway) {
+ return runObservationExperiment({ shots, observeMidway: true, noise });
+ }
+ if (depth > 0) {
+ return runDecoherenceExperiment({ xxPairs: depth, shots, noise });
+ }
+ return runPhaseExperiment({ theta, shots, noise });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [theta, shots, noise, depth, observeMidway, runToken]);
+
+ const score = useMemo(
+ () => coherenceScore({
+ noise,
+ depth: Math.max(1, depth + 1),
+ observedMidway: observeMidway,
+ }),
+ [noise, depth, observeMidway],
+ );
+
+ const explanation = useMemo(
+ () => buildExplanation({ result, score, mode, theta, observeMidway, depth, noise }),
+ [result, score, mode, theta, observeMidway, depth, noise],
+ );
+
+ const brainDeltas = useMemo(() => mapQuantumToBrainState(result), [result]);
+
+ return (
+
+ Layer 101 · quantum coherence lab
+ Phase, interference, decoherence — in your browser
+
+ A single qubit running |0⟩ → H → RZ(θ) → H → M, simulated
+ locally in JavaScript. Slide θ to see interference move probability
+ between |0⟩ and |1⟩. Add noise. Toggle a mid-circuit observation.
+ Stack X·X pairs to watch identity-on-paper fall apart in practice.
+
+
+ This teaches the mechanism behind the word "alignment" — phase
+ coherence steers outcomes; noise and observation kill it. It does
+ not prove multiverse theory, consciousness collapse, Planck foam,
+ or spiritual portals. Those are metaphors when the toggle is on.
+ For an offline Qiskit run on ideal / noisy / real IBM hardware, see
+ the quantum_alignment/ suite at the repo root.
+
+
+
+ setMode((m) => (m === 'scientific' ? 'metaphor' : 'scientific'))}
+ >
+ Mode: {mode === 'scientific' ? 'Scientific' : 'Metaphor'}
+
+ setRunToken((t) => t + 1)}>Re-run
+ {
+ setTheta(0); setNoise(0); setDepth(0);
+ setObserveMidway(false); setShots(1024);
+ }}
+ >
+ Reset
+
+ {onApplyToBrain && (
+ onApplyToBrain({ result, deltas: brainDeltas, score })}
+ >
+ Apply to brain
+
+ )}
+
+
+ {/* Sliders */}
+
+
+
+ Phase θ (0 → π)
+ {theta.toFixed(3)}
+
+
setTheta(parseFloat(e.target.value))}
+ disabled={observeMidway || depth > 0}
+ style={{ width: '100%' }}
+ />
+
+
+
+
+ Noise (0 = ideal, 1 = thermal mush)
+ {noise.toFixed(2)}
+
+
setNoise(parseFloat(e.target.value))}
+ style={{ width: '100%' }}
+ />
+
+
+
+
+ Depth — extra X·X pairs (algebraically zero work)
+ {depth}
+
+
setDepth(parseInt(e.target.value, 10))}
+ style={{ width: '100%' }}
+ />
+
+
+
+ Shots:
+ {SHOTS_OPTIONS.map((s) => (
+ setShots(s)}
+ title={SHOTS_HINT[s]}
+ >
+ {s}
+
+ ))}
+
+ setObserveMidway(e.target.checked)}
+ />
+ Observe midway
+
+
+
+
+ {/* Circuit visualization */}
+
+
+ {/* Probability bars */}
+
+
+ {/* Coherence score */}
+ = 70 ? '#5ee69a' : score >= 40 ? '#fdab43' : '#dd6974'}`,
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ }}
+ >
+
+ Coherence score{' '}
+ {score} /100
+
+
+ {score >= 70 ? 'phase intact' : score >= 40 ? 'fading' : 'decohered'}
+
+
+
+ {/* Explanation */}
+
+ {explanation}
+
+
+ {/* Brain deltas preview */}
+
+
+ Region deltas (preview)
+
+
+ {Object.entries(brainDeltas).map(([region, delta]) => (
+
+
{region}
+
0 ? '#5ee69a' : '#94a3b8' }}>
+ {delta >= 0 ? '+' : ''}{delta.toFixed(2)}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/brainsnn-r3f-app/src/components/QuantumGlossaryPanel.jsx b/brainsnn-r3f-app/src/components/QuantumGlossaryPanel.jsx
new file mode 100644
index 0000000..8ec18d1
--- /dev/null
+++ b/brainsnn-r3f-app/src/components/QuantumGlossaryPanel.jsx
@@ -0,0 +1,284 @@
+import React, { useMemo, useState } from 'react';
+
+/**
+ * Layer 104 — Quantum Glossary panel.
+ *
+ * A searchable reference card for every quantum term used in the L101 –
+ * L103 cluster. Plain language + the math, side by side. Complements the
+ * Metaphor toggle in the other panels: lets a user look up an unfamiliar
+ * symbol without leaving the page.
+ *
+ * Nothing in here claims literal multiverse / consciousness / portal
+ * physics. The "metaphor" column is explicitly framed as a teaching aid.
+ */
+
+const TERMS = [
+ {
+ term: '|0⟩, |1⟩',
+ category: 'state',
+ plain: 'The two definite answers a single qubit can give when measured.',
+ math: 'Computational basis states. State vector [1, 0] and [0, 1].',
+ metaphor: 'Two doors. After you measure, the qubit is behind one of them.',
+ },
+ {
+ term: '|+⟩, |-⟩',
+ category: 'state',
+ plain: 'Equal-strength mixtures of |0⟩ and |1⟩, with relative phase + or -.',
+ math: '(|0⟩ ± |1⟩) / √2.',
+ metaphor: 'A coin spinning. + = clockwise, - = counter-clockwise; both still 50/50 when it lands.',
+ },
+ {
+ term: 'Superposition',
+ category: 'state',
+ plain: 'A qubit holding both 0 and 1 at the same time, with relative weights and a phase.',
+ math: 'α|0⟩ + β|1⟩ with |α|² + |β|² = 1.',
+ metaphor: 'A coin in flight — neither heads nor tails until something lands the question.',
+ },
+ {
+ term: 'Phase (relative)',
+ category: 'state',
+ plain: 'The angle between the |0⟩ and |1⟩ amplitudes — invisible until you interfere them.',
+ math: 'arg(β / α). Global phase doesn’t affect probabilities.',
+ metaphor: 'How the coin spins. Two spins that are out of step cancel each other.',
+ },
+ {
+ term: 'Amplitude',
+ category: 'state',
+ plain: 'Complex number whose squared magnitude is the probability of an outcome.',
+ math: 'P(state) = |amplitude|².',
+ metaphor: 'The strength and direction of one side of a wave.',
+ },
+ {
+ term: 'Hadamard (H)',
+ category: 'gate',
+ plain: 'Turns |0⟩ into an equal superposition |+⟩ — and back.',
+ math: 'H = (1/√2) [[1, 1], [1, -1]]. H · H = I.',
+ metaphor: 'Spin the coin. Spin it again with the same flick — it lands the way it started.',
+ },
+ {
+ term: 'Pauli-X',
+ category: 'gate',
+ plain: 'Bit flip — swap |0⟩ and |1⟩.',
+ math: 'X = [[0, 1], [1, 0]].',
+ metaphor: 'Pull the switch from "off" to "on".',
+ },
+ {
+ term: 'Pauli-Z',
+ category: 'gate',
+ plain: 'Phase flip — multiplies |1⟩ by -1, leaves |0⟩ alone.',
+ math: 'Z = [[1, 0], [0, -1]].',
+ metaphor: 'Reverse the spin direction without changing whether it’s heads or tails.',
+ },
+ {
+ term: 'RZ(θ)',
+ category: 'gate',
+ plain: 'Rotates the relative phase by an angle θ.',
+ math: 'RZ(θ) = diag(e^{-iθ/2}, e^{+iθ/2}).',
+ metaphor: 'Twist the coin’s spin axis by θ before it lands.',
+ },
+ {
+ term: 'RY(θ)',
+ category: 'gate',
+ plain: 'Rotates around the Y axis — moves probability between |0⟩ and |1⟩ smoothly.',
+ math: 'RY(θ) = [[cos(θ/2), -sin(θ/2)], [sin(θ/2), cos(θ/2)]].',
+ metaphor: 'Tilt the coin: more heads or more tails depending on how far you tilt.',
+ },
+ {
+ term: 'CNOT',
+ category: 'gate',
+ plain: 'Two-qubit gate: flips the target qubit only when the control is |1⟩.',
+ math: 'CNOT|c, t⟩ = |c, t ⊕ c⟩.',
+ metaphor: 'A pair of coins glued together: flipping coin A flips coin B if A landed heads.',
+ },
+ {
+ term: 'Bell state |Φ+⟩',
+ category: 'state',
+ plain: 'Maximally entangled two-qubit state where both qubits always agree when measured.',
+ math: '(|00⟩ + |11⟩) / √2. Built by H ⊗ I → CNOT.',
+ metaphor: 'Two coins that always land the same way — but only because their flips were prepared together, not because one signals the other.',
+ },
+ {
+ term: 'Entanglement',
+ category: 'state',
+ plain: 'A correlation between two qubits stronger than any classical pair can have.',
+ math: 'A pure 2-qubit state that is *not* expressible as |a⟩ ⊗ |b⟩.',
+ metaphor: '"Linked dice." Not telepathy — the link comes from how they were built, and it carries no information by itself.',
+ },
+ {
+ term: 'Measurement',
+ category: 'process',
+ plain: 'Sample one of the basis states with probability |amplitude|². The state collapses.',
+ math: 'For α|0⟩ + β|1⟩: P(0) = |α|², P(1) = |β|². Post-measurement state = the basis ket you got.',
+ metaphor: 'The coin lands. After that, it’s heads-or-tails, not spinning.',
+ },
+ {
+ term: 'Mid-circuit measurement',
+ category: 'process',
+ plain: 'Measuring partway through a circuit. Destroys interference for any later gates.',
+ math: 'Projects the state onto the measurement basis; the rest of the circuit acts on a classical bit.',
+ metaphor: 'Peeking at the spinning coin before it lands stops the spin.',
+ },
+ {
+ term: 'Interference',
+ category: 'process',
+ plain: 'When two paths through a circuit reinforce or cancel based on their relative phase.',
+ math: 'Constructive: amplitudes add. Destructive: they cancel.',
+ metaphor: 'Two ripples in a pond meeting peak-to-peak (taller) or peak-to-trough (flat).',
+ },
+ {
+ term: 'Decoherence',
+ category: 'noise',
+ plain: 'Loss of phase information through unwanted coupling to the environment.',
+ math: 'T2 dephasing damps off-diagonal density-matrix elements.',
+ metaphor: 'A whisper getting drowned out as a room fills up.',
+ },
+ {
+ term: 'Dephasing',
+ category: 'noise',
+ plain: 'A specific decoherence channel: the relative phase randomizes while populations stay.',
+ math: 'Damps imaginary parts of off-diagonal amplitudes / matrix elements.',
+ metaphor: 'The coin keeps spinning at the same speed but its axis wobbles randomly.',
+ },
+ {
+ term: 'Bit-flip channel',
+ category: 'noise',
+ plain: 'A gate misfire: with probability p, the qubit’s state is X-flipped.',
+ math: 'ρ → (1-p) ρ + p X ρ X.',
+ metaphor: 'A 1-in-N chance the switch jiggled to the wrong position.',
+ },
+ {
+ term: 'Depolarizing noise',
+ category: 'noise',
+ plain: 'With probability p, replace the qubit’s state with a uniform random one.',
+ math: 'ρ → (1-p) ρ + p · I/2.',
+ metaphor: 'A small chance the coin gets snatched and replaced with a freshly-tossed one.',
+ },
+ {
+ term: 'Coherence score (this app)',
+ category: 'metric',
+ plain: '0–100 summary of how much quantum-ness survived a run. Drops with noise and depth.',
+ math: 'See utils/quantumCoherence.js → coherenceScore().',
+ metaphor: 'Battery left in the qubit at the end of the experiment.',
+ },
+ {
+ term: 'Correlation strength (this app)',
+ category: 'metric',
+ plain: 'Bell-pair signed correlation: +1 mirrored, 0 independent, -1 anti-mirrored.',
+ math: '(P(00) + P(11)) - (P(01) + P(10)).',
+ metaphor: 'How much the two coins agree across many tosses.',
+ },
+ {
+ term: 'Shots',
+ category: 'metric',
+ plain: 'How many times you rerun the circuit and measure. More shots = smoother bars.',
+ math: 'Shot noise scales as 1 / √N.',
+ metaphor: 'How many coin tosses you average over.',
+ },
+];
+
+const CATEGORY_LABEL = {
+ state: 'States',
+ gate: 'Gates',
+ process: 'Processes',
+ noise: 'Noise',
+ metric: 'Metrics',
+};
+
+const CATEGORY_COLOR = {
+ state: '#5ad4ff',
+ gate: '#a86fdf',
+ process: '#fdab43',
+ noise: '#dd6974',
+ metric: '#5ee69a',
+};
+
+export default function QuantumGlossaryPanel() {
+ const [q, setQ] = useState('');
+ const [activeCategory, setActiveCategory] = useState('all');
+
+ const filtered = useMemo(() => {
+ const needle = q.trim().toLowerCase();
+ return TERMS.filter((t) => {
+ if (activeCategory !== 'all' && t.category !== activeCategory) return false;
+ if (!needle) return true;
+ return (
+ t.term.toLowerCase().includes(needle)
+ || t.plain.toLowerCase().includes(needle)
+ || t.math.toLowerCase().includes(needle)
+ || t.metaphor.toLowerCase().includes(needle)
+ );
+ });
+ }, [q, activeCategory]);
+
+ const counts = useMemo(() => {
+ const c = { all: TERMS.length };
+ for (const t of TERMS) c[t.category] = (c[t.category] || 0) + 1;
+ return c;
+ }, []);
+
+ return (
+
+ Layer 104 · quantum glossary
+ {TERMS.length} terms used in the quantum cluster
+
+ Plain language plus the math, side by side. Includes a metaphor
+ column — these are explicitly framed as teaching aids , not
+ physics claims about consciousness, multiverses, or anything beyond
+ what the math says.
+
+
+
+ setQ(e.target.value)}
+ style={{ flex: 1, minWidth: 220 }}
+ />
+ setActiveCategory(e.target.value)}>
+ All ({counts.all})
+ {Object.entries(CATEGORY_LABEL).map(([id, label]) => (
+ {label} ({counts[id] || 0})
+ ))}
+
+
+
+
+ Showing {filtered.length} of {TERMS.length}
+
+
+
+ {filtered.map((t) => (
+
+
+
{t.term}
+
+ {CATEGORY_LABEL[t.category]}
+
+
+
+
{t.plain}
+
{t.math}
+
+ metaphor: {t.metaphor}
+
+
+
+ ))}
+ {filtered.length === 0 &&
No matches.
}
+
+
+ );
+}
diff --git a/brainsnn-r3f-app/src/components/QuantumSweepPanel.jsx b/brainsnn-r3f-app/src/components/QuantumSweepPanel.jsx
new file mode 100644
index 0000000..6f40b21
--- /dev/null
+++ b/brainsnn-r3f-app/src/components/QuantumSweepPanel.jsx
@@ -0,0 +1,193 @@
+import React, { useMemo, useState } from 'react';
+import { runSweep, rowsToCsv, sweepSummary } from '../utils/quantumSweep';
+
+/**
+ * Layer 103 — Quantum Sweep panel.
+ *
+ * Auto-sweeps a parameter through the L101 single-qubit experiment family
+ * and renders a small SVG line chart of P(0) (and the ideal curve where
+ * one is well-defined). Lets the user download a CSV with the same column
+ * shape as the offline Qiskit suite at /quantum_alignment/results/.
+ */
+
+const KINDS = [
+ { id: 'phase', label: 'Phase θ ∈ [0, π]', help: 'Sweeps θ; ideal P(0) = cos²(θ/2).' },
+ { id: 'noise', label: 'Noise ∈ [0, 1]', help: 'Sweeps depolarizing-style noise at fixed θ.' },
+ { id: 'depth', label: 'X·X depth', help: 'Sweeps logical-identity depth; ideal P(0)=1.' },
+];
+
+const SHOTS_OPTIONS = [256, 1024, 4096];
+
+function csvDownload(rows, kind) {
+ const csv = rowsToCsv(rows);
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
+ a.href = url;
+ a.download = `quantum_sweep_${kind}_${ts}.csv`;
+ document.body.appendChild(a);
+ a.click();
+ setTimeout(() => {
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }, 0);
+}
+
+function SweepChart({ rows }) {
+ if (!rows.length) return null;
+ const W = 360;
+ const H = 140;
+ const padL = 32;
+ const padR = 8;
+ const padT = 8;
+ const padB = 22;
+ const innerW = W - padL - padR;
+ const innerH = H - padT - padB;
+ const xs = rows.map((r) => r.parameter);
+ const xMin = Math.min(...xs);
+ const xMax = Math.max(...xs);
+ const xSpan = xMax - xMin || 1;
+ const xCoord = (v) => padL + ((v - xMin) / xSpan) * innerW;
+ const yCoord = (p) => padT + (1 - p) * innerH;
+ const linePath = (key) =>
+ rows.map((r, i) => `${i === 0 ? 'M' : 'L'} ${xCoord(r.parameter).toFixed(2)} ${yCoord(r[key]).toFixed(2)}`).join(' ');
+
+ return (
+
+ {/* gridlines + axis labels */}
+ {[0, 0.25, 0.5, 0.75, 1].map((y) => (
+
+
+ {y.toFixed(2)}
+
+ ))}
+ {/* ideal curve, dashed */}
+
+ {/* measured P(0) */}
+
+ {/* measured P(1) */}
+
+ {/* x ticks */}
+ {rows[0].parameterLabel}
+ {rows[rows.length - 1].parameterLabel}
+ {/* legend */}
+
+
+ P(0) measured
+
+ P(1)
+
+ P(0) ideal
+
+
+ );
+}
+
+export default function QuantumSweepPanel() {
+ const [kind, setKind] = useState('phase');
+ const [shots, setShots] = useState(1024);
+ const [steps, setSteps] = useState(9);
+ const [noise, setNoise] = useState(0.05);
+ const [theta, setTheta] = useState(Math.PI / 2);
+ const [maxDepth, setMaxDepth] = useState(12);
+ const [runToken, setRunToken] = useState(0);
+
+ const rows = useMemo(() => {
+ void runToken;
+ return runSweep({ kind, shots, steps, noise, theta, maxDepth });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [kind, shots, steps, noise, theta, maxDepth, runToken]);
+
+ const summary = useMemo(() => sweepSummary(rows), [rows]);
+ const kindMeta = KINDS.find((k) => k.id === kind) || KINDS[0];
+
+ return (
+
+ Layer 103 · quantum sweep
+ Auto-sweep a parameter, download a CSV
+
+ Runs the L101 experiment across a range of parameters, plots P(0)
+ and P(1) against the closed-form ideal, and exports the same column
+ shape as the offline Qiskit suite at quantum_alignment/results/.
+ Compare browser-sim curves with hardware curves directly.
+
+
+
+ {KINDS.map((k) => (
+ setKind(k.id)} title={k.help}>
+ {k.label}
+
+ ))}
+ setRunToken((t) => t + 1)}>Re-run
+ csvDownload(rows, kind)} disabled={!rows.length}>
+ Download CSV
+
+
+
+
+
+
+ Steps
+ {steps}
+
+
setSteps(parseInt(e.target.value, 10))} style={{ width: '100%' }} />
+
+ {kind !== 'noise' && (
+
+
+ Noise (fixed)
+ {noise.toFixed(2)}
+
+
setNoise(parseFloat(e.target.value))} style={{ width: '100%' }} />
+
+ )}
+ {kind === 'noise' && (
+
+
+ θ (fixed) — phase circuit angle
+ {theta.toFixed(2)}
+
+
setTheta(parseFloat(e.target.value))} style={{ width: '100%' }} />
+
+ )}
+ {kind === 'depth' && (
+
+
+ Max X·X depth
+ {maxDepth}
+
+
setMaxDepth(parseInt(e.target.value, 10))} style={{ width: '100%' }} />
+
+ )}
+
+ Shots:
+ {SHOTS_OPTIONS.map((s) => (
+ setShots(s)}>{s}
+ ))}
+
+
+
+
+
+ {summary && (
+
+
{summary.n} rows
+
avg error {summary.avgError.toFixed(3)}
+
max error {summary.maxError.toFixed(3)}
+
P(0) range {summary.rangeP0.toFixed(3)}
+
avg coherence {summary.avgCoherence.toFixed(0)} /100
+
+ )}
+
+
+ {kindMeta.help} CSV columns mirror the Qiskit suite where they overlap.
+
+
+ );
+}
diff --git a/brainsnn-r3f-app/src/utils/bellPair.js b/brainsnn-r3f-app/src/utils/bellPair.js
new file mode 100644
index 0000000..4157985
--- /dev/null
+++ b/brainsnn-r3f-app/src/utils/bellPair.js
@@ -0,0 +1,237 @@
+/**
+ * Layer 102 — Bell Pair Lab utilities
+ *
+ * Two-qubit entanglement, browser-native. Builds the Bell state
+ *
+ * |Φ+⟩ = (|00⟩ + |11⟩) / √2
+ *
+ * by running |00⟩ → H ⊗ I → CNOT(0,1), and lets the user rotate one of the
+ * qubits before measurement to see correlations break / persist.
+ *
+ * Convention: a 2-qubit state is a length-4 array of complex amplitudes,
+ * indexed as state[2*q1 + q0] for basis ket |q1 q0⟩. Qubit 0 is the
+ * "right" / least-significant bit.
+ *
+ * Reuses the complex helpers from quantumCoherence.js. Single-qubit gates
+ * are lifted into the 2-qubit space pair-by-pair to keep allocations tiny.
+ */
+
+import { complex, mulComplex, abs2 } from './quantumCoherence.js';
+
+const SQRT1_2 = 1 / Math.SQRT2;
+
+// ---------- 2-qubit state helpers ------------------------------------------
+
+export function zeroPair() {
+ return [complex(1, 0), complex(0, 0), complex(0, 0), complex(0, 0)];
+}
+
+function cloneState(state) {
+ return state.map((z) => ({ re: z.re, im: z.im }));
+}
+
+/**
+ * Apply a 2x2 matrix `[[m00, m01], [m10, m11]]` (each entry complex) to one
+ * qubit of a 2-qubit state. `qubit` is 0 or 1; we pair indices that differ
+ * only in that bit and apply the matrix to each pair.
+ */
+function applySingleQubit(state, qubit, m00, m01, m10, m11) {
+ const out = cloneState(state);
+ const stride = 1 << qubit;
+ for (let i = 0; i < 4; i += 1) {
+ if ((i & stride) !== 0) continue;
+ const j = i | stride;
+ const a = state[i];
+ const b = state[j];
+ // out[i] = m00*a + m01*b
+ out[i] = {
+ re: m00.re * a.re - m00.im * a.im + m01.re * b.re - m01.im * b.im,
+ im: m00.re * a.im + m00.im * a.re + m01.re * b.im + m01.im * b.re,
+ };
+ // out[j] = m10*a + m11*b
+ out[j] = {
+ re: m10.re * a.re - m10.im * a.im + m11.re * b.re - m11.im * b.im,
+ im: m10.re * a.im + m10.im * a.re + m11.re * b.im + m11.im * b.re,
+ };
+ }
+ return out;
+}
+
+export function applyHadamard(state, qubit) {
+ const h = complex(SQRT1_2, 0);
+ const negH = complex(-SQRT1_2, 0);
+ return applySingleQubit(state, qubit, h, h, h, negH);
+}
+
+export function applyPauliX(state, qubit) {
+ return applySingleQubit(state, qubit, complex(0, 0), complex(1, 0), complex(1, 0), complex(0, 0));
+}
+
+/**
+ * RY(θ) = [[cos(θ/2), -sin(θ/2)], [sin(θ/2), cos(θ/2)]] — real-valued,
+ * which is convenient because it produces measurement-axis rotations
+ * that the user can see in the joint distribution.
+ */
+export function applyRY(state, qubit, theta) {
+ const c = complex(Math.cos(theta / 2), 0);
+ const s = complex(Math.sin(theta / 2), 0);
+ const negS = complex(-s.re, 0);
+ return applySingleQubit(state, qubit, c, negS, s, c);
+}
+
+/** RZ on a 2-qubit state (single qubit). */
+export function applyRZQubit(state, qubit, theta) {
+ const half = theta / 2;
+ const m00 = complex(Math.cos(-half), Math.sin(-half));
+ const m11 = complex(Math.cos(half), Math.sin(half));
+ return applySingleQubit(state, qubit, m00, complex(0, 0), complex(0, 0), m11);
+}
+
+/**
+ * CNOT — controlled-X. Flips `target` iff `control` is |1⟩.
+ * Implemented as a permutation of amplitudes.
+ */
+export function applyCNOT(state, control, target) {
+ if (control === target) return cloneState(state);
+ const out = cloneState(state);
+ for (let i = 0; i < 4; i += 1) {
+ const cBit = (i >> control) & 1;
+ if (cBit === 1) {
+ const j = i ^ (1 << target);
+ if (j > i) {
+ const tmp = out[i];
+ out[i] = out[j];
+ out[j] = tmp;
+ }
+ }
+ }
+ return out;
+}
+
+// ---------- joint measurement ----------------------------------------------
+
+/**
+ * Returns the 4-element probability vector for outcomes
+ * |00⟩, |01⟩, |10⟩, |11⟩ (normalized).
+ */
+export function jointDistribution(state) {
+ const probs = state.map(abs2);
+ const total = probs.reduce((a, v) => a + v, 0) || 1;
+ return probs.map((p) => p / total);
+}
+
+/**
+ * Sample `shots` joint measurements; returns counts [c00, c01, c10, c11].
+ */
+export function sampleJointShots(distribution, shots) {
+ const total = Math.max(0, Math.floor(shots) || 0);
+ if (!total) return [0, 0, 0, 0];
+ const counts = [0, 0, 0, 0];
+ // Build cumulative thresholds.
+ const cum = [];
+ let acc = 0;
+ for (let i = 0; i < 4; i += 1) {
+ acc += distribution[i] ?? 0;
+ cum.push(acc);
+ }
+ for (let s = 0; s < total; s += 1) {
+ const r = Math.random();
+ for (let i = 0; i < 4; i += 1) {
+ if (r < cum[i]) {
+ counts[i] += 1;
+ break;
+ }
+ }
+ }
+ return counts;
+}
+
+/**
+ * Probability that the two qubits agree (both 0 or both 1).
+ * For a pure Bell state |Φ+⟩ this is 1; rotation breaks the correlation
+ * smoothly, which is the lesson of the panel.
+ */
+export function correlationStrength(distribution) {
+ const agree = (distribution[0] ?? 0) + (distribution[3] ?? 0);
+ const disagree = (distribution[1] ?? 0) + (distribution[2] ?? 0);
+ return agree - disagree; // -1 (anti-correlated) … 1 (correlated)
+}
+
+// ---------- Bell pair experiment -------------------------------------------
+
+/**
+ * Build the Bell state, optionally rotate qubit 0 by `rotationTheta` (RY) to
+ * preview correlation breakdown, and measure.
+ *
+ * |00⟩ → H ⊗ I → CNOT(0, 1) → RY(theta) on qubit 0 → joint measurement.
+ *
+ * Noise model: depolarizing-style. We mix the pure-state distribution with
+ * the uniform distribution by weight `noise` — i.e. with probability `noise`
+ * we pretend the qubits got randomized. This cleanly takes correlation from
+ * +1 (noise=0) toward 0 (noise=1), unlike a bit-flip channel that just
+ * toggles between perfectly correlated and perfectly anti-correlated.
+ */
+export function runBellPairExperiment({
+ rotationTheta = 0,
+ shots = 1024,
+ noise = 0,
+} = {}) {
+ let state = zeroPair();
+ state = applyHadamard(state, 0);
+ state = applyCNOT(state, 0, 1);
+ if (rotationTheta !== 0) {
+ state = applyRY(state, 0, rotationTheta);
+ }
+
+ let distribution = jointDistribution(state);
+ const n = Math.max(0, Math.min(1, Number(noise) || 0));
+ if (n > 0) {
+ distribution = distribution.map((p) => (1 - n) * p + n * 0.25);
+ }
+ const counts = sampleJointShots(distribution, shots);
+ const correlation = correlationStrength(distribution);
+ return {
+ kind: 'bell',
+ rotationTheta,
+ shots,
+ noise: n,
+ distribution,
+ counts,
+ correlation,
+ finalState: state,
+ };
+}
+
+// ---------- brain mapping ---------------------------------------------------
+
+/**
+ * Bell-pair brain mapping — entanglement = strong inter-region binding.
+ * - HPC ↑ correlation (binding two threads = associative memory)
+ * - PFC ↑ |correlation| (executive lock-on to a coherent answer)
+ * - CTX ↑ shots / 4096 (more data = more cortex)
+ * - AMY ↑ noise
+ * - THL ↑ when |rotationTheta| > 0 (relay traffic between qubits)
+ * - BG ↑ dominance of the lead outcome
+ * - CBL ↑ noise * (1 - |correlation|) (correction work when binding fails)
+ */
+export function mapBellToBrainState(result) {
+ if (!result || !result.distribution) {
+ return { CTX: 0, HPC: 0, THL: 0, AMY: 0, BG: 0, PFC: 0, CBL: 0 };
+ }
+ const corr = Math.max(-1, Math.min(1, result.correlation || 0));
+ const noise = Math.max(0, Math.min(1, Number(result.noise) || 0));
+ const rot = Math.abs(result.rotationTheta || 0);
+ const shots = Math.max(1, Number(result.shots) || 1);
+ const lead = Math.max(...result.distribution);
+ const dominance = Math.max(0, lead - 0.25); // 0 = uniform, 0.75 = pure
+ const clamp = (v) => Math.max(-1, Math.min(1, v));
+ return {
+ HPC: clamp(corr * 0.6),
+ PFC: clamp(Math.abs(corr) * 0.55),
+ CTX: clamp(Math.min(1, Math.log10(shots) / 4) * 0.4),
+ AMY: clamp(noise * 0.5),
+ THL: clamp(Math.min(1, rot / Math.PI) * 0.45),
+ BG: clamp(dominance * 0.55),
+ CBL: clamp(noise * (1 - Math.abs(corr)) * 0.6),
+ };
+}
diff --git a/brainsnn-r3f-app/src/utils/bellPair.test.mjs b/brainsnn-r3f-app/src/utils/bellPair.test.mjs
new file mode 100644
index 0000000..1c934db
--- /dev/null
+++ b/brainsnn-r3f-app/src/utils/bellPair.test.mjs
@@ -0,0 +1,117 @@
+/**
+ * Layer 102 — Bell Pair Lab tests.
+ *
+ * Run from brainsnn-r3f-app/ with:
+ * node --test src/utils/bellPair.test.mjs
+ */
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import {
+ zeroPair,
+ applyHadamard,
+ applyCNOT,
+ applyPauliX,
+ applyRY,
+ jointDistribution,
+ correlationStrength,
+ runBellPairExperiment,
+ mapBellToBrainState,
+} from './bellPair.js';
+
+test('zeroPair starts at |00>', () => {
+ const dist = jointDistribution(zeroPair());
+ assert.ok(dist[0] > 0.999);
+ assert.ok(dist[1] < 0.001);
+ assert.ok(dist[2] < 0.001);
+ assert.ok(dist[3] < 0.001);
+});
+
+test('Hadamard on qubit 0 of |00> gives (|00>+|01>)/sqrt(2)', () => {
+ const state = applyHadamard(zeroPair(), 0);
+ const dist = jointDistribution(state);
+ assert.ok(Math.abs(dist[0] - 0.5) < 1e-9);
+ assert.ok(Math.abs(dist[1] - 0.5) < 1e-9);
+ assert.ok(Math.abs(dist[2]) < 1e-9);
+ assert.ok(Math.abs(dist[3]) < 1e-9);
+});
+
+test('CNOT(0,1) flips qubit 1 only when qubit 0 is 1', () => {
+ // |01> (qubit 0 = 1, qubit 1 = 0) -> |11>
+ let state = applyPauliX(zeroPair(), 0);
+ state = applyCNOT(state, 0, 1);
+ const dist = jointDistribution(state);
+ assert.ok(dist[3] > 0.999, `expected |11>, got ${JSON.stringify(dist)}`);
+});
+
+test('Bell state |Φ+> = H ⊗ I then CNOT gives 50/50 on |00> and |11>', () => {
+ let state = applyHadamard(zeroPair(), 0);
+ state = applyCNOT(state, 0, 1);
+ const dist = jointDistribution(state);
+ assert.ok(Math.abs(dist[0] - 0.5) < 1e-9);
+ assert.ok(Math.abs(dist[3] - 0.5) < 1e-9);
+ assert.ok(dist[1] < 1e-9);
+ assert.ok(dist[2] < 1e-9);
+});
+
+test('correlationStrength of Bell state is +1', () => {
+ let state = applyHadamard(zeroPair(), 0);
+ state = applyCNOT(state, 0, 1);
+ const dist = jointDistribution(state);
+ assert.ok(correlationStrength(dist) > 0.999);
+});
+
+test('rotation by pi/2 on Bell state breaks correlation toward 0', () => {
+ let state = applyHadamard(zeroPair(), 0);
+ state = applyCNOT(state, 0, 1);
+ state = applyRY(state, 0, Math.PI / 2);
+ const dist = jointDistribution(state);
+ const corr = correlationStrength(dist);
+ assert.ok(Math.abs(corr) < 0.05, `expected ~0 correlation after pi/2 rotation; got ${corr}`);
+});
+
+test('rotation by pi flips correlation to anti-correlated', () => {
+ let state = applyHadamard(zeroPair(), 0);
+ state = applyCNOT(state, 0, 1);
+ state = applyRY(state, 0, Math.PI);
+ const dist = jointDistribution(state);
+ // After RY(pi) on qubit 0 of (|00>+|11>)/sqrt(2): qubit 0 flips amplitude
+ // sign per branch — measurement now favors |10> and |01> (anti-correlated).
+ assert.ok(correlationStrength(dist) < -0.99,
+ `expected fully anti-correlated, got ${correlationStrength(dist)}`);
+});
+
+test('runBellPairExperiment with rotationTheta=0 noise=0 reports correlation=1', () => {
+ const res = runBellPairExperiment({ rotationTheta: 0, shots: 4096, noise: 0 });
+ assert.equal(res.kind, 'bell');
+ assert.ok(res.correlation > 0.999, `got ${res.correlation}`);
+ // counts should be split between [0] and [3], with [1] and [2] tiny
+ assert.ok(res.counts[0] + res.counts[3] === 4096);
+});
+
+test('runBellPairExperiment with high noise lowers correlation', () => {
+ const clean = runBellPairExperiment({ rotationTheta: 0, shots: 4096, noise: 0 });
+ const noisy = runBellPairExperiment({ rotationTheta: 0, shots: 4096, noise: 0.9 });
+ assert.ok(noisy.correlation < clean.correlation,
+ `noisy ${noisy.correlation} >= clean ${clean.correlation}`);
+});
+
+test('mapBellToBrainState: HPC rises with correlation', () => {
+ const correlated = mapBellToBrainState(runBellPairExperiment({ rotationTheta: 0, shots: 256, noise: 0 }));
+ const decorrelated = mapBellToBrainState(runBellPairExperiment({ rotationTheta: Math.PI / 2, shots: 256, noise: 0 }));
+ assert.ok(correlated.HPC > decorrelated.HPC,
+ `HPC: correlated ${correlated.HPC} should exceed decorrelated ${decorrelated.HPC}`);
+});
+
+test('mapBellToBrainState: AMY rises with noise', () => {
+ const clean = mapBellToBrainState(runBellPairExperiment({ rotationTheta: 0, shots: 256, noise: 0 }));
+ const noisy = mapBellToBrainState(runBellPairExperiment({ rotationTheta: 0, shots: 256, noise: 0.85 }));
+ assert.ok(noisy.AMY > clean.AMY);
+});
+
+test('all brain deltas are bounded to [-1, 1]', () => {
+ const res = runBellPairExperiment({ rotationTheta: Math.PI / 3, shots: 1024, noise: 0.4 });
+ const deltas = mapBellToBrainState(res);
+ for (const [region, v] of Object.entries(deltas)) {
+ assert.ok(v >= -1 && v <= 1, `${region}=${v} out of range`);
+ }
+});
diff --git a/brainsnn-r3f-app/src/utils/layerCatalog.js b/brainsnn-r3f-app/src/utils/layerCatalog.js
index 110bd86..fd23169 100644
--- a/brainsnn-r3f-app/src/utils/layerCatalog.js
+++ b/brainsnn-r3f-app/src/utils/layerCatalog.js
@@ -107,6 +107,10 @@ export const LAYER_CATALOG = [
{ id: 98, name: 'Theme + A11y', group: 'view', blurb: 'Dark/light, high-contrast, reduced-motion, font scale.' },
{ id: 99, name: 'Federated Community Firewall', group: 'firewall', blurb: 'Weekly-rotated community rule pack.' },
{ id: 100, name: 'Milestone Dashboard', group: 'view', blurb: '100 layers shipped — synthesis + personal stats.' },
+ { id: 101, name: 'Quantum Coherence Lab', group: 'experimental', blurb: 'In-browser single-qubit phase / interference / decoherence sandbox.' },
+ { id: 102, name: 'Bell Pair Lab', group: 'experimental', blurb: 'Two-qubit |Φ+⟩ entanglement + RY rotation + correlation strength.' },
+ { id: 103, name: 'Quantum Sweep', group: 'experimental', blurb: 'Auto-sweep θ / noise / depth, plot vs ideal, download CSV.' },
+ { id: 104, name: 'Quantum Glossary', group: 'experimental', blurb: 'Searchable glossary of every quantum term used in L101–L103.' },
];
export const LAYER_GROUPS = {
@@ -116,6 +120,7 @@ export const LAYER_GROUPS = {
data: { label: 'Data & State', color: '#5ee69a' },
backend: { label: 'Backend & Agents', color: '#77dbe4' },
progression: { label: 'Progression', color: '#e57b40' },
+ experimental: { label: 'Experimental Simulation', color: '#5ad4ff' },
};
export function searchLayers(query = '') {
diff --git a/brainsnn-r3f-app/src/utils/quantumCoherence.js b/brainsnn-r3f-app/src/utils/quantumCoherence.js
new file mode 100644
index 0000000..1ad192e
--- /dev/null
+++ b/brainsnn-r3f-app/src/utils/quantumCoherence.js
@@ -0,0 +1,347 @@
+/**
+ * Layer 101 — Quantum Coherence Lab
+ *
+ * Browser-native, JavaScript-only simulation of a single qubit going through
+ * a tiny circuit:
+ *
+ * |0⟩ → H → RZ(θ) → H → M
+ *
+ * Goal: teach the physical mechanism behind "alignment" — superposition,
+ * phase, interference, observation, noise, and decoherence — without
+ * claiming any of the metaphors are literal physics.
+ *
+ * No backend, no IBM/OriginQ keys. The function shapes here are intentionally
+ * compatible with the Qiskit suite at /quantum_alignment/, so a future
+ * version can swap the local sampler for hardware (Aer noisy / IBM Quantum /
+ * OriginQ) without changing the panel.
+ *
+ * NOTHING in this file proves multiverse theory, consciousness collapse,
+ * Planck foam, or spiritual portals. The metaphor framing lives only in
+ * the panel UI, not in the math.
+ */
+
+// ---------- complex helpers -------------------------------------------------
+
+export function complex(re, im = 0) {
+ return { re, im };
+}
+
+export function addComplex(a, b) {
+ return { re: a.re + b.re, im: a.im + b.im };
+}
+
+export function mulComplex(a, b) {
+ return {
+ re: a.re * b.re - a.im * b.im,
+ im: a.re * b.im + a.im * b.re,
+ };
+}
+
+/** |z|^2 — squared magnitude. */
+export function abs2(z) {
+ return z.re * z.re + z.im * z.im;
+}
+
+// ---------- single-qubit state ---------------------------------------------
+
+/**
+ * State is a 2-vector of complex amplitudes [α, β] for |0⟩ and |1⟩.
+ * normalizeState scales so |α|^2 + |β|^2 = 1 (or returns |0⟩ if zero).
+ */
+export function normalizeState(state) {
+ const total = abs2(state[0]) + abs2(state[1]);
+ if (!Number.isFinite(total) || total <= 1e-12) {
+ return [complex(1, 0), complex(0, 0)];
+ }
+ const k = 1 / Math.sqrt(total);
+ return [
+ complex(state[0].re * k, state[0].im * k),
+ complex(state[1].re * k, state[1].im * k),
+ ];
+}
+
+const SQRT1_2 = 1 / Math.SQRT2;
+
+/** Hadamard — creates / collapses superposition. */
+export function applyH(state) {
+ const a = state[0];
+ const b = state[1];
+ return [
+ complex((a.re + b.re) * SQRT1_2, (a.im + b.im) * SQRT1_2),
+ complex((a.re - b.re) * SQRT1_2, (a.im - b.im) * SQRT1_2),
+ ];
+}
+
+/** Pauli-X — bit flip. */
+export function applyX(state) {
+ return [state[1], state[0]];
+}
+
+/** Pauli-Z — phase flip on |1⟩. */
+export function applyZ(state) {
+ return [state[0], complex(-state[1].re, -state[1].im)];
+}
+
+/**
+ * RZ(θ) — rotate phase. Standard form: diag(e^{-iθ/2}, e^{+iθ/2}).
+ * Global phase on |0⟩ is harmless for measurement; the *relative* phase
+ * between the two basis amplitudes is what matters for interference.
+ */
+export function applyRZ(state, theta) {
+ const half = theta / 2;
+ const c0 = complex(Math.cos(-half), Math.sin(-half));
+ const c1 = complex(Math.cos(half), Math.sin(half));
+ return [mulComplex(c0, state[0]), mulComplex(c1, state[1])];
+}
+
+// ---------- measurement -----------------------------------------------------
+
+/** Returns [P(0), P(1)] for a normalized (or near-normalized) state. */
+export function measureDistribution(state) {
+ const norm = normalizeState(state);
+ const p0 = abs2(norm[0]);
+ const p1 = abs2(norm[1]);
+ const total = p0 + p1 || 1;
+ return [p0 / total, p1 / total];
+}
+
+/**
+ * Multinomial sample of `shots` measurements given a [P(0), P(1)] distribution.
+ * Uses Math.random; tests should compare proportions with tolerances rather
+ * than exact counts.
+ */
+export function sampleShots(distribution, shots) {
+ const total = Math.max(0, Math.floor(shots) || 0);
+ if (!total) return [0, 0];
+ const p0 = Math.max(0, Math.min(1, distribution[0] ?? 0));
+ let zeros = 0;
+ for (let i = 0; i < total; i += 1) {
+ if (Math.random() < p0) zeros += 1;
+ }
+ return [zeros, total - zeros];
+}
+
+// ---------- simple noise model ---------------------------------------------
+
+/**
+ * Dephasing damps the off-diagonal coherence. We model the state as a
+ * length-2 amplitude vector but simulate the qualitative effect of T2
+ * dephasing by squeezing the imaginary part of β toward zero. This
+ * reproduces "lose interference fringe with noise" without a full
+ * density-matrix sim.
+ */
+function dephase(state, factor) {
+ const k = Math.max(0, Math.min(1, factor));
+ if (k >= 0.999) return state;
+ return [
+ state[0],
+ complex(state[1].re, state[1].im * k),
+ ];
+}
+
+/**
+ * Mix the ideal [P(0), P(1)] distribution with the uniform [0.5, 0.5] by
+ * weight `noise`. This is a depolarizing-style channel applied at the
+ * distribution level, which keeps tests deterministic in expectation —
+ * unlike a sampled bit-flip, which produces a single ±1 trajectory.
+ */
+function depolarizeDistribution(distribution, noise) {
+ const n = Math.max(0, Math.min(1, noise));
+ if (n <= 0) return distribution;
+ const [p0, p1] = distribution;
+ return [(1 - n) * p0 + n * 0.5, (1 - n) * p1 + n * 0.5];
+}
+
+// ---------- experiments -----------------------------------------------------
+
+/**
+ * H → RZ(θ) → H → M.
+ *
+ * Pure (noise = 0):
+ * θ = 0 → P(0) = 1
+ * θ = π → P(1) = 1
+ * θ = π/2 → P(0) = P(1) = 0.5
+ *
+ * Higher noise damps the interference fringe, pushing both outcomes toward
+ * 0.5 regardless of θ. This is the "alignment" picture: phase coherence is
+ * the resource; noise is the enemy.
+ *
+ * Mirrors `phase_circuit(theta)` in /quantum_alignment/quantum_alignment_tests.py.
+ */
+export function runPhaseExperiment({ theta = 0, shots = 1024, noise = 0 } = {}) {
+ let state = [complex(1, 0), complex(0, 0)];
+ state = applyH(state);
+ state = applyRZ(state, theta);
+ // Dephasing between RZ and final H damps the interference fringe.
+ state = dephase(state, Math.max(0, 1 - noise));
+ state = applyH(state);
+ // Mix in uniform at the distribution level so noise=1 → 50/50 deterministically.
+ const distribution = depolarizeDistribution(measureDistribution(state), noise);
+ const counts = sampleShots(distribution, shots);
+ return {
+ kind: 'phase',
+ theta,
+ shots,
+ noise,
+ distribution,
+ counts,
+ finalState: state,
+ };
+}
+
+/**
+ * H → (optional middle measurement) → H → M.
+ *
+ * Without the middle measurement: H ∘ H = I, so |0⟩ stays |0⟩ → P(0) ≈ 1.
+ * With the middle measurement: superposition collapses, the second H
+ * spreads it back out → P(0) ≈ P(1) ≈ 0.5.
+ *
+ * Demonstrates "observation kills interference" — the quantum-Zeno /
+ * which-path lesson, without invoking consciousness.
+ *
+ * Mirrors `observation_circuit_a/b` in /quantum_alignment/.
+ */
+export function runObservationExperiment({
+ shots = 1024,
+ observeMidway = false,
+ noise = 0,
+} = {}) {
+ let state = [complex(1, 0), complex(0, 0)];
+ state = applyH(state);
+
+ if (observeMidway) {
+ const [p0] = measureDistribution(state);
+ const collapsed = Math.random() < p0
+ ? [complex(1, 0), complex(0, 0)]
+ : [complex(0, 0), complex(1, 0)];
+ state = collapsed;
+ } else {
+ // Apply mild dephasing instead — a "soft watch" that costs coherence.
+ state = dephase(state, Math.max(0, 1 - noise));
+ }
+
+ state = applyH(state);
+
+ const distribution = depolarizeDistribution(measureDistribution(state), noise);
+ const counts = sampleShots(distribution, shots);
+ return {
+ kind: 'observation',
+ observeMidway,
+ shots,
+ noise,
+ distribution,
+ counts,
+ finalState: state,
+ };
+}
+
+/**
+ * Stack `xxPairs` X-X pairs in a row. Algebraically X·X = I, so a perfect
+ * machine returns P(0) = 1 regardless of depth. With noise, each gate is
+ * a chance to misfire, so deeper circuits decohere — which is the point.
+ *
+ * Mirrors `noise_depth_circuit(num_xx_pairs)` in /quantum_alignment/.
+ */
+export function runDecoherenceExperiment({
+ xxPairs = 1,
+ shots = 1024,
+ noise = 0,
+} = {}) {
+ const pairs = Math.max(0, Math.floor(xxPairs) || 0);
+ // Pure-state algebra: X·X = I, so the ideal distribution is always [1, 0].
+ // Effective depolarization compounds per pair: 1 - (1 - noise)^pairs.
+ // This keeps the result deterministic in expectation while still showing
+ // depth-driven decoherence growth.
+ const ideal = [1, 0];
+ const perPair = Math.max(0, Math.min(1, noise));
+ const effective = pairs === 0 ? perPair : 1 - Math.pow(1 - perPair, pairs);
+ const distribution = depolarizeDistribution(ideal, effective);
+ const counts = sampleShots(distribution, shots);
+ return {
+ kind: 'decoherence',
+ xxPairs: pairs,
+ shots,
+ noise,
+ distribution,
+ counts,
+ };
+}
+
+// ---------- coherence score -------------------------------------------------
+
+/**
+ * 0 – 100 score capturing how much "quantum-ness" survived the run.
+ * - More noise lowers the score.
+ * - Deeper circuits lower the score.
+ * - A midway measurement nukes the score (you broke the wavefunction).
+ *
+ * Pure determinism (no Math.random) so the UI is stable.
+ */
+export function coherenceScore({
+ noise = 0,
+ depth = 1,
+ observedMidway = false,
+} = {}) {
+ const n = Math.max(0, Math.min(1, Number(noise) || 0));
+ const d = Math.max(1, Math.floor(Number(depth) || 1));
+ const observationPenalty = observedMidway ? 0.5 : 0;
+ const depthFactor = Math.exp(-0.18 * (d - 1));
+ const noiseFactor = 1 - n * 0.85;
+ const raw = depthFactor * noiseFactor - observationPenalty;
+ const clamped = Math.max(0, Math.min(1, raw));
+ return Math.round(clamped * 100);
+}
+
+// ---------- brain mapping ---------------------------------------------------
+
+/**
+ * Convert a quantum experiment result into per-region brain deltas.
+ * Returns a simple { REGION: delta } object — caller decides how strongly
+ * to nudge state.regions (typical pattern is `delta * 0.3`).
+ *
+ * - PFC ↑ with coherenceScore (alignment / executive coherence)
+ * - AMY ↑ with noise (the system is rattled)
+ * - THL ↑ with signal routing (more shots = more relay traffic)
+ * - HPC ↑ when prior state survives (low depth + low noise = memory holds)
+ * - BG ↑ when one outcome dominates (decisive gating)
+ * - CBL ↑ when error / noise correction is high (cerebellum tunes timing)
+ * - CTX ↑ with experiment complexity (more gates = more cortical work)
+ *
+ * Deltas are bounded to [-1, 1]; consumers should scale further.
+ */
+export function mapQuantumToBrainState(result) {
+ if (!result || !result.distribution) {
+ return { CTX: 0, HPC: 0, THL: 0, AMY: 0, BG: 0, PFC: 0, CBL: 0 };
+ }
+ const noise = Math.max(0, Math.min(1, Number(result.noise) || 0));
+ const shots = Math.max(1, Number(result.shots) || 1);
+ const depth = Math.max(1, Number(result.xxPairs ?? 1));
+ const observedMidway = !!result.observeMidway;
+
+ const score = coherenceScore({ noise, depth, observedMidway }) / 100;
+
+ const [p0, p1] = result.distribution;
+ const dominance = Math.abs((p0 ?? 0) - (p1 ?? 0));
+ const survival = (p0 ?? 0);
+
+ const routing = Math.min(1, Math.log10(shots) / 4);
+ const complexity = Math.min(
+ 1,
+ (result.kind === 'decoherence' ? depth / 10 : 0.35)
+ + (result.kind === 'observation' && observedMidway ? 0.15 : 0)
+ + (result.kind === 'phase' ? 0.4 : 0),
+ );
+ const correction = noise * (1 - score);
+
+ const clamp = (v) => Math.max(-1, Math.min(1, v));
+
+ return {
+ PFC: clamp(score * 0.6),
+ AMY: clamp(noise * 0.5),
+ THL: clamp(routing * 0.45),
+ HPC: clamp(survival * 0.5 * (1 - noise * 0.5)),
+ BG: clamp(dominance * 0.5),
+ CBL: clamp(correction * 0.6),
+ CTX: clamp(complexity * 0.5),
+ };
+}
diff --git a/brainsnn-r3f-app/src/utils/quantumCoherence.test.mjs b/brainsnn-r3f-app/src/utils/quantumCoherence.test.mjs
new file mode 100644
index 0000000..cf226a4
--- /dev/null
+++ b/brainsnn-r3f-app/src/utils/quantumCoherence.test.mjs
@@ -0,0 +1,171 @@
+/**
+ * Layer 101 — Quantum Coherence Lab tests.
+ *
+ * Run from brainsnn-r3f-app/ with:
+ * node --test src/utils/quantumCoherence.test.mjs
+ *
+ * Uses Node's built-in test runner (Node 18+). No extra dev deps.
+ */
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import {
+ runPhaseExperiment,
+ runObservationExperiment,
+ runDecoherenceExperiment,
+ coherenceScore,
+ measureDistribution,
+ applyH,
+ applyX,
+ applyZ,
+ applyRZ,
+ complex,
+ abs2,
+ normalizeState,
+ mapQuantumToBrainState,
+} from './quantumCoherence.js';
+
+const SHOTS = 4096;
+
+test('phase experiment with theta=0 returns high P(0)', () => {
+ const res = runPhaseExperiment({ theta: 0, shots: SHOTS, noise: 0 });
+ assert.equal(res.kind, 'phase');
+ assert.ok(
+ res.distribution[0] > 0.95,
+ `expected P(0) > 0.95, got ${res.distribution[0]}`,
+ );
+});
+
+test('phase experiment with theta=pi returns high P(1)', () => {
+ const res = runPhaseExperiment({ theta: Math.PI, shots: SHOTS, noise: 0 });
+ assert.ok(
+ res.distribution[1] > 0.95,
+ `expected P(1) > 0.95, got ${res.distribution[1]}`,
+ );
+});
+
+test('phase experiment at theta=pi/2 splits ~50/50', () => {
+ const res = runPhaseExperiment({ theta: Math.PI / 2, shots: SHOTS, noise: 0 });
+ assert.ok(
+ Math.abs(res.distribution[0] - 0.5) < 0.05,
+ `expected P(0) ~ 0.5, got ${res.distribution[0]}`,
+ );
+});
+
+test('observation experiment with observeMidway=false returns high P(0)', () => {
+ // H ∘ H = I, so |0⟩ stays |0⟩.
+ const res = runObservationExperiment({ shots: SHOTS, observeMidway: false, noise: 0 });
+ assert.ok(
+ res.distribution[0] > 0.95,
+ `expected P(0) > 0.95 with no midway observation, got ${res.distribution[0]}`,
+ );
+});
+
+test('observation experiment with observeMidway=true is more random', () => {
+ // Average over runs since the midway measurement is stochastic.
+ let p0Sum = 0;
+ const trials = 40;
+ for (let i = 0; i < trials; i += 1) {
+ const r = runObservationExperiment({ shots: 256, observeMidway: true, noise: 0 });
+ p0Sum += r.distribution[0];
+ }
+ const avgP0 = p0Sum / trials;
+ assert.ok(
+ avgP0 > 0.35 && avgP0 < 0.65,
+ `expected avg P(0) ~ 0.5 after midway measurement, got ${avgP0.toFixed(3)}`,
+ );
+});
+
+test('increasing noise lowers coherenceScore', () => {
+ const low = coherenceScore({ noise: 0, depth: 1, observedMidway: false });
+ const mid = coherenceScore({ noise: 0.5, depth: 1, observedMidway: false });
+ const high = coherenceScore({ noise: 1, depth: 1, observedMidway: false });
+ assert.ok(low > mid, `expected ${low} > ${mid}`);
+ assert.ok(mid > high, `expected ${mid} > ${high}`);
+});
+
+test('increasing depth lowers coherenceScore', () => {
+ const shallow = coherenceScore({ noise: 0.1, depth: 1, observedMidway: false });
+ const deeper = coherenceScore({ noise: 0.1, depth: 5, observedMidway: false });
+ const deepest = coherenceScore({ noise: 0.1, depth: 12, observedMidway: false });
+ assert.ok(shallow > deeper, `expected ${shallow} > ${deeper}`);
+ assert.ok(deeper > deepest, `expected ${deeper} > ${deepest}`);
+});
+
+test('observing midway dings coherenceScore', () => {
+ const watched = coherenceScore({ noise: 0, depth: 1, observedMidway: true });
+ const unwatched = coherenceScore({ noise: 0, depth: 1, observedMidway: false });
+ assert.ok(unwatched > watched, `expected ${unwatched} > ${watched}`);
+});
+
+test('decoherence experiment ideal (noise=0) returns ~P(0)=1', () => {
+ const res = runDecoherenceExperiment({ xxPairs: 8, shots: SHOTS, noise: 0 });
+ assert.ok(
+ res.distribution[0] > 0.99,
+ `X·X pairs are identity at noise=0; got P(0)=${res.distribution[0]}`,
+ );
+});
+
+test('decoherence experiment with noise lowers P(0) vs ideal', () => {
+ const ideal = runDecoherenceExperiment({ xxPairs: 6, shots: SHOTS, noise: 0 });
+ const noisy = runDecoherenceExperiment({ xxPairs: 6, shots: SHOTS, noise: 0.7 });
+ assert.ok(
+ noisy.distribution[0] < ideal.distribution[0],
+ `expected noisy P(0) < ideal P(0); got ${noisy.distribution[0]} vs ${ideal.distribution[0]}`,
+ );
+});
+
+test('Hadamard on |0> gives equal probabilities', () => {
+ const state = applyH([complex(1, 0), complex(0, 0)]);
+ const [p0, p1] = measureDistribution(state);
+ assert.ok(Math.abs(p0 - 0.5) < 1e-9);
+ assert.ok(Math.abs(p1 - 0.5) < 1e-9);
+});
+
+test('Pauli-X flips |0> to |1>', () => {
+ const state = applyX([complex(1, 0), complex(0, 0)]);
+ const [p0, p1] = measureDistribution(state);
+ assert.ok(p1 > 0.999);
+ assert.ok(p0 < 0.001);
+});
+
+test('Pauli-Z preserves probabilities (phase only)', () => {
+ const start = applyH([complex(1, 0), complex(0, 0)]);
+ const after = applyZ(start);
+ const before = measureDistribution(start);
+ const post = measureDistribution(after);
+ assert.ok(Math.abs(before[0] - post[0]) < 1e-9);
+});
+
+test('RZ(2π) acts as identity on probabilities', () => {
+ const start = applyH([complex(1, 0), complex(0, 0)]);
+ const after = applyRZ(start, Math.PI * 2);
+ assert.ok(Math.abs(abs2(start[0]) - abs2(after[0])) < 1e-9);
+ assert.ok(Math.abs(abs2(start[1]) - abs2(after[1])) < 1e-9);
+});
+
+test('normalizeState normalizes near-zero state to |0>', () => {
+ const norm = normalizeState([complex(0, 0), complex(0, 0)]);
+ assert.equal(norm[0].re, 1);
+ assert.equal(norm[1].re, 0);
+});
+
+test('mapQuantumToBrainState returns 7 region deltas in [-1, 1]', () => {
+ const res = runPhaseExperiment({ theta: 0, shots: 1024, noise: 0 });
+ const deltas = mapQuantumToBrainState(res);
+ for (const region of ['CTX', 'HPC', 'THL', 'AMY', 'BG', 'PFC', 'CBL']) {
+ assert.ok(region in deltas, `missing region ${region}`);
+ assert.ok(deltas[region] >= -1 && deltas[region] <= 1, `${region} out of range: ${deltas[region]}`);
+ }
+});
+
+test('mapQuantumToBrainState: AMY rises with noise', () => {
+ const clean = mapQuantumToBrainState(runPhaseExperiment({ theta: 0, shots: 256, noise: 0 }));
+ const noisy = mapQuantumToBrainState(runPhaseExperiment({ theta: 0, shots: 256, noise: 0.9 }));
+ assert.ok(noisy.AMY > clean.AMY, `expected noisy AMY > clean AMY; got ${noisy.AMY} vs ${clean.AMY}`);
+});
+
+test('mapQuantumToBrainState: PFC rises with coherence (clean run > noisy run)', () => {
+ const clean = mapQuantumToBrainState(runPhaseExperiment({ theta: 0, shots: 256, noise: 0 }));
+ const noisy = mapQuantumToBrainState(runPhaseExperiment({ theta: 0, shots: 256, noise: 0.95 }));
+ assert.ok(clean.PFC > noisy.PFC, `expected clean PFC > noisy PFC; got ${clean.PFC} vs ${noisy.PFC}`);
+});
diff --git a/brainsnn-r3f-app/src/utils/quantumSweep.js b/brainsnn-r3f-app/src/utils/quantumSweep.js
new file mode 100644
index 0000000..acdb3f2
--- /dev/null
+++ b/brainsnn-r3f-app/src/utils/quantumSweep.js
@@ -0,0 +1,202 @@
+/**
+ * Layer 103 — Quantum Sweep
+ *
+ * Automates parameter sweeps over the L101 single-qubit experiment family
+ * and emits a CSV with the same column shape as the offline Qiskit suite at
+ * /quantum_alignment/results/results.csv. The point: a user can run a
+ * sweep in the browser, download the CSV, and compare it directly against
+ * the Qiskit ideal/noisy/real CSV for the same parameters.
+ *
+ * Three sweep kinds:
+ * - 'phase' — sweeps θ ∈ [0, π] for fixed noise / shots
+ * - 'noise' — sweeps noise ∈ [0, 1] for fixed θ / shots
+ * - 'depth' — sweeps X·X pair count ∈ [0, maxDepth] for fixed
+ * noise / shots (decoherence experiment)
+ *
+ * Pure JS, no DOM, deterministic-ish (uses Math.random for shot sampling).
+ */
+
+import {
+ runPhaseExperiment,
+ runDecoherenceExperiment,
+ coherenceScore,
+} from './quantumCoherence.js';
+
+// ---------- sweep -----------------------------------------------------------
+
+function linspace(start, stop, count) {
+ if (count <= 1) return [start];
+ const step = (stop - start) / (count - 1);
+ const out = [];
+ for (let i = 0; i < count; i += 1) out.push(start + step * i);
+ return out;
+}
+
+function rangeInts(start, stop, count) {
+ // inclusive, integer-snapped, evenly spaced.
+ if (count <= 1) return [Math.round(start)];
+ const out = new Set();
+ const step = (stop - start) / (count - 1);
+ for (let i = 0; i < count; i += 1) {
+ out.add(Math.max(0, Math.round(start + step * i)));
+ }
+ return Array.from(out).sort((a, b) => a - b);
+}
+
+/**
+ * Run a sweep. `kind` is 'phase' | 'noise' | 'depth'. Returns an array of
+ * row objects. Each row carries enough context to reproduce: parameter,
+ * shots, noise, distribution, counts, expected probability for the ideal
+ * outcome where one is well-defined.
+ */
+export function runSweep({
+ kind = 'phase',
+ shots = 1024,
+ steps = 9,
+ noise = 0,
+ theta = 0,
+ maxDepth = 12,
+} = {}) {
+ const rows = [];
+ if (kind === 'phase') {
+ for (const t of linspace(0, Math.PI, steps)) {
+ const res = runPhaseExperiment({ theta: t, shots, noise });
+ const expectedP0 = Math.cos(t / 2) ** 2;
+ rows.push({
+ kind,
+ parameter: t,
+ parameterLabel: `θ=${t.toFixed(3)}`,
+ shots,
+ noise,
+ p0: res.distribution[0],
+ p1: res.distribution[1],
+ counts0: res.counts[0],
+ counts1: res.counts[1],
+ expectedP0,
+ error: Math.abs(res.distribution[0] - expectedP0),
+ coherence: coherenceScore({ noise, depth: 1, observedMidway: false }),
+ });
+ }
+ return rows;
+ }
+ if (kind === 'noise') {
+ for (const n of linspace(0, 1, steps)) {
+ const res = runPhaseExperiment({ theta, shots, noise: n });
+ const expectedP0 = Math.cos(theta / 2) ** 2;
+ rows.push({
+ kind,
+ parameter: n,
+ parameterLabel: `noise=${n.toFixed(2)}`,
+ shots,
+ noise: n,
+ p0: res.distribution[0],
+ p1: res.distribution[1],
+ counts0: res.counts[0],
+ counts1: res.counts[1],
+ expectedP0,
+ error: Math.abs(res.distribution[0] - expectedP0),
+ coherence: coherenceScore({ noise: n, depth: 1, observedMidway: false }),
+ });
+ }
+ return rows;
+ }
+ if (kind === 'depth') {
+ for (const d of rangeInts(0, maxDepth, steps)) {
+ const res = runDecoherenceExperiment({ xxPairs: d, shots, noise });
+ rows.push({
+ kind,
+ parameter: d,
+ parameterLabel: `xx=${d}`,
+ shots,
+ noise,
+ p0: res.distribution[0],
+ p1: res.distribution[1],
+ counts0: res.counts[0],
+ counts1: res.counts[1],
+ expectedP0: 1.0,
+ error: Math.abs(res.distribution[0] - 1.0),
+ coherence: coherenceScore({ noise, depth: d + 1, observedMidway: false }),
+ });
+ }
+ return rows;
+ }
+ throw new Error(`Unknown sweep kind: ${kind}`);
+}
+
+// ---------- CSV serialization ----------------------------------------------
+
+/**
+ * CSV columns mirror the Qiskit suite's results.csv where they overlap.
+ * Extra columns (counts0/counts1/coherence) are appended.
+ */
+export const SWEEP_CSV_COLUMNS = [
+ 'kind',
+ 'parameter',
+ 'parameter_label',
+ 'shots',
+ 'noise',
+ 'p0',
+ 'p1',
+ 'counts0',
+ 'counts1',
+ 'expected_p0',
+ 'error',
+ 'coherence_score',
+];
+
+function csvEscape(value) {
+ if (value == null) return '';
+ const str = typeof value === 'number' ? String(value) : String(value);
+ if (/[",\n]/.test(str)) {
+ return `"${str.replace(/"/g, '""')}"`;
+ }
+ return str;
+}
+
+export function rowsToCsv(rows) {
+ const lines = [SWEEP_CSV_COLUMNS.join(',')];
+ for (const r of rows) {
+ lines.push([
+ r.kind,
+ typeof r.parameter === 'number' ? r.parameter.toFixed(6) : r.parameter,
+ r.parameterLabel,
+ r.shots,
+ typeof r.noise === 'number' ? r.noise.toFixed(4) : r.noise,
+ r.p0?.toFixed(6) ?? '',
+ r.p1?.toFixed(6) ?? '',
+ r.counts0,
+ r.counts1,
+ r.expectedP0?.toFixed(6) ?? '',
+ r.error?.toFixed(6) ?? '',
+ r.coherence,
+ ].map(csvEscape).join(','));
+ }
+ return lines.join('\n');
+}
+
+// ---------- summary stats ---------------------------------------------------
+
+export function sweepSummary(rows) {
+ if (!rows.length) return null;
+ let maxError = 0;
+ let avgError = 0;
+ let minP0 = Infinity;
+ let maxP0 = -Infinity;
+ let avgCoherence = 0;
+ for (const r of rows) {
+ if (r.error > maxError) maxError = r.error;
+ avgError += r.error;
+ if (r.p0 < minP0) minP0 = r.p0;
+ if (r.p0 > maxP0) maxP0 = r.p0;
+ avgCoherence += r.coherence;
+ }
+ return {
+ n: rows.length,
+ maxError: maxError,
+ avgError: avgError / rows.length,
+ minP0,
+ maxP0,
+ rangeP0: maxP0 - minP0,
+ avgCoherence: avgCoherence / rows.length,
+ };
+}
diff --git a/brainsnn-r3f-app/src/utils/quantumSweep.test.mjs b/brainsnn-r3f-app/src/utils/quantumSweep.test.mjs
new file mode 100644
index 0000000..9827fa9
--- /dev/null
+++ b/brainsnn-r3f-app/src/utils/quantumSweep.test.mjs
@@ -0,0 +1,86 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import {
+ runSweep,
+ rowsToCsv,
+ sweepSummary,
+ SWEEP_CSV_COLUMNS,
+} from './quantumSweep.js';
+
+test('phase sweep produces `steps` rows spanning 0..pi', () => {
+ const rows = runSweep({ kind: 'phase', shots: 1024, steps: 5, noise: 0 });
+ assert.equal(rows.length, 5);
+ assert.ok(Math.abs(rows[0].parameter - 0) < 1e-9);
+ assert.ok(Math.abs(rows[rows.length - 1].parameter - Math.PI) < 1e-9);
+});
+
+test('phase sweep matches cos^2(theta/2) at theta=0 and theta=pi (noise=0)', () => {
+ const rows = runSweep({ kind: 'phase', shots: 4096, steps: 5, noise: 0 });
+ const first = rows[0];
+ const last = rows[rows.length - 1];
+ assert.ok(first.error < 0.05, `theta=0 error too high: ${first.error}`);
+ assert.ok(last.error < 0.05, `theta=pi error too high: ${last.error}`);
+});
+
+test('noise sweep increasing degrades coherence monotonically', () => {
+ const rows = runSweep({ kind: 'noise', shots: 1024, steps: 6, theta: 0 });
+ for (let i = 1; i < rows.length; i += 1) {
+ assert.ok(
+ rows[i].coherence <= rows[i - 1].coherence,
+ `coherence not monotone: rows[${i - 1}]=${rows[i - 1].coherence} rows[${i}]=${rows[i].coherence}`,
+ );
+ }
+});
+
+test('depth sweep produces integer parameters in non-decreasing order', () => {
+ const rows = runSweep({ kind: 'depth', shots: 1024, steps: 5, noise: 0.1, maxDepth: 12 });
+ for (let i = 1; i < rows.length; i += 1) {
+ assert.ok(rows[i].parameter >= rows[i - 1].parameter);
+ assert.ok(Number.isInteger(rows[i].parameter));
+ }
+});
+
+test('rowsToCsv emits the expected header and one line per row', () => {
+ const rows = runSweep({ kind: 'phase', shots: 256, steps: 3, noise: 0 });
+ const csv = rowsToCsv(rows);
+ const lines = csv.split('\n');
+ assert.equal(lines[0], SWEEP_CSV_COLUMNS.join(','));
+ assert.equal(lines.length, rows.length + 1);
+});
+
+test('CSV cells with commas are properly escaped', () => {
+ // forge a row with a comma in a string field
+ const rows = [{
+ kind: 'phase',
+ parameter: 0.5,
+ parameterLabel: 'theta=0.5,foo',
+ shots: 256,
+ noise: 0,
+ p0: 0.5,
+ p1: 0.5,
+ counts0: 128,
+ counts1: 128,
+ expectedP0: 0.5,
+ error: 0,
+ coherence: 100,
+ }];
+ const csv = rowsToCsv(rows);
+ assert.ok(csv.includes('"theta=0.5,foo"'),
+ `expected quoted comma; got: ${csv}`);
+});
+
+test('sweepSummary computes max / avg / range', () => {
+ const rows = runSweep({ kind: 'phase', shots: 1024, steps: 7, noise: 0 });
+ const s = sweepSummary(rows);
+ assert.equal(s.n, 7);
+ assert.ok(s.rangeP0 > 0.5, `expected wide P(0) range across phase sweep, got ${s.rangeP0}`);
+ assert.ok(s.maxError >= 0);
+});
+
+test('sweepSummary returns null on empty input', () => {
+ assert.equal(sweepSummary([]), null);
+});
+
+test('unknown sweep kind throws', () => {
+ assert.throws(() => runSweep({ kind: 'mystery', steps: 3 }));
+});
diff --git a/quantum_alignment/README.md b/quantum_alignment/README.md
new file mode 100644
index 0000000..0304dc4
--- /dev/null
+++ b/quantum_alignment/README.md
@@ -0,0 +1,132 @@
+# Quantum Alignment Tests
+
+A small Qiskit suite that probes the *physical* meaning of "alignment" in
+quantum computing — phase coherence, observation collapse, and decoherence —
+across an ideal simulator, a noisy simulator, and (optionally) real IBM
+Quantum hardware.
+
+> **Important framing.** "Multiverse alignment" is used here as a metaphor for
+> *coherent quantum phase relationships and the interference patterns they
+> produce*. This suite does **not** test, prove, or disprove literal parallel
+> universes or the many-worlds interpretation. See [What this is **not**](#what-this-is-not).
+
+## What is being tested
+
+| # | Experiment | What it probes |
+|---|---|---|
+| 1 | Phase alignment / interference | `|0> → H → RZ(θ) → H → Measure`. Sweeps θ ∈ {0, π/8, π/4, π/2, 3π/4, π}. Ideal `p(0) = cos²(θ/2)`. |
+| 2 | Observation collapse | Compares `H · H` (no mid-circuit measurement) against `H · Measure · H · Measure`. Mid-circuit measurement should destroy interference and push the final readout toward 50/50. |
+| 3 | Decoherence vs. depth | `|0> → H → (X X)ⁿ → H → Measure` with n ∈ {0, 1, 2, 4, 8, 16, 32, 64}. Each `X X` is logical identity, so the ideal output is `p(0) = 1` for all n; any drift is gate noise + decoherence. |
+
+## Setup
+
+```bash
+git clone
+cd quantum_alignment
+
+python3 -m venv .venv
+source .venv/bin/activate
+pip install -r requirements.txt
+```
+
+`qiskit-ibm-runtime` is listed in `requirements.txt` for completeness, but the
+ideal and noisy modes only need `qiskit`, `qiskit-aer`, `matplotlib`, and
+`numpy`. The runtime package is imported lazily, only when you ask for
+`--mode real`.
+
+## Running
+
+All three modes write the same artifacts to `./results/`:
+
+- `results.csv` — one row per (experiment, parameter) with raw probabilities and error
+- `phase_alignment_plot.png` — Experiment 1 plot
+- `noise_depth_plot.png` — Experiment 3 plot
+- `report.md` — plain-English explanation of what the data shows
+
+### Ideal local simulator (no token required)
+
+```bash
+python quantum_alignment_tests.py --mode ideal --shots 4096
+```
+
+### Noisy simulator (no token required)
+
+Uses `qiskit_aer.AerSimulator.from_backend(GenericBackendV2(...))` so that a
+realistic noise model is applied without contacting IBM:
+
+```bash
+python quantum_alignment_tests.py --mode noisy --shots 4096
+```
+
+### Real IBM Quantum hardware
+
+You need an IBM Quantum account and an API token. **Never hard-code the
+token** and never commit it to git. The script reads it from the
+`IBM_QUANTUM_TOKEN` environment variable.
+
+Recommended ways to set it safely:
+
+```bash
+# Option A: prompt yourself, do not echo, do not store in shell history
+read -s IBM_QUANTUM_TOKEN
+export IBM_QUANTUM_TOKEN
+python quantum_alignment_tests.py --mode real
+
+# Option B: a per-project .envrc loaded by direnv (add .envrc to .gitignore!)
+echo 'export IBM_QUANTUM_TOKEN="$(security find-generic-password -s ibm-quantum -w)"' > .envrc
+direnv allow
+
+# Option C: a system keyring / secret manager (1Password, macOS Keychain, etc.)
+export IBM_QUANTUM_TOKEN="$(op read 'op://Personal/IBM Quantum/credential')"
+python quantum_alignment_tests.py --mode real
+```
+
+To pin a specific backend (otherwise the script uses `least_busy`):
+
+```bash
+python quantum_alignment_tests.py --mode real --backend ibm_brisbane
+```
+
+If your selected backend does not support mid-circuit measurement, pass
+`--skip-observation` and run Experiment 2 separately on the simulator. The
+script also gracefully records a "skipped" row in `results.csv` for any
+mid-measure circuit it could not execute.
+
+## CLI reference
+
+```
+--mode {ideal,noisy,real} execution backend (default: ideal)
+--backend NAME real-mode backend name (default: least_busy)
+--shots N shots per circuit (default: 4096)
+--seed N deterministic seed for noisy mode (default: 42)
+--out PATH output directory (default: ./results)
+--skip-observation skip Experiment 2 (for backends with no mid-circuit measure)
+```
+
+## What this is **not**
+
+- **Not a test of literal parallel universes.** Quantum mechanics is
+ interpretation-agnostic; no single-qubit experiment can settle the
+ many-worlds vs. Copenhagen vs. relational debate.
+- **Not a hardware benchmark.** A handful of circuits at a few thousand shots
+ cannot replace published quantum-volume / EPLG / RB numbers.
+- **Not a randomness or cryptographic source.** The bits returned here are
+ experimental data, not certified entropy.
+
+## What this **is**
+
+- A small, reproducible demo that quantum amplitudes have phase, that those
+ phases interfere, that observation destroys interference, and that real
+ hardware loses coherence as circuit depth grows.
+- A scaffold you can extend with more circuits (entanglement, GHZ, Bell
+ inequality, randomized benchmarking, …) using the same backend / CSV /
+ report scaffolding.
+
+## Security notes
+
+- The token is **only** read from `IBM_QUANTUM_TOKEN` at runtime. The script
+ does not log it, does not write it to disk, and does not include it in any
+ output file or plot.
+- `requirements.txt` pins minimum versions but does not freeze; in a
+ production setting you should `pip freeze > requirements.lock` and check the
+ lock file in.
diff --git a/quantum_alignment/quantum_alignment_tests.py b/quantum_alignment/quantum_alignment_tests.py
new file mode 100644
index 0000000..8061adb
--- /dev/null
+++ b/quantum_alignment/quantum_alignment_tests.py
@@ -0,0 +1,708 @@
+"""Quantum alignment test suite.
+
+Runs three experiments that probe the *physical* meaning of "alignment" in a
+quantum computer:
+
+1. Phase alignment / interference (single-qubit Mach-Zehnder via H-RZ-H)
+2. Observation collapse (measurement in the middle of a circuit)
+3. Decoherence / noise accumulation (deepening identity-equivalent X-X chains)
+
+This is *not* a test of literal parallel universes. "Multiverse alignment" is
+treated here as a metaphor for coherent quantum phase relationships and the
+interference patterns they produce.
+
+Three execution modes are supported:
+
+ --mode ideal AerSimulator with no noise.
+ --mode noisy AerSimulator with a noise model derived from a synthetic
+ IBM-like fake backend (no IBM account or token required).
+ --mode real Real IBM Quantum hardware via qiskit-ibm-runtime. Requires
+ the IBM_QUANTUM_TOKEN environment variable.
+
+Outputs (under --out, default ./results):
+
+ results.csv raw counts and probabilities for every shot batch
+ phase_alignment_plot.png p(0)/p(1)/error vs theta (Experiment 1)
+ noise_depth_plot.png p(0)/p(1)/error vs X-X depth (Experiment 3)
+ report.md plain-English summary of what the data shows
+"""
+
+from __future__ import annotations
+
+import argparse
+import csv
+import math
+import os
+import sys
+from dataclasses import dataclass, field
+from typing import Callable, Iterable
+
+import matplotlib
+
+matplotlib.use("Agg")
+import matplotlib.pyplot as plt
+import numpy as np
+
+from qiskit import QuantumCircuit, transpile
+from qiskit_aer import AerSimulator
+
+
+# ---------------------------------------------------------------------------
+# Backend selection
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class Backend:
+ """Bundle of (label, sampler) used by the rest of the script."""
+
+ label: str
+ mode: str
+ run: Callable[[QuantumCircuit, int], dict[str, int]]
+ supports_mid_measure: bool = True
+ notes: list[str] = field(default_factory=list)
+
+
+def make_ideal_backend() -> Backend:
+ sim = AerSimulator()
+
+ def run(circuit: QuantumCircuit, shots: int) -> dict[str, int]:
+ tqc = transpile(circuit, sim)
+ result = sim.run(tqc, shots=shots).result()
+ return result.get_counts()
+
+ return Backend(label="aer-ideal", mode="ideal", run=run)
+
+
+def make_noisy_backend(seed: int = 42) -> Backend:
+ """Noisy AerSimulator built from a synthetic IBM-like fake backend.
+
+ We use ``qiskit.providers.fake_provider.GenericBackendV2`` because it ships
+ with qiskit core and does not require ``qiskit-ibm-runtime`` (which would
+ in turn require the IBM cloud SDK). ``AerSimulator.from_backend`` extracts
+ a noise model from the fake backend's calibration data.
+ """
+
+ from qiskit.providers.fake_provider import GenericBackendV2
+
+ fake = GenericBackendV2(num_qubits=5, seed=seed)
+ sim = AerSimulator.from_backend(fake)
+
+ def run(circuit: QuantumCircuit, shots: int) -> dict[str, int]:
+ tqc = transpile(circuit, sim, optimization_level=0, seed_transpiler=seed)
+ result = sim.run(tqc, shots=shots).result()
+ return result.get_counts()
+
+ return Backend(
+ label=f"aer-noisy({fake.name})",
+ mode="noisy",
+ run=run,
+ notes=[
+ "Noise model derived from synthetic fake backend "
+ f"{fake.name!r} (qiskit GenericBackendV2).",
+ ],
+ )
+
+
+def make_real_backend(token: str, backend_name: str | None) -> Backend:
+ """Real IBM Quantum backend via qiskit-ibm-runtime.
+
+ ``token`` is *only* used to instantiate the service; it is never logged or
+ written to disk by this script.
+ """
+
+ # Lazy import: keeps simulator-only runs working even if qiskit-ibm-runtime
+ # (and its IBM cloud SDK dependency tree) is not importable.
+ from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
+
+ service = QiskitRuntimeService(channel="ibm_quantum", token=token)
+ if backend_name:
+ backend = service.backend(backend_name)
+ else:
+ backend = service.least_busy(operational=True, simulator=False)
+
+ sampler = SamplerV2(mode=backend)
+
+ def run(circuit: QuantumCircuit, shots: int) -> dict[str, int]:
+ tqc = transpile(circuit, backend=backend, optimization_level=1)
+ job = sampler.run([tqc], shots=shots)
+ result = job.result()
+ # SamplerV2 result -> per-circuit PubResult; default classical register
+ # is named "meas" when QuantumCircuit.measure_all is used and "c" when
+ # we declared the register ourselves below.
+ pub = result[0]
+ data = pub.data
+ register_name = next(iter(data.__dict__)) if hasattr(data, "__dict__") else "c"
+ bit_array = getattr(data, register_name)
+ return bit_array.get_counts()
+
+ return Backend(
+ label=f"ibm-real({backend.name})",
+ mode="real",
+ run=run,
+ # Most modern IBM backends support mid-circuit measurement, but
+ # individual devices vary. Caller can override via CLI if needed.
+ supports_mid_measure=True,
+ notes=[f"Running on IBM backend {backend.name!r}."],
+ )
+
+
+# ---------------------------------------------------------------------------
+# Circuit builders
+# ---------------------------------------------------------------------------
+
+
+def phase_circuit(theta: float) -> QuantumCircuit:
+ """|0> -> H -> RZ(theta) -> H -> Measure."""
+ qc = QuantumCircuit(1, 1, name=f"phase_{theta:.4f}")
+ qc.h(0)
+ qc.rz(theta, 0)
+ qc.h(0)
+ qc.measure(0, 0)
+ return qc
+
+
+def observation_circuit_a() -> QuantumCircuit:
+ """A: |0> -> H -> H -> Measure (no mid-circuit observation)."""
+ qc = QuantumCircuit(1, 1, name="obs_A")
+ qc.h(0)
+ qc.h(0)
+ qc.measure(0, 0)
+ return qc
+
+
+def observation_circuit_b() -> QuantumCircuit:
+ """B: |0> -> H -> Measure -> H -> Measure (mid-circuit collapse)."""
+ qc = QuantumCircuit(1, 2, name="obs_B")
+ qc.h(0)
+ qc.measure(0, 0) # collapse
+ qc.h(0)
+ qc.measure(0, 1) # final readout
+ return qc
+
+
+def noise_depth_circuit(num_xx_pairs: int) -> QuantumCircuit:
+ """|0> -> H -> (X X) * n -> H -> Measure.
+
+ Each X-X pair is the identity, so the *ideal* output is always |0>. Any
+ drift away from p(0)=1 on real or noisy hardware is gate noise + decoherence.
+ """
+ qc = QuantumCircuit(1, 1, name=f"noise_{num_xx_pairs}")
+ qc.h(0)
+ for _ in range(num_xx_pairs):
+ qc.x(0)
+ qc.x(0)
+ qc.barrier()
+ qc.h(0)
+ qc.measure(0, 0)
+ return qc
+
+
+# ---------------------------------------------------------------------------
+# Counts analysis
+# ---------------------------------------------------------------------------
+
+
+def _last_bit_counts(counts: dict[str, int]) -> tuple[int, int]:
+ """Sum counts of the *final* classical bit (Qiskit prints little-endian).
+
+ For circuit B (two classical bits) only the second measurement is the
+ "after re-prepared superposition" outcome -- that's the bit we care about.
+ """
+ zeros = 0
+ ones = 0
+ for bitstring, count in counts.items():
+ # strip any spaces inserted between registers
+ stripped = bitstring.replace(" ", "")
+ # leftmost char in qiskit's printed string is the highest-index bit;
+ # the *final* measurement of obs_B writes bit index 1, which is the
+ # leftmost char. For single-bit circuits both ends agree.
+ bit = stripped[0]
+ if bit == "0":
+ zeros += count
+ else:
+ ones += count
+ return zeros, ones
+
+
+def _first_bit_counts(counts: dict[str, int]) -> tuple[int, int]:
+ zeros = 0
+ ones = 0
+ for bitstring, count in counts.items():
+ stripped = bitstring.replace(" ", "")
+ bit = stripped[-1]
+ if bit == "0":
+ zeros += count
+ else:
+ ones += count
+ return zeros, ones
+
+
+def probabilities(zeros: int, ones: int) -> tuple[float, float]:
+ total = zeros + ones
+ if total == 0:
+ return 0.0, 0.0
+ return zeros / total, ones / total
+
+
+# ---------------------------------------------------------------------------
+# Experiments
+# ---------------------------------------------------------------------------
+
+
+THETAS: list[tuple[str, float]] = [
+ ("0", 0.0),
+ ("pi/8", math.pi / 8),
+ ("pi/4", math.pi / 4),
+ ("pi/2", math.pi / 2),
+ ("3pi/4", 3 * math.pi / 4),
+ ("pi", math.pi),
+]
+
+NOISE_DEPTHS: list[int] = [0, 1, 2, 4, 8, 16, 32, 64]
+
+
+@dataclass
+class Row:
+ experiment: str
+ backend_label: str
+ mode: str
+ label: str
+ parameter: float
+ shots: int
+ p0: float
+ p1: float
+ expected_p0: float
+ error: float
+ notes: str = ""
+
+
+def run_phase_experiment(backend: Backend, shots: int) -> list[Row]:
+ rows: list[Row] = []
+ for name, theta in THETAS:
+ # ideal probability of measuring 0 for H-RZ(theta)-H is cos(theta/2)^2
+ expected_p0 = math.cos(theta / 2.0) ** 2
+ counts = backend.run(phase_circuit(theta), shots)
+ zeros, ones = _first_bit_counts(counts)
+ p0, p1 = probabilities(zeros, ones)
+ rows.append(
+ Row(
+ experiment="phase_alignment",
+ backend_label=backend.label,
+ mode=backend.mode,
+ label=name,
+ parameter=theta,
+ shots=shots,
+ p0=p0,
+ p1=p1,
+ expected_p0=expected_p0,
+ error=abs(p0 - expected_p0),
+ )
+ )
+ return rows
+
+
+def run_observation_experiment(backend: Backend, shots: int) -> list[Row]:
+ rows: list[Row] = []
+
+ counts_a = backend.run(observation_circuit_a(), shots)
+ zeros_a, ones_a = _first_bit_counts(counts_a)
+ p0_a, p1_a = probabilities(zeros_a, ones_a)
+ rows.append(
+ Row(
+ experiment="observation",
+ backend_label=backend.label,
+ mode=backend.mode,
+ label="A_no_mid_measure",
+ parameter=0,
+ shots=shots,
+ p0=p0_a,
+ p1=p1_a,
+ expected_p0=1.0, # H H = I
+ error=abs(p0_a - 1.0),
+ notes="H then H -> identity, expect p(0)=1",
+ )
+ )
+
+ if not backend.supports_mid_measure:
+ rows.append(
+ Row(
+ experiment="observation",
+ backend_label=backend.label,
+ mode=backend.mode,
+ label="B_mid_measure",
+ parameter=0,
+ shots=shots,
+ p0=float("nan"),
+ p1=float("nan"),
+ expected_p0=0.5,
+ error=float("nan"),
+ notes=(
+ "Skipped: selected backend does not support mid-circuit "
+ "measurement. Run in --mode ideal or --mode noisy to see B."
+ ),
+ )
+ )
+ return rows
+
+ counts_b = backend.run(observation_circuit_b(), shots)
+ zeros_b, ones_b = _last_bit_counts(counts_b)
+ p0_b, p1_b = probabilities(zeros_b, ones_b)
+ rows.append(
+ Row(
+ experiment="observation",
+ backend_label=backend.label,
+ mode=backend.mode,
+ label="B_mid_measure",
+ parameter=0,
+ shots=shots,
+ p0=p0_b,
+ p1=p1_b,
+ expected_p0=0.5, # collapse erases interference
+ error=abs(p0_b - 0.5),
+ notes="Mid-circuit measurement collapses the state; expect ~50/50",
+ )
+ )
+ return rows
+
+
+def run_noise_depth_experiment(backend: Backend, shots: int) -> list[Row]:
+ rows: list[Row] = []
+ for depth in NOISE_DEPTHS:
+ counts = backend.run(noise_depth_circuit(depth), shots)
+ zeros, ones = _first_bit_counts(counts)
+ p0, p1 = probabilities(zeros, ones)
+ # H (XX)^n H |0> = |0> ideally, regardless of n
+ rows.append(
+ Row(
+ experiment="noise_depth",
+ backend_label=backend.label,
+ mode=backend.mode,
+ label=f"xx_pairs={depth}",
+ parameter=depth,
+ shots=shots,
+ p0=p0,
+ p1=p1,
+ expected_p0=1.0,
+ error=abs(p0 - 1.0),
+ )
+ )
+ return rows
+
+
+# ---------------------------------------------------------------------------
+# Persistence + plots + report
+# ---------------------------------------------------------------------------
+
+
+CSV_FIELDS = [
+ "experiment",
+ "backend_label",
+ "mode",
+ "label",
+ "parameter",
+ "shots",
+ "p0",
+ "p1",
+ "expected_p0",
+ "error",
+ "notes",
+]
+
+
+def write_csv(rows: Iterable[Row], path: str) -> None:
+ with open(path, "w", newline="") as f:
+ writer = csv.DictWriter(f, fieldnames=CSV_FIELDS)
+ writer.writeheader()
+ for row in rows:
+ writer.writerow({k: getattr(row, k) for k in CSV_FIELDS})
+
+
+def plot_phase(rows: list[Row], path: str, backend_label: str) -> None:
+ phase_rows = [r for r in rows if r.experiment == "phase_alignment"]
+ if not phase_rows:
+ return
+ thetas = [r.parameter for r in phase_rows]
+ p0 = [r.p0 for r in phase_rows]
+ p1 = [r.p1 for r in phase_rows]
+ err = [r.error for r in phase_rows]
+ expected = [r.expected_p0 for r in phase_rows]
+
+ fig, ax = plt.subplots(figsize=(8, 5))
+ ax.plot(thetas, p0, marker="o", label="p(0) measured")
+ ax.plot(thetas, p1, marker="o", label="p(1) measured")
+ ax.plot(thetas, expected, linestyle="--", label="p(0) ideal = cos^2(theta/2)")
+ ax.plot(thetas, err, marker="x", label="|p(0) - ideal|")
+ ax.set_xlabel("theta (radians)")
+ ax.set_ylabel("probability")
+ ax.set_title(f"Experiment 1: Phase alignment / interference ({backend_label})")
+ ax.set_xticks(thetas)
+ ax.set_xticklabels([r.label for r in phase_rows])
+ ax.set_ylim(-0.05, 1.05)
+ ax.grid(True, alpha=0.3)
+ ax.legend(loc="center right")
+ fig.tight_layout()
+ fig.savefig(path, dpi=140)
+ plt.close(fig)
+
+
+def plot_noise_depth(rows: list[Row], path: str, backend_label: str) -> None:
+ depth_rows = [r for r in rows if r.experiment == "noise_depth"]
+ if not depth_rows:
+ return
+ depths = [r.parameter for r in depth_rows]
+ p0 = [r.p0 for r in depth_rows]
+ p1 = [r.p1 for r in depth_rows]
+ err = [r.error for r in depth_rows]
+
+ fig, ax = plt.subplots(figsize=(8, 5))
+ ax.plot(depths, p0, marker="o", label="p(0) measured")
+ ax.plot(depths, p1, marker="o", label="p(1) measured")
+ ax.plot(depths, err, marker="x", label="error = 1 - p(0)")
+ ax.axhline(1.0, linestyle="--", alpha=0.5, label="ideal p(0)=1")
+ ax.set_xscale("symlog", linthresh=1)
+ ax.set_xlabel("number of X-X pairs (logical identity, real depth grows)")
+ ax.set_ylabel("probability")
+ ax.set_title(f"Experiment 3: Decoherence vs depth ({backend_label})")
+ ax.set_ylim(-0.05, 1.05)
+ ax.grid(True, alpha=0.3, which="both")
+ ax.legend(loc="center right")
+ fig.tight_layout()
+ fig.savefig(path, dpi=140)
+ plt.close(fig)
+
+
+def write_report(rows: list[Row], path: str, backend: Backend, shots: int) -> None:
+ phase_rows = [r for r in rows if r.experiment == "phase_alignment"]
+ obs_rows = [r for r in rows if r.experiment == "observation"]
+ depth_rows = [r for r in rows if r.experiment == "noise_depth"]
+
+ def fmt(p: float) -> str:
+ if p != p: # NaN
+ return "n/a"
+ return f"{p:.3f}"
+
+ lines: list[str] = []
+ lines.append("# Quantum Alignment Test Report")
+ lines.append("")
+ lines.append(f"- Backend: `{backend.label}`")
+ lines.append(f"- Mode: `{backend.mode}`")
+ lines.append(f"- Shots per circuit: {shots}")
+ if backend.notes:
+ lines.append("- Backend notes:")
+ for note in backend.notes:
+ lines.append(f" - {note}")
+ lines.append("")
+ lines.append("## What this report tests")
+ lines.append("")
+ lines.append(
+ "This suite probes three *physical* properties of quantum computation: "
+ "phase coherence and interference, the effect of measurement on a "
+ "superposition, and the accumulation of gate noise / decoherence as "
+ "circuit depth grows. \"Multiverse alignment\" is used here as a "
+ "metaphor for the coherent phase relationships that produce "
+ "interference -- it is **not** a literal claim about parallel "
+ "universes, and nothing here can prove or disprove the many-worlds "
+ "interpretation."
+ )
+ lines.append("")
+
+ # Experiment 1
+ lines.append("## Experiment 1 -- Phase alignment / interference")
+ lines.append("")
+ lines.append(
+ "Circuit: `|0> - H - RZ(theta) - H - Measure`. The first H spreads "
+ "|0> into an equal superposition; RZ(theta) puts a relative phase "
+ "between the two branches; the second H lets those branches "
+ "interfere. The ideal outcome is `p(0) = cos^2(theta/2)`."
+ )
+ lines.append("")
+ lines.append("| theta | p(0) measured | p(1) measured | p(0) ideal | |error| |")
+ lines.append("|---|---|---|---|---|")
+ for r in phase_rows:
+ lines.append(
+ f"| {r.label} | {fmt(r.p0)} | {fmt(r.p1)} | "
+ f"{fmt(r.expected_p0)} | {fmt(r.error)} |"
+ )
+ lines.append("")
+ if phase_rows:
+ max_err = max(r.error for r in phase_rows)
+ lines.append(
+ f"Maximum deviation from the ideal interference pattern: "
+ f"**{max_err:.3f}**. "
+ f"On the ideal simulator this should be at the level of shot noise "
+ f"(~1/sqrt(shots) ~= {1.0 / math.sqrt(shots):.3f}). On a noisy or "
+ f"real backend it grows because gate errors and readout errors "
+ f"smear the interference fringes."
+ )
+ lines.append("")
+
+ # Experiment 2
+ lines.append("## Experiment 2 -- Observation collapse")
+ lines.append("")
+ lines.append(
+ "Two circuits are compared. **A** applies H then H with no measurement "
+ "in between: H is its own inverse, so the qubit returns to |0> and we "
+ "expect `p(0) = 1`. **B** measures *between* the two H gates. That "
+ "mid-circuit measurement collapses the superposition to a definite "
+ "|0> or |1>; the second H then turns whichever basis state we have "
+ "into a fresh equal superposition, so the final readout is ~50/50. "
+ "If A is near 1.0 and B is near 0.5, observation has demonstrably "
+ "destroyed interference."
+ )
+ lines.append("")
+ lines.append("| variant | p(0) | p(1) | expected p(0) | |error| | notes |")
+ lines.append("|---|---|---|---|---|---|")
+ for r in obs_rows:
+ lines.append(
+ f"| {r.label} | {fmt(r.p0)} | {fmt(r.p1)} | "
+ f"{fmt(r.expected_p0)} | {fmt(r.error)} | {r.notes} |"
+ )
+ lines.append("")
+
+ # Experiment 3
+ lines.append("## Experiment 3 -- Decoherence / noise accumulation")
+ lines.append("")
+ lines.append(
+ "Circuit: `|0> - H - (X X)^n - H - Measure`. Each `X X` pair is the "
+ "identity, so the ideal outcome is always `p(0) = 1` regardless of n. "
+ "Any drift toward `p(0) = 0.5` as n grows is *purely* gate noise and "
+ "decoherence (T1/T2 relaxation, control errors, readout errors). On "
+ "the ideal simulator the line should stay flat at 1.0; on a noisy or "
+ "real backend it will sag toward 0.5."
+ )
+ lines.append("")
+ lines.append("| X-X pairs | p(0) | p(1) | error = 1-p(0) |")
+ lines.append("|---|---|---|---|")
+ for r in depth_rows:
+ lines.append(
+ f"| {int(r.parameter)} | {fmt(r.p0)} | "
+ f"{fmt(r.p1)} | {fmt(r.error)} |"
+ )
+ lines.append("")
+ if depth_rows:
+ deepest = depth_rows[-1]
+ lines.append(
+ f"At depth {int(deepest.parameter)} X-X pairs, p(0) is "
+ f"**{fmt(deepest.p0)}** vs the ideal **1.0** "
+ f"(error **{fmt(deepest.error)}**). "
+ "The further this number is from 1.0, the more decoherence the "
+ "device has accumulated over the run."
+ )
+ lines.append("")
+
+ # Scope / disclaimer
+ lines.append("## What this is NOT")
+ lines.append("")
+ lines.append(
+ "- Not a test of literal parallel universes or the many-worlds "
+ "interpretation. Quantum mechanics is interpretation-agnostic and "
+ "no single-qubit experiment can settle that debate."
+ )
+ lines.append(
+ "- Not a benchmark of any specific IBM device. We use a tiny number "
+ "of circuits at modest shot counts; published quantum-volume / EPLG "
+ "numbers are far more rigorous."
+ )
+ lines.append(
+ "- Not a security or randomness test. Do not use these counts as a "
+ "source of cryptographic entropy."
+ )
+ lines.append("")
+ lines.append("## What this IS")
+ lines.append("")
+ lines.append(
+ "- A reproducible demonstration that quantum amplitudes have phase, "
+ "that phases interfere, that observation destroys interference, and "
+ "that real hardware loses coherence as depth grows."
+ )
+ lines.append("")
+
+ with open(path, "w") as f:
+ f.write("\n".join(lines))
+
+
+# ---------------------------------------------------------------------------
+# Entry point
+# ---------------------------------------------------------------------------
+
+
+def select_backend(args: argparse.Namespace) -> Backend:
+ if args.mode == "ideal":
+ return make_ideal_backend()
+ if args.mode == "noisy":
+ return make_noisy_backend(seed=args.seed)
+ if args.mode == "real":
+ token = os.environ.get("IBM_QUANTUM_TOKEN")
+ if not token:
+ raise SystemExit(
+ "IBM_QUANTUM_TOKEN is not set. Export it (without quoting it "
+ "into shell history -- prefer `read -s` or a secret manager) "
+ "before running with --mode real, or pick --mode ideal/noisy."
+ )
+ return make_real_backend(token=token, backend_name=args.backend)
+ raise SystemExit(f"Unknown mode: {args.mode!r}")
+
+
+def parse_args(argv: list[str]) -> argparse.Namespace:
+ p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+ p.add_argument("--mode", choices=["ideal", "noisy", "real"], default="ideal")
+ p.add_argument(
+ "--backend",
+ default=None,
+ help="(real mode only) IBM backend name. Defaults to least_busy.",
+ )
+ p.add_argument("--shots", type=int, default=4096)
+ p.add_argument("--seed", type=int, default=42)
+ p.add_argument(
+ "--out",
+ default=os.path.join(os.path.dirname(os.path.abspath(__file__)), "results"),
+ help="Output directory for CSV, plots, and report.",
+ )
+ p.add_argument(
+ "--skip-observation",
+ action="store_true",
+ help="Skip Experiment 2 entirely (e.g., on a backend with no mid-circuit measurement support).",
+ )
+ return p.parse_args(argv)
+
+
+def main(argv: list[str] | None = None) -> int:
+ args = parse_args(argv if argv is not None else sys.argv[1:])
+ os.makedirs(args.out, exist_ok=True)
+
+ backend = select_backend(args)
+ np.random.seed(args.seed)
+
+ print(f"[quantum_alignment] backend={backend.label} shots={args.shots}")
+
+ rows: list[Row] = []
+ print("[quantum_alignment] running Experiment 1: phase alignment...")
+ rows.extend(run_phase_experiment(backend, args.shots))
+ if args.skip_observation:
+ print("[quantum_alignment] skipping Experiment 2 (per --skip-observation)")
+ else:
+ print("[quantum_alignment] running Experiment 2: observation collapse...")
+ rows.extend(run_observation_experiment(backend, args.shots))
+ print("[quantum_alignment] running Experiment 3: noise vs depth...")
+ rows.extend(run_noise_depth_experiment(backend, args.shots))
+
+ csv_path = os.path.join(args.out, "results.csv")
+ phase_plot = os.path.join(args.out, "phase_alignment_plot.png")
+ noise_plot = os.path.join(args.out, "noise_depth_plot.png")
+ report_path = os.path.join(args.out, "report.md")
+
+ write_csv(rows, csv_path)
+ plot_phase(rows, phase_plot, backend.label)
+ plot_noise_depth(rows, noise_plot, backend.label)
+ write_report(rows, report_path, backend, args.shots)
+
+ print(f"[quantum_alignment] wrote {csv_path}")
+ print(f"[quantum_alignment] wrote {phase_plot}")
+ print(f"[quantum_alignment] wrote {noise_plot}")
+ print(f"[quantum_alignment] wrote {report_path}")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/quantum_alignment/requirements.txt b/quantum_alignment/requirements.txt
new file mode 100644
index 0000000..c05aaf1
--- /dev/null
+++ b/quantum_alignment/requirements.txt
@@ -0,0 +1,9 @@
+# Core
+qiskit>=1.0
+qiskit-aer>=0.13
+matplotlib>=3.7
+numpy>=1.24
+
+# Only needed for --mode real (real IBM hardware execution).
+# Pulled in lazily; ideal/noisy modes work without it.
+qiskit-ibm-runtime>=0.20
diff --git a/quantum_alignment/results/noise_depth_plot.png b/quantum_alignment/results/noise_depth_plot.png
new file mode 100644
index 0000000..8a7fe04
Binary files /dev/null and b/quantum_alignment/results/noise_depth_plot.png differ
diff --git a/quantum_alignment/results/phase_alignment_plot.png b/quantum_alignment/results/phase_alignment_plot.png
new file mode 100644
index 0000000..93a6c6b
Binary files /dev/null and b/quantum_alignment/results/phase_alignment_plot.png differ
diff --git a/quantum_alignment/results/report.md b/quantum_alignment/results/report.md
new file mode 100644
index 0000000..a571f9e
--- /dev/null
+++ b/quantum_alignment/results/report.md
@@ -0,0 +1,62 @@
+# Quantum Alignment Test Report
+
+- Backend: `aer-noisy(generic_backend_5q)`
+- Mode: `noisy`
+- Shots per circuit: 4096
+- Backend notes:
+ - Noise model derived from synthetic fake backend 'generic_backend_5q' (qiskit GenericBackendV2).
+
+## What this report tests
+
+This suite probes three *physical* properties of quantum computation: phase coherence and interference, the effect of measurement on a superposition, and the accumulation of gate noise / decoherence as circuit depth grows. "Multiverse alignment" is used here as a metaphor for the coherent phase relationships that produce interference -- it is **not** a literal claim about parallel universes, and nothing here can prove or disprove the many-worlds interpretation.
+
+## Experiment 1 -- Phase alignment / interference
+
+Circuit: `|0> - H - RZ(theta) - H - Measure`. The first H spreads |0> into an equal superposition; RZ(theta) puts a relative phase between the two branches; the second H lets those branches interfere. The ideal outcome is `p(0) = cos^2(theta/2)`.
+
+| theta | p(0) measured | p(1) measured | p(0) ideal | |error| |
+|---|---|---|---|---|
+| 0 | 0.997 | 0.003 | 1.000 | 0.003 |
+| pi/8 | 0.957 | 0.043 | 0.962 | 0.005 |
+| pi/4 | 0.859 | 0.141 | 0.854 | 0.006 |
+| pi/2 | 0.499 | 0.501 | 0.500 | 0.001 |
+| 3pi/4 | 0.151 | 0.849 | 0.146 | 0.005 |
+| pi | 0.004 | 0.996 | 0.000 | 0.004 |
+
+Maximum deviation from the ideal interference pattern: **0.006**. On the ideal simulator this should be at the level of shot noise (~1/sqrt(shots) ~= 0.016). On a noisy or real backend it grows because gate errors and readout errors smear the interference fringes.
+
+## Experiment 2 -- Observation collapse
+
+Two circuits are compared. **A** applies H then H with no measurement in between: H is its own inverse, so the qubit returns to |0> and we expect `p(0) = 1`. **B** measures *between* the two H gates. That mid-circuit measurement collapses the superposition to a definite |0> or |1>; the second H then turns whichever basis state we have into a fresh equal superposition, so the final readout is ~50/50. If A is near 1.0 and B is near 0.5, observation has demonstrably destroyed interference.
+
+| variant | p(0) | p(1) | expected p(0) | |error| | notes |
+|---|---|---|---|---|---|
+| A_no_mid_measure | 0.995 | 0.005 | 1.000 | 0.005 | H then H -> identity, expect p(0)=1 |
+| B_mid_measure | 0.500 | 0.500 | 0.500 | 0.000 | Mid-circuit measurement collapses the state; expect ~50/50 |
+
+## Experiment 3 -- Decoherence / noise accumulation
+
+Circuit: `|0> - H - (X X)^n - H - Measure`. Each `X X` pair is the identity, so the ideal outcome is always `p(0) = 1` regardless of n. Any drift toward `p(0) = 0.5` as n grows is *purely* gate noise and decoherence (T1/T2 relaxation, control errors, readout errors). On the ideal simulator the line should stay flat at 1.0; on a noisy or real backend it will sag toward 0.5.
+
+| X-X pairs | p(0) | p(1) | error = 1-p(0) |
+|---|---|---|---|
+| 0 | 0.997 | 0.003 | 0.003 |
+| 1 | 0.996 | 0.004 | 0.004 |
+| 2 | 0.996 | 0.004 | 0.004 |
+| 4 | 0.993 | 0.007 | 0.007 |
+| 8 | 0.995 | 0.005 | 0.005 |
+| 16 | 0.990 | 0.010 | 0.010 |
+| 32 | 0.988 | 0.012 | 0.012 |
+| 64 | 0.980 | 0.020 | 0.020 |
+
+At depth 64 X-X pairs, p(0) is **0.980** vs the ideal **1.0** (error **0.020**). The further this number is from 1.0, the more decoherence the device has accumulated over the run.
+
+## What this is NOT
+
+- Not a test of literal parallel universes or the many-worlds interpretation. Quantum mechanics is interpretation-agnostic and no single-qubit experiment can settle that debate.
+- Not a benchmark of any specific IBM device. We use a tiny number of circuits at modest shot counts; published quantum-volume / EPLG numbers are far more rigorous.
+- Not a security or randomness test. Do not use these counts as a source of cryptographic entropy.
+
+## What this IS
+
+- A reproducible demonstration that quantum amplitudes have phase, that phases interfere, that observation destroys interference, and that real hardware loses coherence as depth grows.
diff --git a/quantum_alignment/results/results.csv b/quantum_alignment/results/results.csv
new file mode 100644
index 0000000..ea77875
--- /dev/null
+++ b/quantum_alignment/results/results.csv
@@ -0,0 +1,17 @@
+experiment,backend_label,mode,label,parameter,shots,p0,p1,expected_p0,error,notes
+phase_alignment,aer-noisy(generic_backend_5q),noisy,0,0.0,4096,0.99658203125,0.00341796875,1.0,0.00341796875,
+phase_alignment,aer-noisy(generic_backend_5q),noisy,pi/8,0.39269908169872414,4096,0.95654296875,0.04345703125,0.9619397662556434,0.005396797505643369,
+phase_alignment,aer-noisy(generic_backend_5q),noisy,pi/4,0.7853981633974483,4096,0.859130859375,0.140869140625,0.8535533905932737,0.005577468781726269,
+phase_alignment,aer-noisy(generic_backend_5q),noisy,pi/2,1.5707963267948966,4096,0.4990234375,0.5009765625,0.5000000000000001,0.000976562500000111,
+phase_alignment,aer-noisy(generic_backend_5q),noisy,3pi/4,2.356194490192345,4096,0.151123046875,0.848876953125,0.1464466094067263,0.004676437468273703,
+phase_alignment,aer-noisy(generic_backend_5q),noisy,pi,3.141592653589793,4096,0.003662109375,0.996337890625,3.749399456654644e-33,0.003662109375,
+observation,aer-noisy(generic_backend_5q),noisy,A_no_mid_measure,0,4096,0.995361328125,0.004638671875,1.0,0.004638671875,"H then H -> identity, expect p(0)=1"
+observation,aer-noisy(generic_backend_5q),noisy,B_mid_measure,0,4096,0.499755859375,0.500244140625,0.5,0.000244140625,Mid-circuit measurement collapses the state; expect ~50/50
+noise_depth,aer-noisy(generic_backend_5q),noisy,xx_pairs=0,0,4096,0.997314453125,0.002685546875,1.0,0.002685546875,
+noise_depth,aer-noisy(generic_backend_5q),noisy,xx_pairs=1,1,4096,0.99560546875,0.00439453125,1.0,0.00439453125,
+noise_depth,aer-noisy(generic_backend_5q),noisy,xx_pairs=2,2,4096,0.99609375,0.00390625,1.0,0.00390625,
+noise_depth,aer-noisy(generic_backend_5q),noisy,xx_pairs=4,4,4096,0.993408203125,0.006591796875,1.0,0.006591796875,
+noise_depth,aer-noisy(generic_backend_5q),noisy,xx_pairs=8,8,4096,0.9951171875,0.0048828125,1.0,0.0048828125,
+noise_depth,aer-noisy(generic_backend_5q),noisy,xx_pairs=16,16,4096,0.989990234375,0.010009765625,1.0,0.010009765625,
+noise_depth,aer-noisy(generic_backend_5q),noisy,xx_pairs=32,32,4096,0.98828125,0.01171875,1.0,0.01171875,
+noise_depth,aer-noisy(generic_backend_5q),noisy,xx_pairs=64,64,4096,0.979736328125,0.020263671875,1.0,0.020263671875,