Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 53 additions & 8 deletions glue/crumble/circuit/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import {Operation} from "./operation.js"
import {GATE_MAP} from "../gates/gateset.js";
import {groupBy} from "../base/seq.js";

/**
* @type {!Map<!string, !string>}
*/
const DEMOLITION_MEASURE_BASIS = new Map([
['MR', 'Z'],
['MRX', 'X'],
['MRY', 'Y'],
]);

class Layer {
constructor() {
this.id_ops = /** @type {!Map<!int, !Operation>} */ new Map();
Expand Down Expand Up @@ -220,6 +229,16 @@ class Layer {
let after = new Map();
let handled = new Set();

// Record qubits involved in demolition measurements (measure + reset)
let demolitionResetQubits = new Set();
for (let op of this.id_ops.values()) {
if (DEMOLITION_MEASURE_BASIS.has(op.gate.name)) {
for (let q of op.id_targets) {
demolitionResetQubits.add(q);
}
}
}

for (let k of before.keys()) {
let v = before.get(k);
let op = this.id_ops.get(k);
Expand All @@ -237,21 +256,35 @@ class Layer {
}
b += r;
}
let a = op.pauliFrameAfter(b);
let hasErr = a.startsWith('ERR:');
for (let qi = 0; qi < op.id_targets.length; qi++) {
let q = op.id_targets[qi];
if (hasErr) {
after.set(q, 'ERR:' + a[4 + qi]);
} else {
after.set(q, a[qi]);
let demolitionBasis = DEMOLITION_MEASURE_BASIS.get(op.gate.name);
if (demolitionBasis !== undefined) {
// Only measure now. Defer reset until after markers are handled
let q = op.id_targets[0];
let c = b[0];
after.set(q, (c === 'I' || c === demolitionBasis) ? c : 'ERR:' + c);
} else {
let a = op.pauliFrameAfter(b);
let hasErr = a.startsWith('ERR:');
for (let qi = 0; qi < op.id_targets.length; qi++) {
let q = op.id_targets[qi];
if (hasErr) {
after.set(q, 'ERR:' + a[4 + qi]);
} else {
after.set(q, a[qi]);
}
}
}
} else {
after.set(k, v);
}
}

// Snapshot operators before reset to distinguish incoming from outgoing markers later
let demolitionIncoming = new Map();
for (let q of demolitionResetQubits) {
demolitionIncoming.set(q, after.get(q));
}

for (let op of this.markers) {
if (op.gate.name === 'MARKX' && op.args[0] === marker_index) {
let key = op.id_targets[0];
Expand Down Expand Up @@ -295,6 +328,18 @@ class Layer {
}
}

// Apply deferred resets
for (let q of demolitionResetQubits) {
let incoming = demolitionIncoming.get(q);
if (incoming === undefined || incoming === 'I') {
// Nothing at reset: a marker here denotes an outgoing operator
continue;
}
// Operator at reset: clean if a marker captured it (now I), else lost (ERR)
let post = after.get(q);
after.set(q, (post === undefined || post === 'I') ? 'I' : 'ERR:I');
}

return after;
}

Expand Down
151 changes: 151 additions & 0 deletions glue/crumble/circuit/propagated_pauli_frames.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,154 @@ test("propagated_pauli_frames.fromMeasurements", () => {
)],
])));
});

test("propagated_pauli_frames.fromCircuit_demolitionMeasurementMarkers", () => {
// Operator processing for demolition measurements (MR/MRX/MRY)

// Uncaptured: the Z reaches the MR's reset and is reported as lost at that layer.
let propagated = PropagatedPauliFrames.fromCircuit(Circuit.fromStimCircuit(`
R 0
MARKZ(0) 0
TICK
MR 0
TICK
M 0
`), 0);
assertThat(propagated).isEqualTo(new PropagatedPauliFrames(new Map([
[0.5, new PropagatedPauliFrameLayer(
new Map([[0, 'Z']]),
new Set(),
[],
)],
[1, new PropagatedPauliFrameLayer(
new Map(),
new Set([0]),
[],
)],
])));

// Captured: a co-located MARKZ (measured basis) captures the incoming Z, so it ends cleanly.
propagated = PropagatedPauliFrames.fromCircuit(Circuit.fromStimCircuit(`
R 0
MARKZ(0) 0
TICK
MR 0
MARKZ(0) 0
TICK
M 0
`), 0);
assertThat(propagated).isEqualTo(new PropagatedPauliFrames(new Map([
[0.5, new PropagatedPauliFrameLayer(
new Map([[0, 'Z']]),
new Set(),
[],
)],
])));

// Outgoing: with nothing flowing in, a MARKZ at the MR introduces a Z on the reset's output,
// which propagates forward (here it passes through the following M).
propagated = PropagatedPauliFrames.fromCircuit(Circuit.fromStimCircuit(`
MR 0
MARKZ(0) 0
TICK
M 0
`), 0);
assertThat(propagated).isEqualTo(new PropagatedPauliFrames(new Map([
[0.5, new PropagatedPauliFrameLayer(
new Map([[0, 'Z']]),
new Set(),
[],
)],
[1.5, new PropagatedPauliFrameLayer(
new Map([[0, 'Z']]),
new Set(),
[],
)],
])));

// The measured basis is per gate: MRX captures an X.
propagated = PropagatedPauliFrames.fromCircuit(Circuit.fromStimCircuit(`
RX 0
MARKX(0) 0
TICK
MRX 0
MARKX(0) 0
TICK
MX 0
`), 0);
assertThat(propagated).isEqualTo(new PropagatedPauliFrames(new Map([
[0.5, new PropagatedPauliFrameLayer(
new Map([[0, 'X']]),
new Set(),
[],
)],
])));

// MRY reports an uncaptured Y.
propagated = PropagatedPauliFrames.fromCircuit(Circuit.fromStimCircuit(`
RY 0
MARKY(0) 0
TICK
MRY 0
TICK
MY 0
`), 0);
assertThat(propagated).isEqualTo(new PropagatedPauliFrames(new Map([
[0.5, new PropagatedPauliFrameLayer(
new Map([[0, 'Y']]),
new Set(),
[],
)],
[1, new PropagatedPauliFrameLayer(
new Map(),
new Set([0]),
[],
)],
])));

// A marker in the wrong basis does not capture the incoming operator: it is still reported.
propagated = PropagatedPauliFrames.fromCircuit(Circuit.fromStimCircuit(`
R 0
MARKZ(0) 0
TICK
MR 0
MARKX(0) 0
TICK
M 0
`), 0);
assertThat(propagated).isEqualTo(new PropagatedPauliFrames(new Map([
[0.5, new PropagatedPauliFrameLayer(
new Map([[0, 'Z']]),
new Set(),
[],
)],
[1, new PropagatedPauliFrameLayer(
new Map(),
new Set([0]),
[],
)],
])));

// An operator that anticommutes with the measured basis (X into an MR) cannot be captured and
// is reported at the measurement.
propagated = PropagatedPauliFrames.fromCircuit(Circuit.fromStimCircuit(`
RX 0
MARKX(0) 0
TICK
MR 0
TICK
M 0
`), 0);
assertThat(propagated).isEqualTo(new PropagatedPauliFrames(new Map([
[0.5, new PropagatedPauliFrameLayer(
new Map([[0, 'X']]),
new Set(),
[],
)],
[1, new PropagatedPauliFrameLayer(
new Map(),
new Set([0]),
[],
)],
])));
});