qml.ftqc.get_byproduct_corrections¶
- get_byproduct_corrections(tape, mid_meas, measurement_vals)[source]¶
Correct sample results offline based on the executed quantum script and the mid-circuit measurement results for each shot. The mid measurement results are first parsed with the quantum script to get the byproduct operations for each Clifford and non-Clifford gates. Note that byproduct operations are stored as a list and accessed in a stack manner. The calculation iteratively pops out the first operation in the tape and applies commutation rules for the first byproduct ops in the byproduct stack and then the results are commutated to the byproduct of the current operations in the tape if it is a Clifford gate. The calculation starts from applying commutate rules for
qml.I
gate or \(encode\_xz(x,z)=(0,0)\) to the first gate in the tape. The measurement corrections are returned based on the observable operators and the xz recorded.- Parameters:
(tape (tape) – qml.tape.QuantumScript): A Clifford quantum tape with
X
,Y
,Z
,I
,H
,S
,CNOT
and non-Clifford gates (RZ
andRotXZX
) at the beginning of circuit in the standard circuit formalism. Note that one non-Clifford gate per wire at most is supported.mid_meas (list) – MidMeasurement results per shot.
measurement_vals (list) – Raw measurement results.
- Returns:
A list of corrected measurement results.
Note This work is to be integrated into the MBQC transform pipeline.
Example:
import pennylane as qml from pennylane.ftqc import diagonalize_mcms, generate_lattice, measure_x, measure_y from pennylane.ftqc import GraphStatePrep from pennylane.ftqc.pauli_tracker import get_byproduct_corrections import numpy as np def generate_random_state(n): seed_value = 42 # You can use any integer as the seed np.random.seed(seed_value) input_state = np.random.random(2**n) + 1j * np.random.random(2**n) return input_state / np.linalg.norm(input_state) def generate_rot_gate_graph(): lattice = generate_lattice([4], "chain") return lattice.graph num_shots = 1000 dev = qml.device("lightning.qubit", shots=num_shots) @diagonalize_mcms @qml.qnode(dev, mcm_method="one-shot") def circ(start_state): qml.StatePrep(start_state, wires=[0]) GraphStatePrep(generate_rot_gate_graph(), wires=[1, 2, 3, 4]) qml.CZ(wires=[0, 1]) m0 = measure_x(0, reset=True) m1 = measure_y(1, reset=True) m2 = measure_y(2, reset=True) m3 = measure_y(3, reset=True) GraphStatePrep(generate_rot_gate_graph(), wires=[3, 2, 1, 0]) qml.CZ(wires=[3, 4]) m4 = measure_x(4, reset=True) m5 = measure_y(3, reset=True) m6 = measure_y(2, reset=True) m7 = measure_y(1, reset=True) GraphStatePrep(generate_rot_gate_graph(), wires=[1, 2, 3, 4]) qml.CZ(wires=[0, 1]) m8 = measure_x(0, reset=True) m9 = measure_y(1, reset=True) m10 = measure_y(2, reset=True) m11 = measure_y(3, reset=True) return ( qml.sample(wires=[4]), qml.sample(m0), qml.sample(m1), qml.sample(m2), qml.sample(m3), qml.sample(m4), qml.sample(m5), qml.sample(m6), qml.sample(m7), qml.sample(m8), qml.sample(m9), qml.sample(m10), qml.sample(m11) ) init_state = generate_random_state(1) res = circ(init_state) ops = [qml.H(wires=[0]), qml.H(wires=[0]), qml.H(wires=[0])] measurements = [qml.sample(qml.Z(0))] meas_res = res[0] mid_meas_res = res[1:] corrected_meas_res = [] script = qml.tape.QuantumScript(ops, measurements, shots=num_shots) for i in range(num_shots): mid_meas = [row[i] for row in mid_meas_res] corrected_meas_res.extend(get_byproduct_corrections(script, mid_meas, [meas_res[i]])) res_corrected = 1 - 2*np.sum(corrected_meas_res) / num_shots dev_ref = qml.device("default.qubit") @diagonalize_mcms @qml.qnode(dev) def circ_ref(start_state): qml.StatePrep(start_state, wires=[0]) qml.H(0) qml.H(0) qml.H(0) return qml.expval(qml.Z(0)) np.allclose(res_corrected, circ_ref(init_state))