Skip to content

Problems measuring logical T+ state of a surface QEC code #1029

@sergei-mironov

Description

@sergei-mironov

Let me report a possible problem in the near-Clifford simulation. The below code initializes a surface quantum error correction code in a T+ state and calculates 200 shots of its logical measurements out of raw 'physical' ones in X basis. The logical measurements are then saved into the logical_measurements.json file. If one puts the default 'lightning.qubit' device of PennyLane, the statistics would be something like 170 0s against 30 1s, which approaches the predicted value of sqrt(2)/2 ~= 0.7. However, with the shown QRack device I get inconsistent results which is far from expectations (e.g. 91/109 or 114/86).

I'm using pennylane 0.43.1, pennylane_catalyst 0.13.0, pennylane_qrack 0.25.0 (compiled from source). The simulation is done with isTensorNetwork flag set to False.

Can we improve the precision of this simulation mode? Does it have a maximum-precision mode which would match the statevector method's precision? Please let me know, I will be glad to debug or adjust parameters.

--

The runnable program:

import json
import time
from contextlib import contextmanager
from signal import ITIMER_REAL, setitimer
from functools import reduce

import pennylane as qml
from catalyst import qjit, measure as catalyst_measure

num_shots = 200
nattempts = 1
timeout = 3000.0
dry_run = False


@contextmanager
def with_alarm(timeout: float):
    timer_set = False
    try:
        if timeout is not None and 0 < timeout < float("inf"):
            setitimer(ITIMER_REAL, timeout)
            timer_set = True
        yield
    finally:
        if timer_set:
            setitimer(ITIMER_REAL, 0)


def dump_logical_observables(measurement_attempts) -> None:
    """ For each observable, calculate its value from measurements (by xor-ing them) and dump to the
    configured JSON file.  """
    logical_values_attempts = []
    logical_measurements = {'q1_X_0': ('c_(0, 0, 0)_0', 'c_(1, 0, 0)_0', 'c_(2, 0, 0)_0', 'c_(2, 1, 1)_0', 'c_(0, 1, 1)_0')}

    for attempt_shots in measurement_attempts:
        if not attempt_shots:
            continue
        nshots = len(attempt_shots)
        logical_values = {}

        for lol, meas_labels in logical_measurements.items():
            lo_shots = []
            for i in range(nshots):
                shot_dict = attempt_shots[i]
                samples = [int(shot_dict[l]) for l in meas_labels]
                lo_value = reduce(lambda a,b: a ^ b, samples)
                lo_shots.append(int(lo_value))
            logical_values[lol] = lo_shots

        logical_values_attempts.append(logical_values)

    with open("logical_measurements.json", "w") as f:
        json.dump(logical_values_attempts, f)

if not dry_run:
    @qjit
    @qml.qnode(qml.device("qrack.simulator",wires=17,isTensorNetwork=False,isStabilizerHybrid=True,isOpenCL=True,isPaged=False,isCpuGpuHybrid=False,), shots=num_shots)
    def pl_main():

        # PennyLane Program Generated from Eka
        # Initialize Wires from Eka quantum channels Ids
        measurements = {}
        qml.registers({'a643ceef-00bc-44cd-8514-b7b8cbe8bf95': 1, 'f0252ea6-ffa6-40f9-a8cf-aa886542e808': 1, '64a8a798-6799-4dde-8eb3-c4e1c3591cc8': 1, '18823f07-02b7-4965-9f2b-63737573cd92': 1, 'e299cd6f-5b93-4001-92bc-eff2ac056fff': 1, 'e6f6e48b-741f-471a-a0f6-20c0006971cd': 1, 'bf7ed026-3336-4705-a285-17c9b788d74d': 1, '3adeb324-cb13-4eaf-9c84-6d286ee08c4b': 1, '18788481-309e-4b4f-b8de-f81f5ebe0b49': 1, '123d3886-b07a-4655-a739-5f9ef84a1170': 1, '1fb8be97-58fa-48c3-812d-e946b2e6e58d': 1, 'c2e764ee-9cb3-4115-82e7-384bda96e261': 1, 'e6dd20b2-4b29-4abd-9bf5-25f109954f51': 1, '25b6c46e-948f-4ffd-a5a3-ca9c190d6598': 1, '7fb8aa79-b6a4-4a31-81fb-705992eee653': 1, 'd5c2ab4f-ac70-4df2-8413-60d7219cdc2c': 1, 'ca4f79db-08f6-4011-9d58-11f5a350911c': 1})
        
        # Start of circuit: final circuit
        # Start of circuit: inject t into block q1 and measure syndromes
        # Start of circuit: inject t into block q1
        # Start of circuit: resource state reset
        catalyst_measure(5, reset=True)
        qml.Hadamard([5])
        qml.T([5])
        #  End of circuit: resource state reset
        # Start of circuit: reset four quadrants
        catalyst_measure(0, reset=True)
        qml.Hadamard([0])
        catalyst_measure(1, reset=True)
        qml.Hadamard([1])
        catalyst_measure(12, reset=True)
        qml.Hadamard([12])
        catalyst_measure(14, reset=True)
        qml.Hadamard([14])
        catalyst_measure(4, reset=True)
        catalyst_measure(10, reset=True)
        catalyst_measure(3, reset=True)
        catalyst_measure(7, reset=True)
        #  End of circuit: reset four quadrants
        #  End of circuit: inject t into block q1
        # Start of circuit: measure q1 syndromes 1 time(s)
        # Start of circuit: measure q1 syndromes - cycle 0
        catalyst_measure(6, reset=True)
        catalyst_measure(15, reset=True)
        catalyst_measure(8, reset=True)
        catalyst_measure(13, reset=True)
        catalyst_measure(2, reset=True)
        catalyst_measure(16, reset=True)
        catalyst_measure(11, reset=True)
        catalyst_measure(9, reset=True)
        qml.Hadamard([6])
        qml.Hadamard([15])
        qml.Hadamard([8])
        qml.Hadamard([13])
        qml.Hadamard([2])
        qml.Hadamard([16])
        qml.Hadamard([11])
        qml.Hadamard([9])
        qml.CZ([6, 4])
        qml.CZ([15, 12])
        qml.CNOT([8, 5])
        qml.CNOT([13, 10])
        qml.CNOT([2, 0])
        qml.CZ([9, 7])
        qml.CZ([6, 0])
        qml.CZ([15, 5])
        qml.CNOT([8, 7])
        qml.CNOT([13, 12])
        qml.CNOT([2, 1])
        qml.CZ([9, 3])
        qml.CZ([6, 5])
        qml.CZ([15, 14])
        qml.CNOT([8, 1])
        qml.CNOT([13, 4])
        qml.CNOT([16, 12])
        qml.CZ([11, 10])
        qml.CZ([6, 1])
        qml.CZ([15, 7])
        qml.CNOT([8, 3])
        qml.CNOT([13, 5])
        qml.CNOT([16, 14])
        qml.CZ([11, 4])
        qml.Hadamard([6])
        qml.Hadamard([15])
        qml.Hadamard([8])
        qml.Hadamard([13])
        qml.Hadamard([2])
        qml.Hadamard([16])
        qml.Hadamard([11])
        qml.Hadamard([9])
        measurements["c_(1, 1, 1)_0"] = catalyst_measure(6)
        measurements["c_(2, 2, 1)_0"] = catalyst_measure(15)
        measurements["c_(1, 2, 1)_0"] = catalyst_measure(8)
        measurements["c_(2, 1, 1)_0"] = catalyst_measure(13)
        measurements["c_(0, 1, 1)_0"] = catalyst_measure(2)
        measurements["c_(3, 2, 1)_0"] = catalyst_measure(16)
        measurements["c_(2, 0, 1)_0"] = catalyst_measure(11)
        measurements["c_(1, 3, 1)_0"] = catalyst_measure(9)
        #  End of circuit: measure q1 syndromes - cycle 0
        #  End of circuit: measure q1 syndromes 1 time(s)
        #  End of circuit: inject t into block q1 and measure syndromes
        # Start of circuit: measure q1 syndromes 1 time(s)
        # Start of circuit: measure q1 syndromes - cycle 0
        catalyst_measure(6, reset=True)
        catalyst_measure(15, reset=True)
        catalyst_measure(8, reset=True)
        catalyst_measure(13, reset=True)
        catalyst_measure(2, reset=True)
        catalyst_measure(16, reset=True)
        catalyst_measure(11, reset=True)
        catalyst_measure(9, reset=True)
        qml.Hadamard([6])
        qml.Hadamard([15])
        qml.Hadamard([8])
        qml.Hadamard([13])
        qml.Hadamard([2])
        qml.Hadamard([16])
        qml.Hadamard([11])
        qml.Hadamard([9])
        qml.CZ([6, 4])
        qml.CZ([15, 12])
        qml.CNOT([8, 5])
        qml.CNOT([13, 10])
        qml.CNOT([2, 0])
        qml.CZ([9, 7])
        qml.CZ([6, 0])
        qml.CZ([15, 5])
        qml.CNOT([8, 7])
        qml.CNOT([13, 12])
        qml.CNOT([2, 1])
        qml.CZ([9, 3])
        qml.CZ([6, 5])
        qml.CZ([15, 14])
        qml.CNOT([8, 1])
        qml.CNOT([13, 4])
        qml.CNOT([16, 12])
        qml.CZ([11, 10])
        qml.CZ([6, 1])
        qml.CZ([15, 7])
        qml.CNOT([8, 3])
        qml.CNOT([13, 5])
        qml.CNOT([16, 14])
        qml.CZ([11, 4])
        qml.Hadamard([6])
        qml.Hadamard([15])
        qml.Hadamard([8])
        qml.Hadamard([13])
        qml.Hadamard([2])
        qml.Hadamard([16])
        qml.Hadamard([11])
        qml.Hadamard([9])
        measurements["c_(1, 1, 1)_1"] = catalyst_measure(6)
        measurements["c_(2, 2, 1)_1"] = catalyst_measure(15)
        measurements["c_(1, 2, 1)_1"] = catalyst_measure(8)
        measurements["c_(2, 1, 1)_1"] = catalyst_measure(13)
        measurements["c_(0, 1, 1)_1"] = catalyst_measure(2)
        measurements["c_(3, 2, 1)_1"] = catalyst_measure(16)
        measurements["c_(2, 0, 1)_1"] = catalyst_measure(11)
        measurements["c_(1, 3, 1)_1"] = catalyst_measure(9)
        #  End of circuit: measure q1 syndromes - cycle 0
        #  End of circuit: measure q1 syndromes 1 time(s)
        # Start of circuit: measure logical x of q1
        qml.Hadamard([0])
        qml.Hadamard([1])
        qml.Hadamard([3])
        qml.Hadamard([4])
        qml.Hadamard([5])
        qml.Hadamard([7])
        qml.Hadamard([10])
        qml.Hadamard([12])
        qml.Hadamard([14])
        measurements["c_(0, 0, 0)_0"] = catalyst_measure(0)
        measurements["c_(0, 1, 0)_0"] = catalyst_measure(1)
        measurements["c_(0, 2, 0)_0"] = catalyst_measure(3)
        measurements["c_(1, 0, 0)_0"] = catalyst_measure(4)
        measurements["c_(1, 1, 0)_0"] = catalyst_measure(5)
        measurements["c_(1, 2, 0)_0"] = catalyst_measure(7)
        measurements["c_(2, 0, 0)_0"] = catalyst_measure(10)
        measurements["c_(2, 1, 0)_0"] = catalyst_measure(12)
        measurements["c_(2, 2, 0)_0"] = catalyst_measure(14)
        #  End of circuit: measure logical x of q1
        #  End of circuit: final circuit
        return measurements


else:
    pl_main = None

if not dry_run:
    with with_alarm(timeout or float("inf")):
        pl_main.compile()

times = []
measurements = []

print("Running", end="")
for _ in range(nattempts):
    begin = time.time()
    shot_results = []
    if not dry_run:
        with with_alarm(timeout or float("inf")):
            raw = pl_main()
            per_key_lists = {
                k: v.astype(int).tolist() for k, v in raw.items()
            }
            nshots = (
                len(next(iter(per_key_lists.values())))
                if per_key_lists else 0
            )
            for i in range(nshots):
                shot_results.append({
                    k: vals[i] for k, vals in per_key_lists.items()
                })
    end = time.time()
    times.append(end - begin)
    measurements.append(shot_results)

    with open("running_times.json", "w") as f:
        json.dump(times, f)
    with open("measurements.json", "w") as f:
        json.dump(measurements, f)

    print(f", t={times[-1]}", end="", flush=True)
print(" (dry-run)" if dry_run else "")

if not dry_run:
    dump_logical_observables(measurements)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions