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
23 changes: 19 additions & 4 deletions glue/crumble/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ Crumble is not polished.**

## Index

- [Crumble](#crumble)
- [Index](#index)
- [Accessing Crumble](#accessing-crumble)
- [Using Crumble](#using-crumble)
- [Loading and Saving Circuits](#loading-saving)
- [Keyboard Controls](#keyboard-commands)
- [Mouse Controls](#mouse-commands)
- [Loading and Saving Circuits](#loading-and-saving-circuits)
- [Keyboard Controls](#keyboard-controls)
- [Mouse Controls](#mouse-controls)
- [Building Crumble](#building-crumble)
- [Testing Crumble](#testing-crumble)

Expand Down Expand Up @@ -222,8 +224,15 @@ of the **two qubit variant** (c)
of the **square root** (s)
of the **Y gate** (y) (i.e. the gate `SQRT_YY_DAG 1 2`).

**Interaction**

- `Arrow Keys (Up/Down/Left/Right)`: Pan the Planar Layout view
- `ctrl+0`: Reset View (reset both the planar view and the timeline scroll)
- `ctrl+-`: Zoom out
- `ctrl++` or `ctrl+=`: Zoom in

<a name="mouse-commands"></a>
### Mouse Controls
## Mouse Controls

Note: to `BoxSelect` means to press down the left mouse button, drag the mouse
while holding the button down to outline a rectangular region, and then release
Expand All @@ -245,6 +254,12 @@ box selection action. The specific parity being used depends on context. For
example, when selecting a column of qubits, the row parity is used. When
selecting a 2d region, the subgrid parity is used.

- `shift+MMB` (Middle Mouse Button):
- Drag on the left (planar layout) to pan the planar layout view
- Drag on the right to scroll vertically through the wires
- `ctrl+MMB`:
- Drag vertically on the left (planar layout) to zoom in/out

<a name="building-crumble"></a>
# Building Crumble

Expand Down
5 changes: 5 additions & 0 deletions glue/crumble/crumble.html
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@
<button id="btnDeleteLayer">Delete Layer (ctrl+delete)</button>
</div>
</div>
<div style="display: inline-block">
<div>
<button id="btnResetView">Reset View (ctrl+0)</button>
</div>
</div>
<textarea id="txtDefaultCircuit" style="display: none">[[[DEFAULT_CIRCUIT_CONTENT_LITERAL]]]</textarea>
</div>
<div id="divImportExport" style="display: none">
Expand Down
4 changes: 2 additions & 2 deletions glue/crumble/draw/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ const pitch = 50;
const rad = 10;
const OFFSET_X = -pitch + Math.floor(pitch / 4) + 0.5;
const OFFSET_Y = -pitch + Math.floor(pitch / 4) + 0.5;

export {pitch, rad, OFFSET_X, OFFSET_Y};
const step = pitch / 2;
export {pitch, rad, OFFSET_X, OFFSET_Y, step};
81 changes: 55 additions & 26 deletions glue/crumble/draw/main_draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ import {beginPathPolygon} from './draw_util.js';
* @param {!number|undefined} y
* @return {![undefined, undefined]|![!number, !number]}
*/
function xyToPos(x, y) {
function xyToPos(x, y, scrollX = 0, scrollY = 0, zoomScale = 1.0) {
if (x === undefined || y === undefined) {
return [undefined, undefined];
}
let focusX = x / pitch;
let focusY = y / pitch;
let focusX = (x - scrollX) / (pitch * zoomScale);
let focusY = (y - scrollY) / (pitch * zoomScale);
let roundedX = Math.floor(focusX * 2 + 0.5) / 2;
let roundedY = Math.floor(focusY * 2 + 0.5) / 2;
let centerX = roundedX*pitch;
let centerY = roundedY*pitch;
if (Math.abs(centerX - x) <= rad && Math.abs(centerY - y) <= rad && roundedX % 1 === roundedY % 1) {
let scaledRad = rad * zoomScale;
if (Math.abs(centerX - x) <= scaledRad && Math.abs(centerY - y) <= scaledRad && roundedX % 1 === roundedY % 1) {
return [roundedX, roundedY];
}
return [undefined, undefined];
Expand Down Expand Up @@ -198,6 +199,7 @@ function defensiveDraw(ctx, body) {
*/
function draw(ctx, snap) {
let circuit = snap.circuit;
let zoom = snap.zoomScale || 1.0;

let numPropagatedLayers = 0;
for (let layer of circuit.layers) {
Expand All @@ -209,12 +211,17 @@ function draw(ctx, snap) {
}
}

let c2dCoordTransform = (x, y) => [x*pitch - OFFSET_X, y*pitch - OFFSET_Y];
let c2dCoordTransform = (x, y) => [x * pitch * snap.zoomScale - OFFSET_X + snap.scrollX, y * pitch * snap.zoomScale - OFFSET_Y + snap.scrollY];
let qubitDrawCoords = q => {
let x = circuit.qubitCoordData[2 * q];
let y = circuit.qubitCoordData[2 * q + 1];
return c2dCoordTransform(x, y);
};
let logicCoords = q => {
let x = circuit.qubitCoordData[2 * q];
let y = circuit.qubitCoordData[2 * q + 1];
return [x * pitch, y * pitch];
}
let propagatedMarkerLayers = /** @type {!Map<!int, !PropagatedPauliFrames>} */ new Map();
for (let mi = 0; mi < numPropagatedLayers; mi++) {
propagatedMarkerLayers.set(mi, PropagatedPauliFrames.fromCircuit(circuit, mi));
Expand Down Expand Up @@ -253,10 +260,12 @@ function draw(ctx, snap) {
}
}

let scaledRad = rad * snap.zoomScale;

defensiveDraw(ctx, () => {
ctx.fillStyle = 'white';
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
let [focusX, focusY] = xyToPos(snap.curMouseX, snap.curMouseY);
let [focusX, focusY] = xyToPos(snap.curMouseX, snap.curMouseY, snap.scrollX, snap.scrollY);

// Draw the background polygons.
let lastPolygonLayer = snap.curLayer;
Expand All @@ -268,35 +277,47 @@ function draw(ctx, snap) {
}
}
}



ctx.save();
ctx.translate(- OFFSET_X + snap.scrollX, - OFFSET_Y + snap.scrollY);
ctx.scale(zoom, zoom);

let polygonMarkers = [...circuit.layers[lastPolygonLayer].markers];
polygonMarkers.sort((a, b) => b.id_targets.length - a.id_targets.length);
for (let op of polygonMarkers) {
if (op.gate.name === 'POLYGON') {
op.id_draw(qubitDrawCoords, ctx);
op.id_draw(logicCoords, ctx);
}
}
ctx.restore();

// Draw the grid of qubits.
defensiveDraw(ctx, () => {
for (let qx = 0; qx < 100; qx += 0.5) {
let qStep = 0.5;
if (snap.zoomScale < 0.2) {
qStep = 5.0;
} else if (snap.zoomScale < 0.5) {
qStep = 2.0;
} else if (snap.zoomScale < 0.8) {
qStep = 1.0;
}
for (let qx = 0; qx < 100; qx += qStep) {
let [x, _] = c2dCoordTransform(qx, 0);
let s = `${qx}`;
ctx.fillStyle = 'black';
ctx.fillText(s, x - ctx.measureText(s).width / 2, 15);
}
for (let qy = 0; qy < 100; qy += 0.5) {
for (let qy = 0; qy < 100; qy += qStep) {
let [_, y] = c2dCoordTransform(0, qy);
let s = `${qy}`;
ctx.fillStyle = 'black';
ctx.fillText(s, 18 - ctx.measureText(s).width, y);
}

ctx.strokeStyle = 'black';
for (let qx = 0; qx < 100; qx += 0.5) {
let [x, _] = c2dCoordTransform(qx, 0);
let s = `${qx}`;
ctx.fillStyle = 'black';
ctx.fillText(s, x - ctx.measureText(s).width / 2, 15);
for (let qy = qx % 1; qy < 100; qy += 1) {
let [x, y] = c2dCoordTransform(qx, qy);
ctx.fillStyle = 'white';
Expand All @@ -308,8 +329,8 @@ function draw(ctx, snap) {
if (isVeryUnused) {
ctx.globalAlpha *= 0.25;
}
ctx.fillRect(x - rad, y - rad, 2*rad, 2*rad);
ctx.strokeRect(x - rad, y - rad, 2*rad, 2*rad);
ctx.fillRect(x - scaledRad, y - scaledRad, 2 * scaledRad, 2 * scaledRad);
ctx.strokeRect(x - scaledRad, y - scaledRad, 2 * scaledRad, 2 * scaledRad);
if (isUnused) {
ctx.globalAlpha *= 4;
}
Expand All @@ -320,42 +341,48 @@ function draw(ctx, snap) {
}
});

ctx.save();
ctx.translate(- OFFSET_X + snap.scrollX, - OFFSET_Y + snap.scrollY);
ctx.scale(zoom, zoom);

for (let [mi, p] of propagatedMarkerLayers.entries()) {
drawCrossMarkers(ctx, snap, qubitDrawCoords, p, mi);
drawCrossMarkers(ctx, snap, logicCoords, p, mi);
}

for (let op of circuit.layers[snap.curLayer].iter_gates_and_markers()) {
if (op.gate.name !== 'POLYGON') {
op.id_draw(qubitDrawCoords, ctx);
op.id_draw(logicCoords, ctx);
}
}

defensiveDraw(ctx, () => {
ctx.globalAlpha *= 0.25
for (let [qx, qy] of snap.timelineSet.values()) {
let [x, y] = c2dCoordTransform(qx, qy);
ctx.fillStyle = 'yellow';
ctx.fillRect(x - rad * 1.25, y - rad * 1.25, 2.5*rad, 2.5*rad);
ctx.fillRect(x - scaledRad * 1.25, y - scaledRad * 1.25, 2.5 * scaledRad, 2.5 * scaledRad);
}
});

drawMarkers(ctx, snap, logicCoords, propagatedMarkerLayers);

ctx.restore();

defensiveDraw(ctx, () => {
ctx.globalAlpha *= 0.5
for (let [qx, qy] of snap.focusedSet.values()) {
let [x, y] = c2dCoordTransform(qx, qy);
ctx.fillStyle = 'blue';
ctx.fillRect(x - rad * 1.25, y - rad * 1.25, 2.5*rad, 2.5*rad);
ctx.fillRect(x - scaledRad * 1.25, y - scaledRad * 1.25, 2.5 * scaledRad, 2.5 * scaledRad);
}
});

drawMarkers(ctx, snap, qubitDrawCoords, propagatedMarkerLayers);

if (focusX !== undefined) {
ctx.save();
ctx.globalAlpha *= 0.5;
let [x, y] = c2dCoordTransform(focusX, focusY);
ctx.fillStyle = 'red';
ctx.fillRect(x - rad, y - rad, 2*rad, 2*rad);
ctx.fillRect(x - scaledRad, y - scaledRad, 2 * scaledRad, 2 * scaledRad);
ctx.restore();
}

Expand All @@ -379,11 +406,13 @@ function draw(ctx, snap) {
}
for (let [qx, qy] of snap.boxHighlightPreview) {
let [x, y] = c2dCoordTransform(qx, qy);
ctx.fillRect(x - rad, y - rad, rad*2, rad*2);
ctx.fillRect(x - scaledRad, y - scaledRad, 2 * scaledRad, 2 * scaledRad);
}
});
});

ctx.font = `10px sans-serif`;
});

drawTimeline(ctx, snap, propagatedMarkerLayers, qubitDrawCoords, circuit.layers.length);

// Draw scrubber.
Expand Down
6 changes: 5 additions & 1 deletion glue/crumble/draw/state_snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class StateSnapshot {
* @param {!number} mouseDownY
* @param {!Array<![!number, !number]>} boxHighlightPreview
*/
constructor(circuit, curLayer, focusedSet, timelineSet, curMouseX, curMouseY, mouseDownX, mouseDownY, boxHighlightPreview) {
constructor(circuit, curLayer, focusedSet, timelineSet, curMouseX, curMouseY, mouseDownX, mouseDownY, scrollX, scrollY, timelineScrollY, zoomScale, boxHighlightPreview) {
this.circuit = circuit.copy();
this.curLayer = curLayer;
this.focusedSet = new Map(focusedSet.entries());
Expand All @@ -27,6 +27,10 @@ class StateSnapshot {
this.curMouseY = curMouseY;
this.mouseDownX = mouseDownX;
this.mouseDownY = mouseDownY;
this.scrollX = scrollX || 0;
this.scrollY = scrollY || 0;
this.timelineScrollY = timelineScrollY || 0;
this.zoomScale = zoomScale || 1.0;
this.boxHighlightPreview = [...boxHighlightPreview];

while (this.circuit.layers.length <= this.curLayer) {
Expand Down
2 changes: 1 addition & 1 deletion glue/crumble/draw/timeline_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ function drawTimeline(ctx, snap, propagatedMarkerLayers, timesliceQubitCoordsFun
cur_x += rad * 0.25;
cur_run++;
}
base_y2xy.set(`${x},${y}`, [Math.round(cur_x) + 0.5, Math.round(cur_y) + 0.5]);
base_y2xy.set(`${x},${y}`, [Math.round(cur_x) + 0.5, Math.round(cur_y + snap.timelineScrollY) + 0.5]);
}

let x_pitch = TIMELINE_PITCH + Math.ceil(rad*max_run*0.25);
Expand Down
Loading