diff --git a/package.json b/package.json index 8dd81622..a20dd0e4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "title": "Quirk", "description": "A drag-and-drop toy for exploring and understanding small quantum circuits.", "license": "Apache-2.0", - "version": "1.9.0", + "version": "1.9.1", "homepage": "https://github.com/Strilanc/Quirk", "bugs": { "url": "https://github.com/Strilanc/Quirk/issues" diff --git a/src/circuit/Serializer.js b/src/circuit/Serializer.js index 3c2ebbf6..3f0925d3 100644 --- a/src/circuit/Serializer.js +++ b/src/circuit/Serializer.js @@ -15,9 +15,19 @@ import {MysteryGateSymbol, MysteryGateMakerWithMatrix} from "src/gates/Joke_Myst import {seq} from "src/base/Seq.js" import {circuitDefinitionToGate} from "src/circuit/CircuitComputeUtil.js" +/** @type {!function(!GateDrawParams)} */ let matrixDrawer = undefined; +/** @type {!function(!GateDrawParams)} */ let circuitDrawer = undefined; -function initSerializer(gateMatrixDrawer, gateCircuitDrawer) { +/** @type {!function(!GateDrawParams)} */ +let labelDrawer = undefined; +/** + * @param {!function(!GateDrawParams)} gateLabelDrawer + * @param {!function(!GateDrawParams)} gateMatrixDrawer + * @param {!function(!GateDrawParams)} gateCircuitDrawer + */ +function initSerializer(gateLabelDrawer, gateMatrixDrawer, gateCircuitDrawer) { + labelDrawer = gateLabelDrawer; matrixDrawer = gateMatrixDrawer; circuitDrawer = gateCircuitDrawer; } @@ -193,9 +203,12 @@ let fromJson_Gate_Matrix = props => { let height = Math.round(Math.log2(mat.height())); let width = props.symbol === '' ? height : 1; + let matrix = _parseGateMatrix(props.matrix); - return Gate.fromKnownMatrix(props.symbol, _parseGateMatrix(props.matrix), props.name, ''). - withCustomDrawer(props.symbol === "" ? matrixDrawer : undefined). + return Gate.fromKnownMatrix(props.symbol, matrix, props.name, ''). + withCustomDrawer(props.symbol === "" ? matrixDrawer + : matrix.isIdentity() ? labelDrawer + : undefined). withSerializedId(props.id). withHeight(height). withWidth(width); diff --git a/src/draw/GatePainting.js b/src/draw/GatePainting.js index 6557b391..ab2d82b1 100644 --- a/src/draw/GatePainting.js +++ b/src/draw/GatePainting.js @@ -28,6 +28,21 @@ GatePainting.paintBackground = args.painter.fillRect(args.rect, backColor); }; +/** + * @param {!GateDrawParams} args + */ +GatePainting.LABEL_DRAWER = args => { + if (args.positionInCircuit === undefined || args.isHighlighted) { + GatePainting.DEFAULT_DRAWER(args); + return; + } + + let cut = Math.max(0, args.rect.h - Config.GATE_RADIUS*2)/2; + args.painter.fillRect(args.rect.skipTop(cut).skipBottom(cut), Config.GATE_FILL_COLOR); + + GatePainting.paintGateSymbol(args); +}; + /** * @param {!string=} toolboxFillColor * @param {!string=} normalFillColor diff --git a/src/gates/CountingGates.js b/src/gates/CountingGates.js index 972ecf7c..fe04b3bf 100644 --- a/src/gates/CountingGates.js +++ b/src/gates/CountingGates.js @@ -10,12 +10,15 @@ import {makeCycleBitsMatrix, cycleBits} from "src/gates/CycleBitsGates.js" let CountingGates = {}; const staircaseCurve = steps => { + steps = Math.min(128, steps); let curve = []; for (let i = 0; i < steps; i++) { let x = i/steps; let y = i/(steps-1); - curve.push(new Point(x, y)); - curve.push(new Point(x + 1/steps, y)); + if (steps < 128) { + curve.push(new Point(x, y)); + } + curve.push(new Point(x + 1 / steps, y)); } return curve; }; @@ -83,7 +86,7 @@ CountingGates.QuarterPhaseClockPulseGate = Gate.fromVaryingMatrix( withCustomDrawer(STAIRCASE_DRAWER(0.75, 2)). withStableDuration(0.25); -CountingGates.CountingFamily = Gate.generateFamily(1, 8, span => Gate.withoutKnownMatrix( +CountingGates.CountingFamily = Gate.generateFamily(1, 16, span => Gate.withoutKnownMatrix( "+⌈t⌉", "Counting Gate", "Adds an increasing little-endian count into a block of qubits."). @@ -96,7 +99,7 @@ CountingGates.CountingFamily = Gate.generateFamily(1, 8, span => Gate.withoutKno withStableDuration(1.0 / (1< incrementShaderFunc(ctx, span, Math.floor(ctx.time*(1< Gate.withoutKnownMatrix( +CountingGates.UncountingFamily = Gate.generateFamily(1, 16, span => Gate.withoutKnownMatrix( "-⌈t⌉", "Down Counting Gate", "Subtracts an increasing little-endian count from a block of qubits."). diff --git a/src/gates/MultiplyAccumulateGates.js b/src/gates/MultiplyAccumulateGates.js index ddb9493a..def12f0c 100644 --- a/src/gates/MultiplyAccumulateGates.js +++ b/src/gates/MultiplyAccumulateGates.js @@ -150,11 +150,55 @@ MultiplyAccumulateGates.MultiplySubtractInputsFamily = Gate.generateFamily(1, 16 -1) })); +MultiplyAccumulateGates.SquareAddInputFamily = Gate.generateFamily(1, 16, span => Gate.withoutKnownMatrix( + "+=A^2", + "Square-Add Gate [Input A]", + "Adds the square of input A into the qubits covered by this gate."). + markedAsOnlyPermutingAndPhasing(). + markedAsStable(). + withSerializedId("+=AA" + span). + withHeight(span). + withRequiredContextKeys('Input Range A'). + withCustomShader(ctx => { + let {offset: inputOffsetA, length: inputLengthA} = ctx.customContextFromGates.get('Input Range A'); + return multiplyAccumulate( + ctx, + span, + inputOffsetA, + inputLengthA, + inputOffsetA, + inputLengthA, + +1) + })); + +MultiplyAccumulateGates.SquareSubtractInputFamily = Gate.generateFamily(1, 16, span => Gate.withoutKnownMatrix( + "-=A^2", + "Square-Subtract Gate [Input A]", + "Subtracts the square of input A out of the qubits covered by this gate."). + markedAsOnlyPermutingAndPhasing(). + markedAsStable(). + withSerializedId("-=AA" + span). + withHeight(span). + withRequiredContextKeys('Input Range A'). + withCustomShader(ctx => { + let {offset: inputOffsetA, length: inputLengthA} = ctx.customContextFromGates.get('Input Range A'); + return multiplyAccumulate( + ctx, + span, + inputOffsetA, + inputLengthA, + inputOffsetA, + inputLengthA, + -1) + })); + MultiplyAccumulateGates.all = [ ...MultiplyAccumulateGates.MultiplyAddFamily.all, ...MultiplyAccumulateGates.MultiplySubtractFamily.all, ...MultiplyAccumulateGates.MultiplyAddInputsFamily.all, - ...MultiplyAccumulateGates.MultiplySubtractInputsFamily.all + ...MultiplyAccumulateGates.MultiplySubtractInputsFamily.all, + ...MultiplyAccumulateGates.SquareAddInputFamily.all, + ...MultiplyAccumulateGates.SquareSubtractInputFamily.all, ]; export {MultiplyAccumulateGates} diff --git a/src/main.js b/src/main.js index 087833e3..c492f8d8 100644 --- a/src/main.js +++ b/src/main.js @@ -27,7 +27,7 @@ import {initTitleSync} from "src/ui/title.js" import {simulate} from "src/ui/sim.js" import {GatePainting} from "src/draw/GatePainting.js" import {GATE_CIRCUIT_DRAWER} from "src/ui/DisplayedCircuit.js" -initSerializer(GatePainting.MATRIX_DRAWER, GATE_CIRCUIT_DRAWER); +initSerializer(GatePainting.LABEL_DRAWER, GatePainting.MATRIX_DRAWER, GATE_CIRCUIT_DRAWER); const canvasDiv = document.getElementById("canvasDiv"); diff --git a/src/ui/DisplayedCircuit.js b/src/ui/DisplayedCircuit.js index 1394c513..0868c799 100644 --- a/src/ui/DisplayedCircuit.js +++ b/src/ui/DisplayedCircuit.js @@ -1465,7 +1465,7 @@ let _cachedColLabelDrawer = new CachablePainting( painter, dw, colCount, - i => prefix + Util.bin(colCount-1-i, rowWires), + i => prefix + Util.bin(colCount-1-i, colWires), SUPERPOSITION_GRID_LABEL_SPAN); }); diff --git a/src/ui/menu.js b/src/ui/menu.js index 2a01ba29..360ad3d1 100644 --- a/src/ui/menu.js +++ b/src/ui/menu.js @@ -24,22 +24,28 @@ const groverLink = { ], "gates":[{"id":"~vn6c","name":"Oracle","circuit":{"cols":[["Z","•","◦","•","•"]]}}] }; -const teleportLink = {"cols":[ - [1,"H"], - [1,"•",1,1,"X"], - ["…","…",1,1,"…"], - ["…","…",1,1,"…"], - ["e^-iYt"], - ["X^t"], - ["Bloch"], - ["•","X"], - ["H"], - ["Measure","Measure"], - [1,"•",1,1,"X"], - ["•",1,1,1,"Z"], - [1,1,1,1,"Bloch"]] +const teleportLink = { + "cols":[ + [1,"H"], + [1,"•",1,1,"X"], + ["…","…",1,1,"…"], + ["…","…",1,1,"…"], + ["~87lj"], + ["Bloch"], + ["•","X"], + ["H"], + ["Measure","Measure"], + [1,"•",1,1,"X"], + ["•",1,1,1,"Z"], + [1,1,1,1,"Bloch"], + [1,1,1,1,"~f7c0"] + ], + "gates":[ + {"id":"~87lj","name":"message","circuit":{"cols":[["e^-iYt"],["X^t"]]}}, + {"id":"~f7c0","name":"received","matrix":"{{1,0},{0,1}}"} + ] }; -const erasureLink = {"cols":[ +const eraserLink = {"cols":[ [1,"H"], [1,"•",1,1,"X"], [1,1,"QFT7"], @@ -57,6 +63,121 @@ const erasureLink = {"cols":[ ["•","◦","Chance7"], ["•","•","Chance7"] ]}; +const chshTestLink = { + "cols": [ + ["H"], + ["◦",1,1,1,"X"], + ["X^-¼"], + ["…","…","…","…","…"], + ["~da85","~5s2n",1,"~5s2n","~ahov"], + [1,"H",1,"H"], + [1,"Measure",1,"Measure"], + ["X^½","•"], + [1,1,1,"•","X^½"], + ["Measure",1,1,1,"Measure"], + ["…","…","…","…","…"], + [1,"•","X","•"], + ["•",1,"X"], + [1,1,"X",1,"•"], + [1,1,"Chance"], + [1,1,"~q6e"] + ], + "gates": [ + {"id":"~da85","name":"Alice","matrix":"{{1,0},{0,1}}"}, + {"id":"~ahov","name":"Bob","matrix":"{{1,0},{0,1}}"}, + {"id":"~5s2n","name":"Referee","matrix":"{{1,0},{0,1}}"}, + {"id":"~q6e","name":"Win?","matrix":"{{1,0},{0,1}}"} + ] +}; +const additionLink = {"cols":[ + ["Counting5",1,1,1,1,1,1,1,1,"X"], + ["Chance5",1,1,1,1,"Measure","Chance5"], + ["…","…","…","…","…","…","…","…","…","…","…"], + ["X","X","X","X","•",1,1,"X","X","X"], + ["Swap",1,1,1,"Swap",1,"•"], + [1,1,1,1,"•",1,1,"X"], + [1,"Swap",1,1,"Swap",1,1,"•"], + [1,1,1,1,"•",1,1,1,"X"], + [1,1,"Swap",1,"Swap",1,1,1,"•"], + [1,1,1,1,"•",1,1,1,1,"X"], + [1,1,1,"Swap","Swap",1,1,1,1,"•"], + [1,1,1,1,"•",1,1,1,1,1,"X"], + [1,1,1,"Swap","Swap",1,1,1,1,"•"], + [1,1,"Swap",1,"Swap",1,1,1,"•"], + [1,"Swap",1,1,"Swap",1,1,"•"], + ["Swap",1,1,1,"Swap",1,"•"], + ["X","X","X","X","•"], + [1,1,1,"•",1,1,1,1,1,"X"], + [1,1,"•",1,1,1,1,1,"X"], + [1,"•",1,1,1,1,1,"X"], + ["•",1,1,1,1,1,"X"], + ["…","…","…","…","…","…","…","…","…","…","…"], + ["Chance5",1,1,1,1,1,"Chance5"] +]}; +const qftLink = {"cols":[ + ["Counting8"], + ["Chance8"], + ["…","…","…","…","…","…","…","…"], + ["rev8"], + ["H"], + ["Z^½","•"], + [1,"H"], + ["Z^¼","Z^½","•"], + [1,1,"H"], + ["Z^⅛","Z^¼","Z^½","•"], + [1,1,1,"H"], + ["Z^⅟₁₆","Z^⅛","Z^¼","Z^½","•"], + [1,1,1,1,"H"], + ["Z^⅟₃₂","Z^⅟₁₆","Z^⅛","Z^¼","Z^½","•"], + [1,1,1,1,1,"H"], + ["Z^⅟₆₄","Z^⅟₃₂","Z^⅟₁₆","Z^⅛","Z^¼","Z^½","•"], + [1,1,1,1,1,1,"H"], + ["Z^⅟₁₂₈","Z^⅟₆₄","Z^⅟₃₂","Z^⅟₁₆","Z^⅛","Z^¼","Z^½","•"], + [1,1,1,1,1,1,1,"H"] +]}; +const superdenseCodingLink = {"cols":[ + [1,1,"H"], + [1,1,"•",1,1,1,"X"], + ["…","…","…","…","…","…","…"], + ["Counting2"], + ["Measure","Measure"], + ["Chance","Chance"], + [1,"•","X"], + ["•",1,"Z"], + [1,1,"Swap",1,1,"Swap"], + [1,1,1,1,1,"•","X"], + [1,1,1,1,1,"H"], + [1,1,1,1,1,"Measure","Measure"], + [1,1,1,1,1,"Chance","Chance"] +]}; +const symmetryBreakingLink = { + "cols":[ + ["~tpqg",1,"~r2ku"], + ["…","…","…","…"], + ["H"], + [1,1,"H"], + ["•","X"], + [1,1,"•","X"], + [1,"Swap",1,"Swap"], + ["•","X"], + [1,1,"•","X"], + ["X^½","◦"], + [1,1,"X^½","◦"], + [1,"X^½"], + [1,1,1,"X^½"], + ["Measure","Measure","Measure","Measure"], + [1,"~57au"], + ["•",1,"Chance"], + [1,"•",1,"Chance"], + ["◦",1,"Chance"], + [1,"◦",1,"Chance"] + ], + "gates": [ + {"id":"~tpqg","name":"Alice^1","matrix":"{{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}"}, + {"id":"~r2ku","name":"Alice^2","matrix":"{{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}"}, + {"id":"~57au","name":"disagree","matrix":"{{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}"} + ] +}; /** * @param {!Revision} revision @@ -89,11 +210,21 @@ function initMenu(revision, obsIsAnyOverlayShowing) { const groverAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-anchor-grover'); const teleportAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-anchor-teleport'); - const erasureAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-anchor-delayed-erasure'); + const eraserAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-anchor-delayed-eraser'); + const additionAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-addition'); + const superdenseCodeAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-superdense-coding'); + const symmetryBreakAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-symmetry-break'); + const chshTestAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-chsh-test'); + const qftAnchor = /** @type {!HTMLAnchorElement} */ document.getElementById('example-qft'); for (let [a, t] of [[groverAnchor, groverLink], [teleportAnchor, teleportLink], - [erasureAnchor, erasureLink]]) { + [eraserAnchor, eraserLink], + [additionAnchor, additionLink], + [superdenseCodeAnchor, superdenseCodingLink], + [symmetryBreakAnchor, symmetryBreakingLink], + [chshTestAnchor, chshTestLink], + [qftAnchor, qftLink]]) { let text = JSON.stringify(t); a.href = "#circuit=" + text; a.onclick = ev => { diff --git a/template/menu.partial.html b/template/menu.partial.html index 89784d5a..6d67be2c 100644 --- a/template/menu.partial.html +++ b/template/menu.partial.html @@ -39,11 +39,16 @@ -
- Example Circuits

- Grover Search

- Quantum Teleportation

- Delayed Choice Erasure +
+ Example Circuits
+ Grover Search
+ Bell Inequality Test (CHSH)
+ Quantum Fourier Transform
+ Quantum Teleportation
+ Superdense Coding
+ Delayed Choice Eraser
+ Reversible Addition
+ Symmetry Breaking
diff --git a/test/circuit/Serializer.test.js b/test/circuit/Serializer.test.js index cba45c36..effa60d2 100644 --- a/test/circuit/Serializer.test.js +++ b/test/circuit/Serializer.test.js @@ -184,14 +184,16 @@ const IDS_THAT_SHOULD_BE_KNOWN = [ "dec1", "dec2", "dec3", "dec4", "dec5", "dec6", "dec7", "dec8", "dec9", "dec10", "dec11", "dec12", "dec13", "dec14", "dec15", "dec16", "add2", "add3", "add4", "add5", "add6", "add7", "add8", "add9", "add10", "add11", "add12", "add13", "add14", "add15", "add16", "+=A1", "+=A2", "+=A3", "+=A4", "+=A5", "+=A6", "+=A7", "+=A8", "+=A9", "+=A10", "+=A11", "+=A12", "+=A13", "+=A14", "+=A15", "+=A16", + "+=AA1", "+=AA2", "+=AA3", "+=AA4", "+=AA5", "+=AA6", "+=AA7", "+=AA8", "+=AA9", "+=AA10", "+=AA11", "+=AA12", "+=AA13", "+=AA14", "+=AA15", "+=AA16", "^=A1", "^=A2", "^=A3", "^=A4", "^=A5", "^=A6", "^=A7", "^=A8", "sub2", "sub3", "sub4", "sub5", "sub6", "sub7", "sub8", "sub9", "sub10", "sub11", "sub12", "sub13", "sub14", "sub15", "sub16", "-=A1", "-=A2", "-=A3", "-=A4", "-=A5", "-=A6", "-=A7", "-=A8", "-=A9", "-=A10", "-=A11", "-=A12", "-=A13", "-=A14", "-=A15", "-=A16", + "-=AA1", "-=AA2", "-=AA3", "-=AA4", "-=AA5", "-=AA6", "-=AA7", "-=AA8", "-=AA9", "-=AA10", "-=AA11", "-=AA12", "-=AA13", "-=AA14", "-=AA15", "-=AA16", "+cntA1", "+cntA2", "+cntA3", "+cntA4", "+cntA5", "+cntA6", "+cntA7", "+cntA8", "+cntA9", "+cntA10", "+cntA11", "+cntA12", "+cntA13", "+cntA14", "+cntA15", "+cntA16", "-cntA1", "-cntA2", "-cntA3", "-cntA4", "-cntA5", "-cntA6", "-cntA7", "-cntA8", "-cntA9", "-cntA10", "-cntA11", "-cntA12", "-cntA13", "-cntA14", "-cntA15", "-cntA16", "X^⌈t⌉", "X^⌈t-¼⌉", - "Counting1", "Counting2", "Counting3", "Counting4", "Counting5", "Counting6", "Counting7", "Counting8", - "Uncounting1", "Uncounting2", "Uncounting3", "Uncounting4", "Uncounting5", "Uncounting6", "Uncounting7", "Uncounting8", + "Counting1", "Counting2", "Counting3", "Counting4", "Counting5", "Counting6", "Counting7", "Counting8", "Counting9", "Counting10", "Counting11", "Counting12", "Counting13", "Counting14", "Counting15", "Counting16", + "Uncounting1", "Uncounting2", "Uncounting3", "Uncounting4", "Uncounting5", "Uncounting6", "Uncounting7", "Uncounting8", "Uncounting9", "Uncounting10", "Uncounting11", "Uncounting12", "Uncounting13", "Uncounting14", "Uncounting15", "Uncounting16", ">>t2", ">>t3", ">>t4", ">>t5", ">>t6", ">>t7", ">>t8", ">>t9", ">>t10", ">>t11", ">>t12", ">>t13", ">>t14", ">>t15", ">>t16", "<