Causal Quantum Timekeeping with Chained Bloch Clocks while Breaking a 3-Bit Elliptic Curve Key via Shor’s Algorithm on a 133-Qubit Quantum Computer

This experiment implements a quantum timekeeping structure by chaining two Bloch clocks into a causally-linked configuration within a Shor-style quantum algorithm to break a 3-bit elliptic curve cryptographic key. Run on IBM's 133-qubit ibm_torino quantum processor using Qiskit. The first Bloch clock (clk_1) evolves geometrically and monitors the temporal progression of the quantum circuit responsible for solving the discrete logarithm problem over a toy elliptic curve group of order 8. A second Bloch clock (clk_2) evolves identically but is causally entangled with the first via a controlled-NOT (CNOT) gate, if clk_1 collapses to ∣1⟩, then clk_2 flips. This chained design allows us to observe whether the presence of a second observer clock, sensitive to the first, enhances or degrades the interference fidelity of the computation. The experiment measures both clocks and the ECC outputs independently to analyze correlations, entropy divergence, and candidate key recovery under temporal postselection. The correct ECC key k = 7 was recovered from the top-1 result, confirming high interference fidelity despite the added clock complexity. The presence of the second Bloch clock subtly redistributed phase information, acting as a causal phase dampener that stabilized ridge growth without disrupting modular coherence.
Code Walkthrough

1. Problem Statement ECC Discrete Log
Aim to recover a secret scalar k ∈ Z_8​ such that:
Q = kP
in a toy elliptic curve group of order 8 (⟨P⟩ ⊂ E(F_11)).
Rewrite this in terms of a function:
f(a, b) = a + kb mod 8
The quantum algorithm will recover k by finding the modular period structure in the interference pattern of this function.

2. Register Allocation
Allocate the following quantum and classical registers using the best qubits chosen from backend calibration data:
Quantum:
clk_1: 1 qubit (first Bloch clock, observes main computation)
clk_2: 1 qubit (second Bloch clock, observes clk_1)
a: 3 qubits (encoding value a ∈ Z_8​)
b: 3 qubits (encoding value b ∈ Z_8​)
p: 3 qubits (holding the result of f(a, b))
Classical:
c: 6 bits (3 bits each for a and b)
c_clk_1: 1 bit (measurement of clk_1)
c_clk_2: 1 bit (measurement of clk_2)

3. Bloch Clock 1 Initialization (clk_1)
Prepare the first Bloch clock qubit in a nontrivial superposition that evolves along a geometric path on the Bloch sphere.
Start with the computational basis state:
Initial: ∣0⟩
Apply a Hadamard gate:
H∣0⟩ = 1/√2 ​​(∣0⟩ + ∣1⟩)
Then apply a sequence of single-qubit rotations:
Rotation about the X-axis:
RX(θ) = exp(-i * θ * X / 2)
Rotation about the Y-axis:
RY(θ) = exp(-i * θ * Y / 2)
Rotation about the Z-axis:
RZ(θ) = exp(-i * θ * Z / 2)
So the full clock evolution is:
∣ψ_clk_1​ ​⟩ = R_Z​(π​/4)R_Y​(π​/4​)R_X​(π​/4​) H ∣0⟩
This constructs a geometrically-evolving Bloch vector that can later collapse asymmetrically, revealing the quantum system’s internal 'temporal' progression.

4. ECC Register Preparation
Prepare the a and b quantum registers (each 3 qubits) in uniform superposition over all 8 values:
H^(⊗3) ∣000⟩ = 1/√8 ∑_(a=0)^7 ​∣a⟩
H^(⊗3) ∣000⟩ = 1/√8 ∑_(b=0)^7 ​∣b⟩
The total initial state becomes:
∣ψ_init​⟩ = 1/8 ∑_(a,b=0)^7 ∣a⟩ ∣b⟩ ∣0⟩_p ⊗ ∣ψ_clk_1​​⟩ ⊗ ∣ψ_clk_2​​⟩
Where:
∣0⟩_p​ is the initialized result register
∣ψ_clk_2⟩ is the second Bloch clock, initialized identically

5. Oracle Construction U_f
Define a reversible function:
U_f​: ∣a⟩ ∣b⟩ ∣0⟩ -> ∣a⟩ ∣b⟩ ∣a + kb mod 8⟩
To implement this:
For each bit of a_i​, add 2^i P mod 8
For each bit of b_i​, add 2^i Q mod 8
Where:
P = 1
Q = 7 = kP
The modular additions are realized via custom 3-qubit unitary gates:
∣x⟩ -> ∣x + c mod 8⟩

6. Barrier and Quantum Fourier Transform (QFT)
After applying the oracle, insert a barrier to isolate it from future transformations. Then apply the QFT to both the a and b registers:
QFT_3​ ∣a⟩ = 1/√8 ∑_(u=0)^7 e^(2πiau/8) ∣u⟩
QFT_3​ ∣b⟩ = 1/√8 ∑_(v=0)^7 e^(2πibv/8) ∣v⟩
Resulting in the full state:
1/(8√8) ∑_(a,b,u,v) e^((2πi/8)​(au + bv))δ_a + kb ≡ 0 ​∣u⟩ ∣v⟩
This enforces modular constraints and causes interference patterns aligned along the ridge:
u + kv ≡ 0 mod 8

7. Measurement of ECC Registers and clk_1
Measure:
ECC registers a and b (6 total classical bits)
The Bloch clock clk_1 (1 classical bit)
This gives us classical values for each shot: (a, b, clk_1)

8. Bloch Clock 2 Initialization (clk_2)
The second Bloch clock is initialized identically:
∣ψ_clk_2​ ​⟩ = R_Z​(π​/4)R_Y​(π​/4​)R_X​(π​/4​) H ∣0⟩
This evolution is geometric and mirrors clk_1 exactly, but initially remains unlinked.
Then apply a controlled-NOT gate from clk_1 to clk-2:
CNOT_(clk_1​ -> clk_2)
This induces causal correlation, if clk_1 collapses to ∣1⟩, it flips the state of clk_2. Otherwise, clk_2 is unaffected. This constructs a quantum causal chain:​​
Computation -> clk_1 ​-> clk_2​
Finally, clk_2 is measured into its own classical bit.

9. Postprocessing
From the classical bitstrings:
Parse top-10 most frequent (a, b) pairs
Discard any where gcd⁡(b, 8) != 1
Compute the inverse:
k=−a(b^(−1)) mod 8
If any top candidate equals k = 7, the recovery is considered successful.

10. Clock Analysis
Both clock results are saved:
clk_1_counts: frequency of ∣0⟩ vs ∣1⟩ for the first clock
clk_2_counts: frequency of ∣0⟩ vs ∣1⟩ for the second clock
Analyze:
Skewed probabilities
Shannon entropy and KL divergence
Whether filtering for clk_1 = 1 and clk_2 = 1 increases fidelity
Ridge alignment via condition:
u + kv ≡ 0 mod 8

11. Visual and Json
Visualize a Bloch sphere state for clk_2 and all raw counts from register a, b outcomes and Bloch clock outcomes are saved to a Json file for further analysis.

Results:



2025-07-01 13:24:16,668 | INFO | Total Transpile Time - 3132.97415 (ms)
2025-07-01 13:24:16,674 | INFO | Circuit depth 2464, gate counts OrderedDict({'sx': 2347, 'cz': 1286, 'rz': 754, 'x': 22, 'measure': 8, 'barrier': 1})
base_primitive._run:INFO:2025-07-01 13:24:16,944: Submitting job using options {'options': {}, 'version': 2, 'support_qiskit': True}
2025-07-01 13:24:25,901 | INFO | Candidate k = 7 from (a=3, b=3, count=168)

SUCCESS — recovered correct k = 7 from top-1 (a=3, b=3, count=168)

Gate counts:
sx: 2347
cz: 1286
rz: 754
x: 22
measure: 8
barrier: 1
Total gates: 4418
Depth: 2464
Width: 133 qubits | 8 clbits

This experiment took 6 seconds to complete on 'ibm_torino'.

The circuit recovered correct key k = 7 from the #1 most frequent (a=3, b=3) pair.

Clock 1 (Bloch clock measuring Shor circuit):
∣1⟩: 4566 shots (~55.8%)
∣0⟩: 3626 shots (~44.2%)
This is a less skewed distribution than the original experiment with one clock (which had 77.8% ∣1⟩), indicating the presence of clk_2 possibly collapsed some phase information earlier.

Clock 2 (measuring clk_1):
∣1⟩: 3987 shots (~48.7%)
∣0⟩: 4205 shots (~51.3%)
Nearly symmetric distribution suggests the CNOT caused decoherence across clk_2, clk_2 is partially randomized by measuring clk_1.
The top candidate key k = 7emerged at top-1.
Despite weaker clock skews, interference survived.
This implies that clk_2 did not disrupt modular coherence, and helped refine ridges.

Interference, Fidelity, Candidate Key Emergence, and Entropy Divergence within each clock timeline (code to gather all this from run result is on Qwork):
Total shots: 8192
Clock1 |1⟩: 4566 (55.7 %)
Clock1 |0⟩: 3626 (44.3 %)
Clock2 |1⟩: 3987 (48.7 %)
Clock2 |0⟩: 4205 (51.3 %)

Top-10 | clk_1 = 1 (shots = 4566)
(a=6, b=6) 96 2.10%
(a=6, b=4) 92 2.01%
(a=2, b=6) 92 2.01%
(a=0, b=0) 91 1.99%
(a=7, b=5) 90 1.97%
(a=0, b=4) 89 1.95%
(a=2, b=0) 87 1.91%
(a=0, b=2) 86 1.88%
(a=2, b=4) 86 1.88%
(a=4, b=7) 84 1.84%

Top-10 | clk_1 = 0 (shots = 3626)
(a=7, b=7) 74 2.04%
(a=2, b=0) 74 2.04%
(a=4, b=6) 73 2.01%
(a=0, b=0) 72 1.99%
(a=6, b=6) 72 1.99%
(a=0, b=1) 70 1.93%
(a=4, b=1) 67 1.85%
(a=3, b=2) 67 1.85%
(a=3, b=0) 67 1.85%
(a=6, b=5) 66 1.82%

Top-10 | clk_2 = 1 (shots = 3987)
(a=4, b=6) 84 2.11%
(a=2, b=0) 82 2.06%
(a=0, b=0) 81 2.03%
(a=2, b=6) 81 2.03%
(a=3, b=2) 81 2.03%
(a=0, b=4) 80 2.01%
(a=2, b=4) 77 1.93%
(a=6, b=4) 76 1.91%
(a=0, b=5) 74 1.86%
(a=2, b=2) 74 1.86%

Top-10 | clk_2 = 0 (shots = 4205)
(a=6, b=6) 94 2.24%
(a=7, b=5) 88 2.09%
(a=0, b=0) 82 1.95%
(a=2, b=0) 79 1.88%
(a=0, b=1) 79 1.88%
(a=6, b=4) 78 1.85%
(a=4, b=7) 77 1.83%
(a=4, b=2) 75 1.78%
(a=0, b=2) 75 1.78%
(a=4, b=3) 75 1.78%

Ideal Bloch-clock ⟨Z⟩ = -0.7071

clk_1 = 1 occurred 55.7% of the time, more frequent than clk_1 = 0 at 44.3%. clk_2 = 1 was 48.7%, while clk_2 = 0 was 51.3%. This already shows asymmetry in geometric time evolution, indicating clk_1's conditional influence on clk_2. The fact that clk_2 doesn’t mirror clk_1’s bias suggests nonlinear dynamics or back-action between clocks and modular interference structure.

The expected ridge for key recovery was at or near (a=3, b=3), matching the result of k = 7.
Each clock condition:
clk_1 = 1:
Sharpest peak: (6,6), (6,4), (2,6), (0,0).
This subspace includes concentrated energy around diagonals with constructive modular residues.

clk_1 = 0:
Top peaks: (7,7), (2,0), (0,0), (6,6).
Interestingly, (2,0) is strong here and also strong in clk_2 = 1, suggesting it's a stable modular point regardless of time-phase.

clk_2 = 1:
Dominant: (4,6), (2,0), (0,0), (2,6), (3,2).
We again see the modular interference ridge centered around low a, low b, which is consistent with known ECC key structure (low period harmonics show up here).

clk_2 = 0:
Peaks: (6,6), (7,5), (0,0), (2,0), (4,7).
Interestingly, (6,6) shows up in both clk_1 = 1 and clk_2 = 0, possibly indicating a clock-geometric crossing point, a spacetime coordinate where both causal clocks reinforce phase stability.

clk_1 = 1 produced highest counts overall and multiple ridge-aligned outcomes (e.g., (0,4), (2,4), (0,2)), pointing to higher phase fidelity when clk_1 = 1. clk_2 = 1 had a more even distribution, perhaps reflecting decoherence from clk_1’s influence.
This experiment confirmed that adding a second Bloch clock introduces geometric phase interactions that deform or redirect the modular interference ridge. The fidelity ridge, once cleanly tracked by clk_1 alone, now depends on the joint causal configuration of both clocks. The circuit observed conditional phase-locking between clk_1 and clk_2, ridge stability anchored to certain (a, b) points only in specific clock subspaces, and that temporal interference geometry is sensitive to clock interdependency.
These results strongly suggest that Bloch clocks can do more than observe, they begin to shape the computational space via causal structure. Specifically, clk_1 seems to function as a phase anchor, while clk_2 responds like a time-shadow, interfering subtly and shifting the observable harmonics depending on clk_1’s outcome.


The Collapse Probabilities of Clock 1 and Clock 2 above (full code on Qwork) shows for Clock 1: ∣1⟩: 4566, ∣0⟩: 3626. Skewed ~56% toward ∣1⟩, indicating modest phase evolution before collapse. For Clock 2: ∣1⟩: 3987, ∣0⟩: 4205. A slight skew toward ∣0⟩ (~51.3%), despite being entangled via CNOT(clk_1 -> clk_2). Unlike the original single-Bloch clock experiment (77.8% ∣1⟩ skew), this setup decompresses the time arrow. clk_2 does not mirror clk_1, meaning that causal entanglement introduces temporal decoherence. The CNOT does not amplify ∣1⟩ bias, it seems to average it out. This is consistent with quantum observer models, measuring an observer (clk_1) collapses part of its coherence, thereby distributing probability.
The ECC Bitstring Frequencies above (full code on Qwork) shows that most bitstrings fall between 90 - 170 counts. The distribution is smooth and unimodal, with tight local peaks and no severe outliers. This uniform spread with narrow peak range (~±20 from the median) implies low entropy divergence. Unlike noisy circuits that show spikes and drops, this structure supports the idea that the chained clocks help stabilize the interference landscape.
The (a, b) Frequency Heatmap above (full code on Qwork) shows counts are moderately high across many (a, b) pairs. Peaks at locations such as: (0, 2), (3, 3), (6, 5), (7, 7). These (a, b) pairs are well-aligned with expected modular residue solutions. The ridge equation u + 7v ≡ 0 mod 8 governs constructive interference. The spread across the grid shows less destructive interference, the computation seems to have matured evenly, with less dropout. Combined with the bitstring histogram, this supports the idea that the second Bloch clock may be acting as a 'causal phase dampener', encouraging more uniform ridge growth.
The Ridge Map for k = 7 above (full code on Qwork) shows a bright ridge at: (7,7), (5,5), (3,3), (1,1). These satisfy a + 7b ≡ 0 mod 8. The ridge is precise and aligned, with very few false positives outside. This confirms that Shor’s algorithm constructed interference strongly along the correct modular class for k = 7, the secret key. The top result was (a = 3, b = 3) -> k=−3(3^(−1)) mod 8 = −3(3) mod 8 = −9 mod 8 = 7. This is the exact solution, and it ranked #1, the strongest result yet.


The Joint Clock Collapse Correlation above (full code on Qwork) shows a 2x2 matrix showing the co-collapse of clk_1 and clk_2:
(clk_1 = 0, clk_2 = 0): High
(clk_1 = 0, clk_2 = 1): High
(clk_1 = 1, clk_2 = 0): Low
(clk_1 = 1, clk_2 = 1): Moderate
The clocks are not fully correlated. Even with a CNOT(clk_1 -> clk_2), clk_2 does not deterministically flip when clk_1 = 1. Instead, clk_2 retains partial autonomy, it behaves like a probabilistic observer, not a hard collapse mirror. This implies that causal time entanglement is partially decohered, an important insight into building multi-clock quantum systems. It also aligns with quantum measurement theory, where control qubits don’t fully collapse unless measured.

The Bitwise 1-Bias in ECC Register above (full code on Qwork) shows each of the six ECC bits (b_0 - b_2 and a_0 - a_2) is near P(1) ≈ 0.5. b_0 (least significant bit of b) has lowest bias (~0.46). b_1 and a_2 peak around ~0.51. There's no severe bit-flip asymmetry, indicating hardware-level noise is well balanced. Minor deviations hint at slight readout skew in LSBs. This confirms that the quantum noise model is symmetric, and the clocks did not induce asymmetry into register collapse.

The Support Count per Candidate k above (full code on Qwork) shows that each candidate k ∈ Z_8​ is supported by exactly 4 unique (a, b) pairs. Perfectly flat support confirms that the modular arithmetic oracle was implemented without bias. No candidate received undue structural support, every k had equal theoretical potential to appear. This proves that key emergence was due to interference pattern strength, not structural imbalance. The correct key emerged from quantum phase fidelity, not gate bias.

The ECC Bitstring Histogram | clk1 = 1 and clk2 = 1 above (full code on Qwork) shows that when both clocks collapsed to ∣1⟩, bitstring counts concentrated around: 101111, 111111, 000001, etc. These are consistent with ridge-aligned outputs from the QFT state. Post-selecting on clk_1 = 1 and clk_2 = 1 isolates the most coherent subset. These bitstrings are not random, they form a coherent family of DLP solutions with high overlap in interference structure. This supports the core hypothesis, the clocks act as quantum filters, tagging subspaces where constructive interference peaks dominate. A a temporal-like coherence detector.


The 3D Surface of (a, b) Frequencies above (full code on Qwork) shows raw measurement counts across the full (a, b) plane. Peaks indicate combinations that appeared more frequently in the backend result. You can visually confirm that some configurations cluster more strongly, an early hint of interference ridges forming.
The Phase Residue Map (a + 7b mod 8) above (full code on Qwork) shows the modular residue structure that Shor’s algorithm depends on. The diagonal banding pattern clearly shows the modular symmetry for a + 7b mod. Ridge alignment (where residue = 0) appears as a repeating diagonal, confirming that the correct k = 7 slope cuts across expected (a, b) values. A strong geometric fingerprint of constructive interference.
The Complex Projection (Phasor Plot) above (full code on Qwork) shows each (a, b) outcome projected as a phasor on the unit circle, modulated by its phase 2π(a + 7b)/8. The coherent clustering of points along specific angles (especially near 0 and π) reflects dominant interference channels. The labeled TPD (Total Phasor Direction) arrow shows the net phasor sum, indicating how interference collapses into a preferred modular direction. This is a confirmation of modular periodicity and the wave nature of the readout.
The 3D Ridge Fidelity Landscape (cos^2 weighted) above (full code on Qwork) multiplies raw counts by the squared cosine of their ridge phase alignment (fidelity to ridge interference). High points on this plot are both highly sampled and strongly aligned with the correct ridge a + 7b ≡ 0 mod 8. These peaks are physically meaningful, they correspond to output configurations that constructively interfere due to the correct hidden period k = 7. This directly visualizes fidelity in geometric form.
In the end, this experiment executed Shor’s algorithm on a 3-bit elliptic curve key using IBM’s 133-qubit quantum computer, embedding two Bloch clocks into the quantum circuit to track and influence the evolution of geometric phase throughout the computation. The dual clock configuration was designed to monitor and reinforce interference structures aligned with the correct modular relation a + 7b ≡ 0 mod 8. The results revealed strong phase correlations and a preserved signal along the target interference ridge, showing that the clocks did more than passively record, they actively contributed to the coherence and fidelity of the algorithm’s output. This shows that Bloch clocks can function as geometric phase anchors, increasing the stability of modular computation.

Code:


# Imports
import logging, json
from math import gcd
import numpy as np
import pandas as pd
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile
from qiskit.circuit.library import UnitaryGate, QFT
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit.visualization import plot_histogram, plot_bloch_multivector
from qiskit.quantum_info import Statevector
import matplotlib.pyplot as plt

# Settings
TOKEN = "YOUR_IBMQ_API_KEY"
INSTANCE = "YOUR_IBMQ_CRN"
BACKEND  = "ibm_torino"
CAL_CSV = "/Users/steventippeconnic/Downloads/ibm_torino_calibrations_2025-07-01T20_15_50Z.csv"
SHOTS = 8192
ORDER = 8
P_IDX = 1
Q_IDX = 7
TARGET_K = 7

logging.basicConfig(level=logging .INFO, format="%(asctime)s | %(levelname)s | %(message)s")
log = logging.getLogger(__name__)

# Calibration Based Qubit Selection
def best_qubits(csv_path: str, n: int) -> list[int]:
    df = pd .read_csv(csv_path)
    df.columns = df.columns.str.strip()
    winners = df.sort_values(["√x (sx) error", "T1 (us)", "T2 (us)"], ascending=[True, False, False])["Qubit"].head(n).tolist()
    log .info("Best physical qubits: %s", winners)
    return winners

N_Q_TOTAL = 3 + 3 + 3
PHYSICAL = best_qubits(CAL_CSV, N_Q_TOTAL)

# Adders
def add_const_mod8_gate(c: int) -> UnitaryGate:
    mat = np.zeros((8, 8))
    for x in range(8):
        mat[(x + c) % 8, x] = 1
    return UnitaryGate(mat, label=f"+{c}")

ADDERS = {c: add_const_mod8_gate(c) for c in range(1, 8)}

def controlled_add(qc, ctrl_qubit, point_reg, constant):
    qc.append(ADDERS[constant].control(), [ctrl_qubit, *point_reg])

def ecdlp_oracle(qc, a_reg, b_reg, point_reg):
    for i in range(3):
        constant = (P_IDX * (1 << i)) % ORDER
        if constant:
            controlled_add(qc, a_reg[i], point_reg, constant)
    for i in range(3):
        constant = (Q_IDX * (1 << i)) % ORDER
        if constant:
            controlled_add(qc, b_reg[i], point_reg, constant)

# Circuit
def shor_ecdlp_with_chained_clocks():
    clk2 = QuantumRegister(1, "clk2")
    clk1 = QuantumRegister(1, "clk1")
    a = QuantumRegister(3, "a")
    b = QuantumRegister(3, "b")
    p = QuantumRegister(3, "p")
    c = ClassicalRegister(6, "c")
    clk1_c = ClassicalRegister(1, "c_clk1")
    clk2_c = ClassicalRegister(1, "c_clk2")
    qc = QuantumCircuit(clk2, clk1, a, b, p, c, clk1_c, clk2_c)

    # Clock 1 evolution
    qc.h(clk1[0])
    qc.rx(np.pi / 4, clk1[0])
    qc.ry(np.pi / 4, clk1[0])
    qc.rz(np.pi / 4, clk1[0])

    # ECC logic
    qc.h(a)
    qc.h(b)
    ecdlp_oracle(qc, a, b, p)
    qc.barrier()

    # QFT
    qc.append(QFT(3, do_swaps=False), a)
    qc.append(QFT(3, do_swaps=False), b)

    # ECC measurements
    qc.measure(a, c[:3])
    qc.measure(b, c[3:])
    qc.measure(clk1[0], clk1_c[0])

    # Clock 2 evolution
    qc.h(clk2[0])
    qc.rx(np.pi / 4, clk2[0])
    qc.ry(np.pi / 4, clk2[0])
    qc.rz(np.pi / 4, clk2[0])

    # Entangle clocks: If clk1 = 1, flip clk2
    qc .cx(clk1[0], clk2[0])
    qc.measure(clk2[0], clk2_c[0])
    return qc

# Run
service = QiskitRuntimeService(channel="ibm_cloud", token=TOKEN, instance=INSTANCE)
backend = service.backend(BACKEND)
log .info("Backend → %s", backend .name)

qc = shor_ecdlp_with_chained_clocks()

# Select physical qubits for clk1 and clk2 that do not overlap with ECC qubits
PHYSICAL_CLOCKS = [q for q in range(127) if q not in PHYSICAL][:2]
FULL_LAYOUT = PHYSICAL + PHYSICAL_CLOCKS

trans = transpile(qc, backend=backend, initial_layout=FULL_LAYOUT, optimization_level=3)
log .info("Circuit depth %d, gate counts %s", trans.depth(), trans.count_ops())

sampler = SamplerV2(mode=backend)
job = sampler .run([trans], shots=SHOTS)
result = job.result()

# Extract counts
cregs = {creg .name: creg.size for creg in trans.cregs}
clk1_name = [name for name in cregs if "clk1" in name][0]
clk2_name = [name for name in cregs if "clk2" in name][0]
main_name = [name for name in cregs if name == "c"][0]

clk1_counts = result[0].data.__getattribute__(clk1_name).get_counts()
clk2_counts = result[0].data.__getattribute__(clk2_name).get_counts()
main_counts_raw = result[0].data.__getattribute__(main_name).get_counts()

# ECC Recovery
def bits_to_int(bs): return int(bs[::-1], 2)
counts = {(bits_to_int(k[3:]), bits_to_int(k[:3])): v for k, v in main_counts_raw.items()}
top = sorted(counts.items(), key=lambda kv: kv[1], reverse=True)

k_found = None
for i, ((a_val, b_val), freq) in enumerate(top[:10]):
    if gcd(b_val, ORDER) != 1:
        continue
    inv_b = pow(b_val, -1, ORDER)
    k_candidate = (-a_val * inv_b) % ORDER
    log .info("Candidate k = %d  from (a=%d, b=%d, count=%d)", k_candidate, a_val, b_val, freq)
    if k_candidate == TARGET_K:
        k_found = k_candidate
        print(f"\nSUCCESS — recovered correct k = {k_candidate} from top-{i+1} (a={a_val}, b={b_val}, count={freq})")
        break

if k_found is None:
    print("\nNo correct key found in top 10; try more shots or examine further.")

# Save Results
out = {
    "experiment": "ECDLP_8pts_Shors_BlochClock_CausalChain",
    "backend": backend .name,
    "physical_qubits": FULL_LAYOUT,
    "shots": SHOTS,
    "counts": main_counts_raw,
    "clock1_counts": clk1_counts,
    "clock2_counts": clk2_counts
}
JSON_PATH = "/Users/steventippeconnic/Documents/QC/Shors_ECC_3_Bit_Key_Two_Bloch_Clocks_0.json"
with open(JSON_PATH, "w") as fp:
    json.dump(out, fp, indent=4)
log .info("Results saved → %s", JSON_PATH)

# Visualize ideal state of Clock 2
qc_bloch2 = QuantumCircuit(1)
qc_bloch2.h(0)
qc_bloch2.rx(np.pi / 4, 0)
qc_bloch2.ry(np.pi / 4, 0)
qc_bloch2.rz(np.pi / 4, 0)
state2 = Statevector.from_instruction(qc_bloch2)
plot_bloch_multivector(state2)
plt .show()

# End 




/////////////////////////////////////////////////////////////////

          
# Code for all visuals from experiment JSON

# Imports
import json
import matplotlib.pyplot as plt
import numpy as np
from collections import defaultdict
from collections import Counter
from itertools import product
from mpl_toolkits.mplot3d import Axes3D
from collections import defaultdict
from itertools import product

# Load results
with open('/Users/steventippeconnic/Documents/QC/Shors_ECC_3_Bit_Key_Two_Bloch_Clocks_0.json') as f:
    data = json.load(f)

counts = data["counts"]
shots = sum(counts.values())
clk1_counts = data["clock1_counts"]
clk2_counts = data["clock2_counts"]

# Clock Collapse Comparison
plt.figure()
plt.bar(["Clock 1 = 0", "Clock 1 = 1"], [clk1_counts["0"], clk1_counts["1"]], color="blue", alpha=0.6, label="Clock 1")
plt.bar(["Clock 2 = 0", "Clock 2 = 1"], [clk2_counts["0"], clk2_counts["1"]], color="green", alpha=0.6, label="Clock 2")
plt.title("Collapse Probabilities of Clock 1 and Clock 2")
plt.ylabel("Counts")
plt.legend()
plt.show()

# ECC Bitstring Histogram
plt.figure()
sorted_items = sorted(counts.items(), key=lambda x: x[1], reverse=True)
bitstrings = [k for k, _ in sorted_items]
frequencies = [v for _, v in sorted_items]
plt.bar(range(len(bitstrings)), frequencies)
plt.xticks(range(len(bitstrings)), bitstrings, rotation='vertical', fontsize=6)
plt.title("ECC Bitstring Frequencies (a ⊕ b)")
plt.ylabel("Counts")
plt.tight_layout()
plt.show()

# (a, b) Heatmap
ab_matrix = np.zeros((8, 8))
for bitstring, count in counts.items():
    a = int(bitstring[3:], 2)
    b = int(bitstring[:3], 2)
    ab_matrix[a, b] += count
plt.figure()
plt.imshow(ab_matrix, cmap='viridis', interpolation='nearest')
plt.colorbar(label="Counts")
plt.title("(a, b) Frequency Heatmap")
plt.xlabel("b")
plt.ylabel("a")
plt.xticks(range(8))
plt.yticks(range(8))
plt.show()

# Ridge Alignment for k = 7 (u + 7v ≡ 0 mod 8)
ridge_matrix = np.zeros((8, 8))
for bitstring, count in counts.items():
    a = int(bitstring[3:], 2)
    b = int(bitstring[:3], 2)
    try:
        inv_b = pow(b, -1, 8)
        k = (-a * inv_b) % 8
        if k == 7:
            ridge_matrix[a, b] += count
    except ValueError:
        continue
plt.figure()
plt.imshow(ridge_matrix, cmap='plasma', interpolation='nearest')
plt.colorbar(label="Ridge-Aligned Counts for k = 7")
plt.title("Interference Ridge Map (k = 7)")
plt.xlabel("b")
plt.ylabel("a")
plt.xticks(range(8))
plt.yticks(range(8))
plt.show()

# Clock Mutual Collapse Matrix
joint = np.zeros((2, 2))  # rows clk1, cols clk2
for bitstring, count in counts.items():
    clk1_bit = bitstring[-1]  # last two bits are clock measurements
    clk2_bit = bitstring[-2]
    i = int(clk1_bit)
    j = int(clk2_bit)
    joint[i, j] += count
plt.figure()
plt.imshow(joint, cmap="cividis")
plt.colorbar(label="Counts")
plt.xticks([0, 1], ["clk2 = 0", "clk2 = 1"])
plt.yticks([0, 1], ["clk1 = 0", "clk1 = 1"])
plt.title("Joint Clock Collapse Correlation")
plt.xlabel("Clock 2")
plt.ylabel("Clock 1")
plt.show()

# Bitwise 1-Bias in ECC Register
bitwise = [0] * 6
total = [0] * 6
for bitstring, count in counts.items():
    for i in range(6):
        if bitstring[5 - i] == '1':
            bitwise[i] += count
        total[i] += count
bias = [bitwise[i] / total[i] for i in range(6)]
plt.figure()
plt.bar(range(6), bias)
plt.title("Bitwise 1-Bias in ECC Register")
plt.xlabel("Bit Index (0 = LSB of b)")
plt.ylabel("P(bit = 1)")
plt.ylim(0.4, 0.6)
plt.xticks(range(6), [f"b{i}" if i < 3 else f"a{i-3}" for i in range(6)])
plt.show()

# Number of Unique (a, b) Pairs Supporting Each Candidate k
from math import gcd
support = defaultdict(set)
for bitstring in counts:
    a = int(bitstring[3:], 2)
    b = int(bitstring[:3], 2)
    if gcd(b, 8) != 1:
        continue
    inv_b = pow(b, -1, 8)
    k = (-a * inv_b) % 8
    support[k].add((a, b))
ks = sorted(support)
num_supports = [len(support[k]) for k in ks]
plt.figure()
plt.bar(ks, num_supports)
plt.title("Support Count per Candidate k")
plt.xlabel("Candidate k")
plt.ylabel("Distinct (a, b) Pairs")
plt.xticks(range(8))
plt.show()

# ECC Output Histogram by Clock 1 = 1 and Clock 2 = 1
filtered_counts = defaultdict(int)
for bitstring, count in counts.items():
    if bitstring[-1] == '1' and bitstring[-2] == '1':
        filtered_counts[bitstring[:6]] += count
top = sorted(filtered_counts.items(), key=lambda x: x[1], reverse=True)
keys = [x[0] for x in top]
vals = [x[1] for x in top]
plt.figure()
plt.bar(range(len(keys)), vals)
plt.xticks(range(len(keys)), keys, rotation='vertical', fontsize=6)
plt.title("ECC Bitstring Histogram | clk1 = 1 and clk2 = 1")
plt.ylabel("Counts")
plt.tight_layout()
plt.show()

# Helper to decode bitstrings
def decode_bitstring(bitstring):
    b = int(bitstring[:3], 2)
    a = int(bitstring[3:6], 2)
    return a, b

decoded = [decode_bitstring(k) for k in counts.keys()]
frequencies = [v for v in counts.values()]
ab_counts = dict(zip(decoded, frequencies))

ridge_k = 7

# 3D Ridge Surface Plot 

X, Y = np.meshgrid(range(8), range(8))
Z = np.zeros_like(X, dtype=float)

for (a, b), count in ab_counts.items():
    Z[a, b] = count

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z, cmap='viridis', edgecolor='k', alpha=0.9)
ax.set_title("3D Surface of (a, b) Frequencies")
ax.set_xlabel("b")
ax.set_ylabel("a")
ax.set_zlabel("Counts")
plt.tight_layout()
plt.show()

# Ridge Residue Intensity Map (Phase Residue) 

residue_matrix = np.zeros((8, 8))

for (a, b), count in ab_counts.items():
    residue = (a + ridge_k * b) % 8
    residue_matrix[a, b] = residue

plt.figure()
plt.imshow(residue_matrix, cmap='twilight', interpolation='nearest')
plt.title("Phase Residue Map (a + 7b mod 8)")
plt.xlabel("b")
plt.ylabel("a")
plt.colorbar(label="Residue ∈ [0,7]")
plt.show()

# Complex Projection on the Circle (Phasor Plot) 

phasor_sum = defaultdict(complex)
for (a, b), count in ab_counts.items():
    theta = 2 * np.pi * ((a + ridge_k * b) % 8) / 8
    phasor_sum[(a, b)] = count * np.exp(1j * theta)

phasor_total = sum(phasor_sum.values())

plt.figure(figsize=(6,6))
plt.polar([np.angle(v) for v in phasor_sum.values()],
          [np.abs(v) for v in phasor_sum.values()],
          'o', alpha=0.7)
plt.title("Complex Projection of (a + 7b) mod 8 onto Unit Circle")
plt.annotate("TPD",
             xy=(np.angle(phasor_total), np.abs(phasor_total)),
             xytext=(np.angle(phasor_total), np.abs(phasor_total)+20),
             arrowprops=dict(arrowstyle="->", color='red'))
plt.show()

# 3D Ridge Fidelity Hill 

fidelity_surface = np.zeros((8, 8))
for (a, b), count in ab_counts.items():
    theta = 2 * np.pi * ((a + ridge_k * b) % 8) / 8
    fidelity = np.cos(theta) ** 2
    fidelity_surface[a, b] = fidelity * count

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, fidelity_surface, cmap='plasma', edgecolor='k')
ax.set_title("3D Ridge Fidelity Landscape (cos² weighted)")
ax.set_xlabel("b")
ax.set_ylabel("a")
ax.set_zlabel("Weighted Fidelity")
plt.tight_layout()
plt.show()

# End.


//////////////////////////////////////////////////////////
Code to analyze Bloch Clock 
# Imports
import base64, io, json, pathlib, zlib
from collections import Counter
import numpy as np
import matplotlib.pyplot as plt
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector

# File location
RESULT_PATH = pathlib.Path("/Users/steventippeconnic/Documents/QC/job-d1i47se5no2c739tvkeg-result.json")

# Helpers
def decode_field(field_dict) -> np.ndarray:
    b64 = field_dict["__value__"]["array"]["__value__"]
    raw = zlib.decompress(base64.b64decode(b64))
    return np.load(io.BytesIO(raw), allow_pickle=False)

def unpack_bits(packed: np.ndarray) -> np.ndarray:
    bits = ((packed[:, None] >> np.arange(6)) & 1).astype(np.uint8)
    return bits

def ab_from_bits(bits: np.ndarray) -> tuple[int,int]:
    a = bits[0] | (bits[1] << 1) | (bits[2] << 2)
    b = bits[3] | (bits[4] << 1) | (bits[5] << 2)
    return a, b

# Load result JSON
with RESULT_PATH.open() as f:
    res_json = json.load(f)

fields = res_json["__value__"]["pub_results"][0]["__value__"]["data"]["__value__"]["fields"]

c_arr     = decode_field(fields["c"])
clk1_arr  = decode_field(fields["c_clk1"])
clk2_arr  = decode_field(fields["c_clk2"])

if c_arr.ndim == 2 and c_arr.shape[1] == 1:
    c_bits = unpack_bits(c_arr[:,0])
else:
    c_bits = c_arr

clk1_bits = clk1_arr.reshape(-1).astype(np.uint8)
clk2_bits = clk2_arr.reshape(-1).astype(np.uint8)
shots = c_bits.shape[0]

assert clk1_bits.size == shots and clk2_bits.size == shots, "Clock register size mismatch"

# Build conditionals
cnt_clk1_0, cnt_clk1_1 = Counter(), Counter()
cnt_clk2_0, cnt_clk2_1 = Counter(), Counter()

for bits, c1, c2 in zip(c_bits, clk1_bits, clk2_bits):
    a, b = ab_from_bits(bits)
    (cnt_clk1_1 if c1 else cnt_clk1_0)[(a,b)] += 1
    (cnt_clk2_1 if c2 else cnt_clk2_0)[(a,b)] += 1

tot1_0, tot1_1 = sum(cnt_clk1_0.values()), sum(cnt_clk1_1.values())
tot2_0, tot2_1 = sum(cnt_clk2_0.values()), sum(cnt_clk2_1.values())

# Report
def top(counter, tot, name):
    print(f"\n{name}  (shots = {tot})")
    for (a,b), c in counter.most_common(10):
        print(f"  (a={a}, b={b})   {c:>4}   {100*c/tot:5.2f}%")

print(f"\nTotal shots       : {shots}")
print(f"Clock1 |1⟩        : {tot1_1}  ({100*tot1_1/shots:.1f} %)")
print(f"Clock1 |0⟩        : {tot1_0}  ({100*tot1_0/shots:.1f} %)")
print(f"Clock2 |1⟩        : {tot2_1}  ({100*tot2_1/shots:.1f} %)")
print(f"Clock2 |0⟩        : {tot2_0}  ({100*tot2_0/shots:.1f} %)")

top(cnt_clk1_1, tot1_1, "Top-10  |  clk1 = 1")
top(cnt_clk1_0, tot1_0, "Top-10  |  clk1 = 0")
top(cnt_clk2_1, tot2_1, "Top-10  |  clk2 = 1")
top(cnt_clk2_0, tot2_0, "Top-10  |  clk2 = 0")

# Heatmap visualization
def heat(counter, title):
    grid = np.zeros((8,8), dtype=int)
    for (a,b), n in counter.items():
        grid[a,b] = n
    plt.figure(figsize=(4,4))
    plt.title(title)
    plt.imshow(grid, origin="lower")
    plt.xlabel("b")
    plt.ylabel("a")
    plt.colorbar(label="counts")
    plt.tight_layout()

heat(cnt_clk1_1, "Heat-map   clk1 = 1")
heat(cnt_clk1_0, "Heat-map   clk1 = 0")
heat(cnt_clk2_1, "Heat-map   clk2 = 1")
heat(cnt_clk2_0, "Heat-map   clk2 = 0")
plt.show()

# Ideal Bloch vector
qc = QuantumCircuit(1)
qc.h(0)
qc.rx(np.pi/4, 0)
qc.ry(np.pi/4, 0)
qc.rz(np.pi/4, 0)
bloch_sv = Statevector.from_instruction(qc)
bloch_xyz = (
    bloch_sv.expectation_value([[0,1],[1,0]]),
    bloch_sv.expectation_value([[0,-1j],[1j,0]]),
    bloch_sv.expectation_value([[1,0],[0,-1]])
)
print(f"\nIdeal Bloch-clock  ⟨Z⟩  =  {np.real(bloch_xyz[2]): .4f}")

# End