Skip to content

Commit

Permalink
Merge pull request #266 from Strilanc/dev (v1.9.0)
Browse files Browse the repository at this point in the history
  • Loading branch information
Strilanc authored Mar 3, 2017
2 parents 3a5192e + 9e93822 commit e8c5d42
Show file tree
Hide file tree
Showing 28 changed files with 646 additions and 175 deletions.
4 changes: 2 additions & 2 deletions GruntFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ module.exports = function(grunt) {
var errPart = grunt.file.read('template/error.partial.html');
var forgePart = grunt.file.read('template/forge.partial.html');
var exportPart = grunt.file.read('template/export.partial.html');
var loadingPart = grunt.file.read('template/loading.partial.html');
var menuPart = grunt.file.read('template/menu.partial.html');
var output = html;
output = output.split("<!-- INCLUDE SOURCE PART -->").join(js);
output = output.split("<!-- INCLUDE LOADING PART -->").join(loadingPart);
output = output.split("<!-- INCLUDE MENU PART -->").join(menuPart);
output = output.split("<!-- INCLUDE ERROR PART -->").join(errPart);
output = output.split("<!-- INCLUDE FORGE PART -->").join(forgePart);
output = output.split("<!-- INCLUDE EXPORT PART -->").join(exportPart);
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# <a href="http://algorithmicassertions.com/quirk">Quirk <img src="doc/favicon.ico" alt="Icon" title="Icon" /></a>
# <a href="http://algassert.com/quirk">Quirk <img src="doc/favicon.ico" alt="Icon" title="Icon" /></a>

[![Build Status](https://travis-ci.org/Strilanc/Quirk.svg?branch=master)](https://travis-ci.org/Strilanc/Quirk)
[![Code Climate](https://codeclimate.com/github/Strilanc/Quirk/badges/gpa.svg)](https://codeclimate.com/github/Strilanc/Quirk)

Quirk is a toy quantum circuit simulator, intended to help people in learning about quantum computing.

If you want to quickly explore the behavior of a small quantum circuit, Quirk is the tool for you.
There's no installing or configuring or scripting: just go to **[algorithmicassertions.com/quirk](http://algorithmicassertions.com/quirk)**, drag gates onto the circuit, and the output displays will update in real time.
There's no installing or configuring or scripting: just go to **[algassert.com/quirk](http://algassert.com/quirk)**, drag gates onto the circuit, and the output displays will update in real time.

(If you're still trying to understand what a quantum circuit *even is*, then I recommend the video series [Quantum Computing for the Determined](https://www.youtube.com/playlist?list=PL1826E60FD05B44E4).
Quirk assumes you already know background facts like "each wire represents a qubit".)
Expand All @@ -27,7 +27,7 @@ Quirk assumes you already know background facts like "each wire represents a qub

**Try it out**:

**[algorithmicassertions.com/quirk](http://algorithmicassertions.com/quirk)**
**[algassert.com/quirk](http://algassert.com/quirk)**

# Examples

Expand Down
2 changes: 1 addition & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
- Z is +up/-down
- Ordering
- Top wire is the low bit. Bottom wire is the high bit.
- Kets are little-endian. |10100⟩ is 5, not 20.
- Kets are big-endian. |00101⟩ is 5, not 20.
- Listed/grided values are in ascending row-major order from top left to bottom right.
- Colors
- Blue: amplitudes
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.8.0",
"version": "1.9.0",
"homepage": "https://github.com/Strilanc/Quirk",
"bugs": {
"url": "https://github.com/Strilanc/Quirk/issues"
Expand Down
2 changes: 1 addition & 1 deletion src/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
class Config {}

Config.EMPTY_CIRCUIT_TITLE = 'Quantum Circuit Toy - Quirk';
Config.EMPTY_CIRCUIT_TITLE = 'Quirk: Quantum Circuit Simulator';

// Each qubit (when actually used) doubles the cost of simulating each gate applied to the circuit.
// Also each qubit tends to increase the amount of accuracy required.
Expand Down
8 changes: 7 additions & 1 deletion src/base/Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,14 @@ class Util {
return i & 0xFF;
}

/**
* Returns a big-endian binary representation of the given number, zero-padded and truncated to the given length.
* @param {!number|!int} number
* @param {!int} fixedLen
* @returns {!string}
*/
static bin(number, fixedLen) {
return ("0".repeat(fixedLen) + number.toString(2)).slice(-fixedLen).split("").reverse().join("");
return ("0".repeat(fixedLen) + number.toString(2)).slice(-fixedLen).split("").join("");
}

/**
Expand Down
20 changes: 20 additions & 0 deletions src/circuit/CircuitDefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,26 @@ class CircuitDefinition {
return count === 2;
}

/**
* @param {!Point} pt
* @param {!string} key
* @returns {!boolean}
*/
locProvidesStat(pt, key) {
let g = this.gateInSlot(pt.x, pt.y);
return g !== undefined && !g.customColumnContextProvider(0).every(e => e.key !== key);
}

/**
* @param {!Point} pt
* @param {!string} key
* @returns {!boolean}
*/
locNeedsStat(pt, key) {
let g = this.gateInSlot(pt.x, pt.y);
return g !== undefined && g._requiredContextKeys.indexOf(key) !== -1;
}

/**
* @param {!Point} pt
* @returns {!boolean}
Expand Down
28 changes: 26 additions & 2 deletions src/circuit/GateShaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ GateShaders.applyMatrixOperation = (ctx, matrix) => {
*/
GateShaders.cycleAllBits = (inputTexture, shiftAmount) => {
let size = currentShaderCoder().vec2.arrayPowerSizeOfTexture(inputTexture);
return CYCLE_ALL_SHADER(
return CYCLE_ALL_SHADER_VEC2(
inputTexture,
WglArg.float("shiftAmount", 1 << Util.properMod(-shiftAmount, size)));
};
const CYCLE_ALL_SHADER = makePseudoShaderWithInputsAndOutputAndCode(
const CYCLE_ALL_SHADER_VEC2 = makePseudoShaderWithInputsAndOutputAndCode(
[Inputs.vec2('input')],
Outputs.vec2(),
`
Expand All @@ -144,4 +144,28 @@ const CYCLE_ALL_SHADER = makePseudoShaderWithInputsAndOutputAndCode(
return read_input(cycledState);
}`);

/**
* @param {!WglTexture} inputTexture
* @param {!int} shiftAmount
* @returns {!WglConfiguredShader}
*/
GateShaders.cycleAllBitsFloat = (inputTexture, shiftAmount) => {
let size = currentShaderCoder().vec2.arrayPowerSizeOfTexture(inputTexture);
return CYCLE_ALL_SHADER_FLOAT(
inputTexture,
WglArg.float("shiftAmount", 1 << Util.properMod(-shiftAmount, size)));
};
const CYCLE_ALL_SHADER_FLOAT = makePseudoShaderWithInputsAndOutputAndCode(
[Inputs.float('input')],
Outputs.float(),
`
uniform float shiftAmount;
float outputFor(float k) {
float span = len_input();
float shiftedState = k * shiftAmount;
float cycledState = mod(shiftedState, span) + floor(shiftedState / span);
return read_input(cycledState);
}`);

export {GateShaders}
40 changes: 18 additions & 22 deletions src/draw/CachablePainting.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,40 @@ const fixedRng = new RestartableRng();
*/
class CachablePainting {
/**
* @param {!int} width
* @param {!int} height
* @param {!function(painter: !Painter) : void} drawingFunc
* @param {!function(key: *) : !{width: !int, height: !int}} sizeFunc
* @param {!function(painter: !Painter | !(function(!Painter, !string): void)) : void} drawingFunc
*/
constructor(width, height, drawingFunc) {
/** @type {!int} */
this.width = width;
/** @type {!int} */
this.height = height;
constructor(sizeFunc, drawingFunc) {
/** @type {!function(key: *) : !{width: !int, height: !int}} */
this.sizeFunc = sizeFunc;
/**
* @type {!(function(!Painter): void)}
* @type {!(function(!Painter): void) | !(function(!Painter, !string): void)}
* @private
*/
this._drawingFunc = drawingFunc;
/**
* @type {undefined|!HTMLCanvasElement}
* @type {!Map.<!string, !HTMLCanvasElement>}
* @private
*/
this._cacheCanvas = undefined;
this._cachedCanvases = new Map();
}

/**
* @param {!int} x
* @param {!int} y
* @param {!Painter} painter
* @param {!*=} key
*/
paint(x, y, painter) {
if (this._cacheCanvas === undefined) {
this._initCache();
paint(x, y, painter, key=undefined) {
if (!this._cachedCanvases.has(key)) {
let canvas = /** @type {!HTMLCanvasElement} */ document.createElement('canvas');
let {width, height} = this.sizeFunc(key);
canvas.width = width;
canvas.height = height;
this._drawingFunc(new Painter(canvas, fixedRng.restarted()), key);
this._cachedCanvases.set(key, canvas);
}
painter.ctx.drawImage(this._cacheCanvas, x, y);
}

_initCache() {
this._cacheCanvas = document.createElement('canvas');
this._cacheCanvas.width = this.width;
this._cacheCanvas.height = this.height;
this._drawingFunc(new Painter(this._cacheCanvas, fixedRng.restarted()));
painter.ctx.drawImage(this._cachedCanvases.get(key), x, y);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/draw/MathPainter.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ class MathPainter {
let mag = Math.sqrt(real*real + imag*imag);
let g = 1 + Math.log(mag)/10;
let r = Math.max(1, g/mag)*Math.max(d/2, 5);
if (isNaN(mag) || r < 0.1) {
if (r < 0.1) {
return;
}
let cx = x + d/2;
Expand Down
12 changes: 10 additions & 2 deletions src/draw/WidgetPainter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import {Matrix} from "src/math/Matrix.js"
import {Painter} from "src/draw/Painter.js"
import {Point} from "src/math/Point.js"
import {Rect} from "src/math/Rect.js"
import {seq, Seq} from "src/base/Seq.js"
import {Seq} from "src/base/Seq.js"
import {drawCircuitTooltip} from "src/ui/DisplayedCircuit.js"
import {Util} from "src/base/Util.js"

class WidgetPainter {

Expand Down Expand Up @@ -237,6 +238,13 @@ class WidgetPainter {
return {maxW: maxX, maxH: maxY};
}

/**
* @param {!int} bitCount
* @param {!int} bitMask
* @param {!Complex|!number} factor
* @param {!Format} format
* @returns {!string}
*/
static describeKet(bitCount, bitMask, factor, format) {
factor = Complex.from(factor);
if (factor.isEqualTo(0)) {
Expand All @@ -250,7 +258,7 @@ class WidgetPainter {
(factor.real === 0 || factor.imag === 0) && format !== Format.CONSISTENT ? factor.toString(format) :
'(' + factor.toString(format) + ')·';

let bitDesc = seq(bitMask.toString(2)).reverse().padded(bitCount, '0').join('');
let bitDesc = Util.bin(bitMask, bitCount);
return scaleFactorDesc + '|' + bitDesc + '⟩';
}
/**
Expand Down
4 changes: 3 additions & 1 deletion src/gates/AllGates.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {UniversalNotGate} from "src/gates/Impossible_UniversalNotGate.js"
import {VariousXGates} from "src/gates/VariousXGates.js"
import {VariousYGates} from "src/gates/VariousYGates.js"
import {VariousZGates} from "src/gates/VariousZGates.js"
import {XorGates} from "src/gates/XorGates.js"
import {ZeroGate} from "src/gates/Joke_ZeroGate.js"
import {MysteryGateMaker} from "src/gates/Joke_MysteryGate.js"

Expand Down Expand Up @@ -110,7 +111,8 @@ Gates.KnownToSerializer = [
...ReverseBitsGateFamily.all,
...VariousXGates.all,
...VariousYGates.all,
...VariousZGates.all
...VariousZGates.all,
...XorGates.all
];

let gatesById = seq(Gates.KnownToSerializer).keyedBy(g => g.serializedId);
Expand Down
88 changes: 85 additions & 3 deletions src/gates/CycleBitsGates.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,92 @@
import {Config} from "src/Config.js"
import {Gate} from "src/circuit/Gate.js"
import {GatePainting} from "src/draw/GatePainting.js"
import {ketArgs, ketShaderPermute} from "src/circuit/KetShaderUtil.js"
import {Matrix} from "src/math/Matrix.js"
import {Point} from "src/math/Point.js"
import {Util} from "src/base/Util.js"
import {WglArg} from "src/webgl/WglArg.js"
import {WglConfiguredShader} from "src/webgl/WglConfiguredShader.js"

let CycleBitsGates = {};

/**
* @param {!GateDrawParams} args
* @param {!int} offset
* @returns {!number}
*/
function wireY(args, offset) {
return args.rect.center().y + (offset - args.gate.height/2 + 0.5) * Config.WIRE_SPACING;
}

/**
* @param {!GateDrawParams} args
*/
function eraseWires(args) {
for (let i = 0; i < args.gate.height; i++) {
let y = wireY(args, i);
let p = new Point(args.rect.x, y);
let c = new Point(args.rect.center().x, y);
let q = new Point(args.rect.right(), y);
let pt = new Point(args.positionInCircuit.col, args.positionInCircuit.row + i);
let isMeasured1 = args.stats.circuitDefinition.locIsMeasured(pt);
let isMeasured2 = args.stats.circuitDefinition.locIsMeasured(pt.offsetBy(1, 0));

for (let dy of isMeasured1 ? [-1, +1] : [0]) {
args.painter.strokeLine(p.offsetBy(0, dy), c.offsetBy(0, dy), 'white');
}
for (let dy of isMeasured2 ? [-1, +1] : [0]) {
args.painter.strokeLine(c.offsetBy(0, dy), q.offsetBy(0, dy), 'white');
}
}
}

/**
* @param {!GateDrawParams} args
* @returns {!boolean}
*/
function useFallbackDrawer(args) {
return args.isHighlighted ||
args.isResizeHighlighted ||
args.positionInCircuit === undefined ||
args.stats.circuitDefinition.colHasControls(args.positionInCircuit.col);
}

/**
* The X gate is drawn as a crossed circle when it has controls.
* @param {!GateDrawParams} args
* @param {!function(!int) : !int} permutation
*/
function PERMUTATION_DRAWER(args, permutation) {
if (useFallbackDrawer(args)) {
GatePainting.DEFAULT_DRAWER(args);
return;
}

eraseWires(args);

// Draw wires.
for (let i = 0; i < args.gate.height; i++) {
let j = permutation(i);

let pt = new Point(args.positionInCircuit.col, args.positionInCircuit.row + i);
let isMeasured = args.stats.circuitDefinition.locIsMeasured(pt);
let y1 = wireY(args, i);
let y2 = wireY(args, j);
let x1 = args.rect.x;
let x2 = args.rect.right();
args.painter.ctx.beginPath();
args.painter.ctx.strokeStyle = 'black';
for (let [dx, dy] of isMeasured ? [[j > i ? +1 : -1, -1], [0, +1]] : [[0, 0]]) {
args.painter.ctx.moveTo(Math.min(x1, x1 + dx), y1 + dy);
args.painter.ctx.lineTo(x1 + dx, y1 + dy);
args.painter.ctx.lineTo(x2 + dx, y2 + dy);
args.painter.ctx.lineTo(Math.max(x2, x2 + dx), y2 + dy);
}
args.painter.ctx.stroke();
}
}

/**
* @param {!CircuitEvalContext} ctx
* @param {!int} qubitSpan
Expand Down Expand Up @@ -36,7 +116,8 @@ CycleBitsGates.CycleBitsFamily = Gate.generateFamily(2, 16, span => Gate.without
withSerializedId("<<" + span).
withHeight(span).
withKnownBitPermutation(i => (i + 1) % span).
withCustomShader(ctx => cycleBits(ctx, span, +1)));
withCustomShader(ctx => cycleBits(ctx, span, +1)).
withCustomDrawer(args => PERMUTATION_DRAWER(args, r => Util.properMod(r + 1, span))));

CycleBitsGates.ReverseCycleBitsFamily = Gate.generateFamily(2, 16, span => Gate.withoutKnownMatrix(
"↟",
Expand All @@ -48,11 +129,12 @@ CycleBitsGates.ReverseCycleBitsFamily = Gate.generateFamily(2, 16, span => Gate.
withSerializedId(">>" + span).
withHeight(span).
withKnownBitPermutation(i => (i + span - 1) % span).
withCustomShader(ctx => cycleBits(ctx, span, -1)));
withCustomShader(ctx => cycleBits(ctx, span, -1)).
withCustomDrawer(args => PERMUTATION_DRAWER(args, r => Util.properMod(r - 1, span))));

CycleBitsGates.all = [
...CycleBitsGates.CycleBitsFamily.all,
...CycleBitsGates.ReverseCycleBitsFamily.all
];

export {CycleBitsGates, cycleBits, makeCycleBitsMatrix};
export {CycleBitsGates, cycleBits, makeCycleBitsMatrix, PERMUTATION_DRAWER};
1 change: 0 additions & 1 deletion src/gates/InputGates.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ let makeInputGate = (key, reverse) => Gate.generateFamily(1, 16, span => Gate.fr
`Marks some qubits as input '${key}'${reverse ? ', in big-endian order' : ''}.`).
withSerializedId((reverse ? 'rev' : '') + `input${key}${span}`).
withHeight(span).
markedAsControlWireSource().
withCustomColumnContextProvider(qubitIndex => [{
key: `Input Range ${key}`,
val: {
Expand Down
Loading

0 comments on commit e8c5d42

Please sign in to comment.