diff --git a/glue/crumble/circuit/circuit.js b/glue/crumble/circuit/circuit.js
index a0fb3d0b2..048b80dd9 100644
--- a/glue/crumble/circuit/circuit.js
+++ b/glue/crumble/circuit/circuit.js
@@ -164,6 +164,7 @@ class Circuit {
replaceAll('#!pragma ERR', 'ERR').
replaceAll('#!pragma MARK', 'MARK').
replaceAll('#!pragma POLYGON', 'POLYGON').
+ replaceAll('#!pragma REVMARK', 'REVMARK').
replaceAll('_', ' ').
replaceAll('Q(', 'QUBIT_COORDS(').
replaceAll('DT', 'DETECTOR').
diff --git a/glue/crumble/circuit/layer.js b/glue/crumble/circuit/layer.js
index 256e48f3f..b6f4e94ab 100644
--- a/glue/crumble/circuit/layer.js
+++ b/glue/crumble/circuit/layer.js
@@ -2,6 +2,8 @@ import {Operation} from "./operation.js"
import {GATE_MAP} from "../gates/gateset.js";
import {groupBy} from "../base/seq.js";
+const MARKER_NAMES = ['MARKX', 'MARKY', 'MARKZ', 'REVMARKX', 'REVMARKY', 'REVMARKZ'];
+
class Layer {
constructor() {
this.id_ops = /** @type {!Map} */ new Map();
@@ -216,9 +218,12 @@ class Layer {
* @param {!int} marker_index
* @returns {!Map}
*/
- id_pauliFrameAfter(before, marker_index) {
+ id_pauliFrameAfter(before, marker_index, reverse=false) {
let after = new Map();
let handled = new Set();
+ const MARKX_NAME = reverse ? 'REVMARKX' : 'MARKX';
+ const MARKY_NAME = reverse ? 'REVMARKY' : 'MARKY';
+ const MARKZ_NAME = reverse ? 'REVMARKZ' : 'MARKZ';
for (let k of before.keys()) {
let v = before.get(k);
@@ -253,7 +258,7 @@ class Layer {
}
for (let op of this.markers) {
- if (op.gate.name === 'MARKX' && op.args[0] === marker_index) {
+ if (op.gate.name === MARKX_NAME && op.args[0] === marker_index) {
let key = op.id_targets[0];
let pauli = after.get(key);
if (pauli === undefined || pauli === 'I') {
@@ -266,7 +271,7 @@ class Layer {
pauli = 'Y';
}
after.set(key, pauli);
- } else if (op.gate.name === 'MARKY' && op.args[0] === marker_index) {
+ } else if (op.gate.name === MARKY_NAME && op.args[0] === marker_index) {
let key = op.id_targets[0];
let pauli = after.get(key);
if (pauli === undefined || pauli === 'I') {
@@ -279,7 +284,7 @@ class Layer {
pauli = 'X';
}
after.set(key, pauli);
- } else if (op.gate.name === 'MARKZ' && op.args[0] === marker_index) {
+ } else if (op.gate.name === MARKZ_NAME && op.args[0] === marker_index) {
let key = op.id_targets[0];
let pauli = after.get(key);
if (pauli === undefined || pauli === 'I') {
@@ -330,7 +335,7 @@ class Layer {
if (index !== undefined && op.args[0] !== index) {
return true;
}
- if (op.gate.name !== 'MARKX' && op.gate.name !== 'MARKY' && op.gate.name !== 'MARKZ') {
+ if (!(MARKER_NAMES.includes(op.gate.name))) {
return true;
}
return op.id_targets[0] !== q;
@@ -343,7 +348,7 @@ class Layer {
*/
put(op, allow_overwrite=true) {
if (op.gate.is_marker) {
- if (op.gate.name === 'MARKX' || op.gate.name === 'MARKY' || op.gate.name === 'MARKZ') {
+ if (MARKER_NAMES.includes(op.gate.name)) {
this.id_dropMarkersAt(op.id_targets[0], op.args[0]);
}
this.markers.push(op);
diff --git a/glue/crumble/circuit/layer.test.js b/glue/crumble/circuit/layer.test.js
index 5c4c3c557..42a70b6e9 100644
--- a/glue/crumble/circuit/layer.test.js
+++ b/glue/crumble/circuit/layer.test.js
@@ -20,13 +20,17 @@ test("layer.put_get", () => {
let marker1 = new Operation(GATE_MAP.get("MARKX"), '', new Float32Array([0]), new Uint32Array([4]));
let marker2 = new Operation(GATE_MAP.get("MARKZ"), '', new Float32Array([1]), new Uint32Array([5]));
+ let marker3 = new Operation(GATE_MAP.get("REVMARKX"), '', new Float32Array([2]), new Uint32Array([4]));
+ let marker4 = new Operation(GATE_MAP.get("REVMARKZ"), '', new Float32Array([3]), new Uint32Array([5]));
layer.put(marker1);
layer.put(marker2);
+ layer.put(marker3);
+ layer.put(marker4);
assertThat(layer.id_ops).isEqualTo(new Map([
[2, op],
[3, op],
]));
- assertThat(layer.markers).isEqualTo([marker1, marker2]);
+ assertThat(layer.markers).isEqualTo([marker1, marker2, marker3, marker4]);
});
test("layer.filtered", () => {
diff --git a/glue/crumble/circuit/pauli_frame.test.js b/glue/crumble/circuit/pauli_frame.test.js
index 16e35aae7..f3988826c 100644
--- a/glue/crumble/circuit/pauli_frame.test.js
+++ b/glue/crumble/circuit/pauli_frame.test.js
@@ -36,6 +36,10 @@ test("pauli_frame.do_gate_vs_old_frame_updates", () => {
if (g.name === 'DETECTOR' || g.name === 'OBSERVABLE_INCLUDE') {
continue;
}
+ if (g.name.startsWith('REVMARK')) {
+ // skipping REVMARKs since they are not supported by legacy implementation
+ continue;
+ }
let before, after, returned;
if (g.num_qubits === 1) {
before = new PauliFrame(4, g.num_qubits);
diff --git a/glue/crumble/circuit/propagated_pauli_frames.js b/glue/crumble/circuit/propagated_pauli_frames.js
index 2a65df42f..356c2a169 100644
--- a/glue/crumble/circuit/propagated_pauli_frames.js
+++ b/glue/crumble/circuit/propagated_pauli_frames.js
@@ -122,14 +122,18 @@ class PropagatedPauliFrames {
* @param {!int} marker_index
* @returns {!PropagatedPauliFrames}
*/
- static fromCircuit(circuit, marker_index) {
+ static fromCircuit(circuit, marker_index, reverse = false) {
let result = new PropagatedPauliFrames(new Map());
let bases = /** @type {!Map} */ new Map();
- for (let k = 0; k < circuit.layers.length; k++) {
+ const start = reverse ? circuit.layers.length - 1 : 0;
+ const end = reverse ? -1 : circuit.layers.length;
+ const step = reverse ? -1 : 1;
+
+ for (let k = start; reverse ? k > end : k < end; k += step) {
let layer = circuit.layers[k];
let prevBases = bases;
- bases = layer.id_pauliFrameAfter(bases, marker_index);
+ bases = layer.id_pauliFrameAfter(bases, marker_index, reverse);
let errors = new Set();
for (let key of [...bases.keys()]) {
@@ -171,7 +175,7 @@ class PropagatedPauliFrames {
}
if (bases.size > 0) {
- result.id_layers.set(k + 0.5, new PropagatedPauliFrameLayer(bases, new Set(), []));
+ result.id_layers.set(reverse ? k - 0.5 : k + 0.5, new PropagatedPauliFrameLayer(bases, new Set(), []));
}
if (errors.size > 0 || crossings.length > 0) {
result.id_layers.set(k, new PropagatedPauliFrameLayer(new Map(), errors, crossings));
diff --git a/glue/crumble/crumble.html b/glue/crumble/crumble.html
index 4dff02ecc..b0b08bc83 100644
--- a/glue/crumble/crumble.html
+++ b/glue/crumble/crumble.html
@@ -33,7 +33,7 @@
-
diff --git a/glue/crumble/draw/main_draw.js b/glue/crumble/draw/main_draw.js
index 4636133e2..b7b6141ec 100644
--- a/glue/crumble/draw/main_draw.js
+++ b/glue/crumble/draw/main_draw.js
@@ -200,12 +200,16 @@ function draw(ctx, snap) {
let circuit = snap.circuit;
let numPropagatedLayers = 0;
+ let reversedMarkers = new Set();
for (let layer of circuit.layers) {
for (let op of layer.markers) {
let gate = op.gate;
if (gate.name === "MARKX" || gate.name === "MARKY" || gate.name === "MARKZ") {
numPropagatedLayers = Math.max(numPropagatedLayers, op.args[0] + 1);
}
+ if (gate.name === "REVMARKX" || gate.name === "REVMARKY" || gate.name === "REVMARKZ") {
+ reversedMarkers.add(op.args[0]);
+ }
}
}
@@ -217,8 +221,13 @@ function draw(ctx, snap) {
};
let propagatedMarkerLayers = /** @type {!Map} */ new Map();
for (let mi = 0; mi < numPropagatedLayers; mi++) {
- propagatedMarkerLayers.set(mi, PropagatedPauliFrames.fromCircuit(circuit, mi));
+ propagatedMarkerLayers.set(mi, PropagatedPauliFrames.fromCircuit(circuit, mi, false));
+ }
+ let propagatedRevMarkerLayers = /** @type {!Map} */ new Map();
+ for (let mi of reversedMarkers) {
+ propagatedRevMarkerLayers.set(mi, PropagatedPauliFrames.fromCircuit(circuit, mi, true));
}
+
let {dets: dets, obs: obs} = circuit.collectDetectorsAndObservables(false);
let batch_input = [];
for (let mi = 0; mi < dets.length; mi++) {
@@ -349,6 +358,7 @@ function draw(ctx, snap) {
});
drawMarkers(ctx, snap, qubitDrawCoords, propagatedMarkerLayers);
+ drawMarkers(ctx, snap, qubitDrawCoords, propagatedRevMarkerLayers);
if (focusX !== undefined) {
ctx.save();
@@ -384,7 +394,7 @@ function draw(ctx, snap) {
});
});
- drawTimeline(ctx, snap, propagatedMarkerLayers, qubitDrawCoords, circuit.layers.length);
+ drawTimeline(ctx, snap, propagatedMarkerLayers, qubitDrawCoords, circuit.layers.length, propagatedRevMarkerLayers);
// Draw scrubber.
ctx.save();
diff --git a/glue/crumble/draw/timeline_viewer.js b/glue/crumble/draw/timeline_viewer.js
index 2bab08cfb..780b3ddc8 100644
--- a/glue/crumble/draw/timeline_viewer.js
+++ b/glue/crumble/draw/timeline_viewer.js
@@ -14,8 +14,12 @@ let TIMELINE_PITCH = 32;
* @param {!int} max_t
* @param {!number} x_pitch
* @param {!Map} hitCounts
+ * @param {!boolean} reversed
*/
-function drawTimelineMarkers(ctx, ds, qubitTimeCoordFunc, propagatedMarkers, mi, min_t, max_t, x_pitch, hitCounts) {
+function drawTimelineMarkers(ctx, ds, qubitTimeCoordFunc, propagatedMarkers, mi, min_t, max_t, x_pitch, hitCounts, reversed) {
+ const colors = {'X': 'red', 'Y': 'green', 'Z': 'blue'};
+ const lightColors = {'X': '#ff7777', 'Y': '#77ff77', 'Z': '#7777ff'};
+
for (let t = min_t - 1; t <= max_t; t++) {
if (!hitCounts.has(t)) {
hitCounts.set(t, new Map());
@@ -45,16 +49,10 @@ function drawTimelineMarkers(ctx, ds, qubitTimeCoordFunc, propagatedMarkers, mi,
if (x === undefined || y === undefined) {
continue;
}
- if (b === 'X') {
- ctx.fillStyle = 'red'
- } else if (b === 'Y') {
- ctx.fillStyle = 'green'
- } else if (b === 'Z') {
- ctx.fillStyle = 'blue'
- } else {
- throw new Error('Not a pauli: ' + b);
- }
+ ctx.fillStyle = reversed ? lightColors[b] : colors[b];
ctx.fillRect(x - dx, y - dy, wx, wy);
+ ctx.shadowColor = ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
+ ctx.filter = "none";
}
for (let q of p0.errors) {
let {dx, dy, wx, wy} = marker_placement(mi, q, hitCount);
@@ -74,15 +72,7 @@ function drawTimelineMarkers(ctx, ds, qubitTimeCoordFunc, propagatedMarkers, mi,
for (let {q1, q2, color} of p0.crossings) {
let [x1, y1] = qubitTimeCoordFunc(q1, t);
let [x2, y2] = qubitTimeCoordFunc(q2, t);
- if (color === 'X') {
- ctx.strokeStyle = 'red';
- } else if (color === 'Y') {
- ctx.strokeStyle = 'green';
- } else if (color === 'Z') {
- ctx.strokeStyle = 'blue';
- } else {
- ctx.strokeStyle = 'purple'
- }
+ ctx.strokeStyle = reversed ? lightColors[color] : colors[color];
ctx.lineWidth = 8;
stroke_connector_to(ctx, x1, y1, x2, y2);
ctx.lineWidth = 1;
@@ -96,8 +86,9 @@ function drawTimelineMarkers(ctx, ds, qubitTimeCoordFunc, propagatedMarkers, mi,
* @param {!Map} propagatedMarkerLayers
* @param {!function(!int): ![!number, !number]} timesliceQubitCoordsFunc
* @param {!int} numLayers
+ * @param {!Set} reversedMarkers
*/
-function drawTimeline(ctx, snap, propagatedMarkerLayers, timesliceQubitCoordsFunc, numLayers) {
+function drawTimeline(ctx, snap, propagatedMarkerLayers, timesliceQubitCoordsFunc, numLayers, propagatedRevMarkerLayers = new Map()) {
let w = Math.floor(ctx.canvas.width / 2);
let qubits = snap.timelineQubits();
@@ -161,10 +152,15 @@ function drawTimeline(ctx, snap, propagatedMarkerLayers, timesliceQubitCoordsFun
// Draw colored indicators showing Pauli propagation.
let hitCounts = new Map();
+ for (let [mi, p] of propagatedRevMarkerLayers.entries()) {
+ drawTimelineMarkers(ctx, snap, qubitTimeCoords, p, mi, min_t_clamp, max_t, x_pitch, hitCounts, true);
+ }
+
for (let [mi, p] of propagatedMarkerLayers.entries()) {
- drawTimelineMarkers(ctx, snap, qubitTimeCoords, p, mi, min_t_clamp, max_t, x_pitch, hitCounts);
+ drawTimelineMarkers(ctx, snap, qubitTimeCoords, p, mi, min_t_clamp, max_t, x_pitch, hitCounts, false);
}
+
// Draw highlight of current layer.
ctx.globalAlpha *= 0.5;
ctx.fillStyle = 'black';
diff --git a/glue/crumble/editor/editor_state.js b/glue/crumble/editor/editor_state.js
index 9feef8227..22b3c1151 100644
--- a/glue/crumble/editor/editor_state.js
+++ b/glue/crumble/editor/editor_state.js
@@ -226,7 +226,7 @@ class EditorState {
clearMarkers() {
let c = this.copyOfCurCircuit();
for (let layer of c.layers) {
- layer.markers = layer.markers.filter(e => e.gate.name !== 'MARKX' && e.gate.name !== 'MARKY' && e.gate.name !== 'MARKZ');
+ layer.markers = layer.markers.filter(e => !e.gate.name.endsWith('MARKX') && !e.gate.name.endsWith('MARKY') && !e.gate.name.endsWith('MARKZ'));
}
this.commit(c);
}
diff --git a/glue/crumble/gates/gateset.test.js b/glue/crumble/gates/gateset.test.js
index ee1919f40..43b67b62a 100644
--- a/glue/crumble/gates/gateset.test.js
+++ b/glue/crumble/gates/gateset.test.js
@@ -26,6 +26,9 @@ test("gateset.expected_gates", () => {
expectedGates.add("MARKY");
expectedGates.add("MARKZ");
expectedGates.add("MARK");
+ expectedGates.add("REVMARKX");
+ expectedGates.add("REVMARKY");
+ expectedGates.add("REVMARKZ");
expectedGates.add("RZ");
expectedGates.add("MZ");
expectedGates.add("MRZ");
diff --git a/glue/crumble/gates/gateset_markers.js b/glue/crumble/gates/gateset_markers.js
index aad80749d..ddcfc533c 100644
--- a/glue/crumble/gates/gateset_markers.js
+++ b/glue/crumble/gates/gateset_markers.js
@@ -180,6 +180,36 @@ function *iter_gates_markers() {
ctx.fillRect(x1 - rad, y1 - rad, rad, rad);
}
);
+ yield new Gate(
+ 'REVMARKX',
+ 1,
+ true,
+ true,
+ undefined,
+ () => {},
+ () => {},
+ make_marker_drawer('#ff7777'),
+ );
+ yield new Gate(
+ 'REVMARKY',
+ 1,
+ true,
+ true,
+ undefined,
+ () => {},
+ () => {},
+ make_marker_drawer('#77ff77'),
+ );
+ yield new Gate(
+ 'REVMARKZ',
+ 1,
+ true,
+ true,
+ undefined,
+ () => {},
+ () => {},
+ make_marker_drawer('#7777ff'),
+ );
}
export {iter_gates_markers, marker_placement};
diff --git a/glue/crumble/keyboard/toolbox.js b/glue/crumble/keyboard/toolbox.js
index cae74d36d..79538d402 100644
--- a/glue/crumble/keyboard/toolbox.js
+++ b/glue/crumble/keyboard/toolbox.js
@@ -6,9 +6,11 @@ let DIAM = 28;
let PITCH = DIAM + 4;
let PAD = 10.5;
-let COLUMNS = ['H', 'S', 'R', 'M', 'MR', 'C', 'W', 'SC', 'MC', 'P', '1-9'];
+let COLUMNS = ['H', 'S', 'R', 'M', 'MR', 'C', 'W', 'SC', 'MC', 'P', '1-9', '⇧1-9'];
let DEF_ROW = [1, 2, 2, 2, 2, 1, 2, 2, 2, -1, -1, -1];
+let COLORMAP = { 'X1': 'red', 'Y1': 'green', 'Z1': 'blue', 'X1N': '#ff7777', 'Y1N': '#77ff77', 'Z1N': '#7777ff' };
+
/**
* @param {!ChordEvent} ev
* @returns {undefined|!{row: !int, strength: !number}}
@@ -92,6 +94,13 @@ function make_pos_to_gate_dict() {
result.set(`${x},-1`, GATE_MAP.get("MARK").withDefaultArgument(k));
x += 1;
}
+ // Add entries for the '⇧1-9' column (column 11)
+ for (let k = 0; k < 4; k++) {
+ result.set(`${x},0`, GATE_MAP.get("REVMARKX").withDefaultArgument(k));
+ result.set(`${x},1`, GATE_MAP.get("REVMARKY").withDefaultArgument(k));
+ result.set(`${x},2`, GATE_MAP.get("REVMARKZ").withDefaultArgument(k));
+ x += 1;
+ }
return result;
}
let POS_TO_GATE_DICT = make_pos_to_gate_dict();
@@ -145,9 +154,9 @@ function drawToolbox(ev) {
ctx.fillStyle = 'white';
ctx.strokeStyle = 'black';
- let xGates = ['H_YZ', 'S_X', 'R_X', 'M_X', 'MR_X', 'C_X', 'CXSWAP', '√XX', 'M_XX', 'PX', 'X1'];
- let yGates = ['H', 'S_Y', 'R_Y', 'M_Y', 'MR_Y', 'C_Y', 'SWAP', '√YY', 'M_YY', 'PY', 'Y1'];
- let zGates = ['H_XY', 'S', 'R', 'M', 'MR', 'C_Z', 'CZSWAP', '√ZZ', 'M_ZZ', 'PZ', 'Z1'];
+ let xGates = ['H_YZ', 'S_X', 'R_X', 'M_X', 'MR_X', 'C_X', 'CXSWAP', '√XX', 'M_XX', 'PX', 'X1', 'X1N'];
+ let yGates = ['H', 'S_Y', 'R_Y', 'M_Y', 'MR_Y', 'C_Y', 'SWAP', '√YY', 'M_YY', 'PY', 'Y1', 'Y1N'];
+ let zGates = ['H_XY', 'S', 'R', 'M', 'MR', 'C_Z', 'CZSWAP', '√ZZ', 'M_ZZ', 'PZ', 'Z1', 'Z1N'];
let gates = [xGates, yGates, zGates];
for (let k = 0; k < COLUMNS.length; k++) {
for (let p = 0; p < 3; p++) {
@@ -193,20 +202,25 @@ function drawToolbox(ev) {
ctx.globalAlpha *= 4;
continue;
}
- if (text.endsWith('1')) {
+ if (text.endsWith('1') || text.endsWith('1N')) {
ctx.beginPath();
ctx.moveTo(cx + PITCH * 0.15, cy - PITCH * 0.25);
ctx.lineTo(cx, cy + PITCH * 0.1);
ctx.lineTo(cx - PITCH * 0.15, cy - PITCH * 0.25);
ctx.closePath();
- let color = text === 'X1' ? 'red' : text === 'Y1' ? 'green' : 'blue';
+ let color = COLORMAP[text] || 'black';
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.fill();
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(cx, cy);
- ctx.lineTo(cx + DIAM * 0.5, cy);
+ if (text.endsWith('1N')) {
+ // Draw a negated arrow (arrow pointing left instead of right)
+ ctx.lineTo(cx - DIAM * 0.5, cy);
+ } else {
+ ctx.lineTo(cx + DIAM * 0.5, cy);
+ }
ctx.stroke();
ctx.lineWidth = 1;
continue;
diff --git a/glue/crumble/main.js b/glue/crumble/main.js
index 1d16f559d..221035e94 100644
--- a/glue/crumble/main.js
+++ b/glue/crumble/main.js
@@ -136,6 +136,7 @@ function exportCurrentState() {
let validStimCircuit = editorState.copyOfCurCircuit().toStimCircuit().
replaceAll('\nPOLYGON', '\n#!pragma POLYGON').
replaceAll('\nERR', '\n#!pragma ERR').
+ replaceAll('\nREVMARK', '\n#!pragma REVMARK').
replaceAll('\nMARK', '\n#!pragma MARK');
let txt = txtStimCircuit;
txt.value = validStimCircuit + '\n';
@@ -225,6 +226,12 @@ function makeChordHandlers() {
});
res.set(' ', preview => editorState.unmarkFocusInferBasis(preview));
+ const shiftedKeyMap = {
+ '1': '!', '2': '@', '3': '#', '4': '$', '5': '%',
+ '6': '^', '7': '&', '8': '*', '9': '(', '0': ')',
+ '-': '_', '=': '+', '\\': '|', '`': '~'
+ };
+
for (let [key, val] of [
['1', 0],
['2', 1],
@@ -242,6 +249,9 @@ function makeChordHandlers() {
['`', 13],
]) {
res.set(`${key}`, preview => editorState.markFocusInferBasis(preview, val));
+ res.set(`shift+${shiftedKeyMap[key]}+x`, preview => editorState.writeGateToFocus(preview, GATE_MAP.get('REVMARKX').withDefaultArgument(val)));
+ res.set(`shift+${shiftedKeyMap[key]}+y`, preview => editorState.writeGateToFocus(preview, GATE_MAP.get('REVMARKY').withDefaultArgument(val)));
+ res.set(`shift+${shiftedKeyMap[key]}+z`, preview => editorState.writeGateToFocus(preview, GATE_MAP.get('REVMARKZ').withDefaultArgument(val)));
res.set(`${key}+x`, preview => editorState.writeGateToFocus(preview, GATE_MAP.get('MARKX').withDefaultArgument(val)));
res.set(`${key}+y`, preview => editorState.writeGateToFocus(preview, GATE_MAP.get('MARKY').withDefaultArgument(val)));
res.set(`${key}+z`, preview => editorState.writeGateToFocus(preview, GATE_MAP.get('MARKZ').withDefaultArgument(val)));