Skip to content

Commit

Permalink
Add customizable formula gates
Browse files Browse the repository at this point in the history
- Add Formulaic toolbox section
- Add Complex.cos/sin/tan
- Add Gate._withParamRecomputeFunc
- Allow Gate.param to be non-integer
- Add GateDrawParams.hand
- GatePainting.paintGateSymbol has allowExponent parameter
- Add X/Y/Z^f(t) and Rx/y/z(f(t)) gates
- Tidy up after clicking gate button to edit inspector
- Add hand.isHoldingSomething
- Don't draw gate buttons when dragging
  • Loading branch information
Strilanc committed Sep 9, 2019
1 parent ef4c17c commit 4f54538
Show file tree
Hide file tree
Showing 18 changed files with 530 additions and 91 deletions.
30 changes: 25 additions & 5 deletions src/circuit/Gate.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,16 @@ class Gate {
this.width = 1;
/** @type {!int} The number of wires the gate spans on a circuit. */
this.height = 1;
/** @type {undefined|!int} A custom value that gets serialized. Each gate may use it to determine behavior. */
/** @type {undefined|!string|!number|!Array} A custom value that gets serialized.
* Each gate may use it to determine behavior. */
this.param = undefined;
/**
* Updates gate properties based on a new parameter value.
* Called by `Gate.withParam` before returning its result.
* @type {!function(gate: !Gate): undefined}
* @private
*/
this._withParamRecomputeFunc = g => {};

/** @type {undefined|!function(!GateDrawParams) : void} Draws the gate. A default is used when undefined. */
this.customDrawer = undefined;
Expand Down Expand Up @@ -295,6 +303,7 @@ class Gate {
g._requiredContextKeys = this._requiredContextKeys;
g._knownMatrixFunc = this._knownMatrixFunc;
g._stableDuration = this._stableDuration;
g._withParamRecomputeFunc = this._withParamRecomputeFunc;
g._hasNoEffect = this._hasNoEffect;
g._effectPermutesStates = this._effectPermutesStates;
g._effectCreatesSuperpositions = this._effectCreatesSuperpositions;
Expand All @@ -312,13 +321,14 @@ class Gate {
}

/**
* Sets an arbitrary number, saved and restored with the circuit, that the gate's custom functions may use.
* @param {undefined|!int} value
* Sets an arbitrary json value, saved and restored with the circuit, that the gate's custom functions may use.
* @param {undefined|!string|!number|!Array} value
* @returns {!Gate}
*/
withParam(value) {
let g = this._copy();
g.param = value;
g._withParamRecomputeFunc(g);
return g;
}

Expand Down Expand Up @@ -399,7 +409,7 @@ class Gate {
*/
knownMatrixAt(time) {
return this._knownMatrix !== undefined ? this._knownMatrix :
this._knownMatrixFunc !== undefined ? this._knownMatrixFunc(time) :
this._knownMatrixFunc !== undefined ? this._knownMatrixFunc(time, this.param) :
undefined;
}

Expand Down Expand Up @@ -740,7 +750,7 @@ class GateBuilder {
}

/**
* @param {!function(time : !number) : !Matrix} timeToMatrixFunc
* @param {!function(time : !number, gateParam: *) : !Matrix} timeToMatrixFunc
* @returns {!GateBuilder}
*/
setEffectToTimeVaryingMatrix(timeToMatrixFunc) {
Expand All @@ -750,6 +760,16 @@ class GateBuilder {
return this;
}

/**
* A function called by `Gate.withParam` before returning its result.
* @param {!function(gate: !Gate): undefined} withParamRecomputeFunc
* @returns {!GateBuilder}
*/
setWithParamPropertyRecomputeFunc(withParamRecomputeFunc) {
this.gate._withParamRecomputeFunc = withParamRecomputeFunc;
return this;
}

/**
* Sets a custom circuit-update function to run when simulating this gate.
* @param {undefined|!function(!CircuitEvalContext)} circuitUpdateFunc
Expand Down
3 changes: 0 additions & 3 deletions src/circuit/Serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,6 @@ let fromJson_Gate_props = json => {
let matrix = json["matrix"];
let circuit = json["circuit"];
let param = json["arg"];
if (param !== undefined && (!Number.isInteger(param) || param < 0 || param > 1<<16)) {
throw new DetailedError("Gate arg not int in [0, 2^16].", {json});
}
let symbol = json.name !== undefined ? json.name :
id.startsWith('~') ? '' :
id;
Expand Down
4 changes: 4 additions & 0 deletions src/draw/GateDrawParams.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
class GateDrawParams {
/**
* @param {!Painter} painter
* @param {!Hand} hand
* @param {!boolean} isInToolbox
* @param {!boolean} isHighlighted
* @param {!boolean} isResizeShowing
Expand All @@ -30,6 +31,7 @@ class GateDrawParams {
* @param {undefined|*} customStatsForCircuitPos
*/
constructor(painter,
hand,
isInToolbox,
isHighlighted,
isResizeShowing,
Expand All @@ -42,6 +44,8 @@ class GateDrawParams {
customStatsForCircuitPos) {
/** @type {!Painter} */
this.painter = painter;
/** @type {!Hand} */
this.hand = hand;
/** @type {!boolean} */
this.isInToolbox = isInToolbox;
/** @type {!boolean} */
Expand Down
35 changes: 26 additions & 9 deletions src/draw/GatePainting.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ GatePainting.paintResizeTab = args => {
/**
* @param {!GateDrawParams} args
* @param {undefined|!string=undefined} symbolOverride
* @param {!boolean=} allowExponent
*/
GatePainting.paintGateSymbol = (args, symbolOverride=undefined) => {
GatePainting.paintGateSymbol = (args, symbolOverride=undefined, allowExponent=true) => {
let painter = args.painter;
let rect = args.rect.paddedBy(-2);
if (symbolOverride === undefined) {
Expand All @@ -136,7 +137,8 @@ GatePainting.paintGateSymbol = (args, symbolOverride=undefined) => {
let {symbol, offsetY} = _paintSymbolHandleLines(args.painter, symbolOverride, rect);
painter.ctx.font = GATE_SYMBOL_FONT; // So that measure-text calls return the right stuff.

let parts = symbol.split("^");
let splitIndex = allowExponent ? symbol.indexOf('^') : -1;
let parts = splitIndex === -1 ? [symbol] : [symbol.substr(0, splitIndex), symbol.substr(splitIndex + 1)];
if (parts.length !== 2 || parts[0] === "" || parts[1] === "") {
painter.print(
symbol,
Expand Down Expand Up @@ -369,10 +371,20 @@ GatePainting.makeCycleDrawer = (xScale=1, yScale=1, tScale=1, zeroAngle=0) => ar
if (args.isInToolbox && !args.isHighlighted) {
return;
}
let τ = 2 * Math.PI;
let t = Util.properMod(-args.stats.time * τ * tScale, τ);
GatePainting.paintCycleState(args, args.stats.time * 2 * Math.PI * tScale, xScale, yScale, zeroAngle);
};

/**
* @param {!GateDrawParams} args
* @param {!number} angle
* @param {!number} xScale
* @param {!number} yScale
* @param {!number} zeroAngle
*/
GatePainting.paintCycleState = (args, angle, xScale=1, yScale=1, zeroAngle=0) => {
let t = Util.properMod(-angle, 2 * Math.PI);
let c = args.rect.center();
let r = 0.4 * args.rect.w;
let r = 16;

args.painter.ctx.save();

Expand All @@ -386,7 +398,7 @@ GatePainting.makeCycleDrawer = (xScale=1, yScale=1, tScale=1, zeroAngle=0) => ar
args.painter.ctx.beginPath();
args.painter.ctx.moveTo(0, 0);
args.painter.ctx.lineTo(0, r);
args.painter.ctx.arc(0, 0, r, τ/4, τ/4 + t, true);
args.painter.ctx.arc(0, 0, r, Math.PI/2, Math.PI/2 + t, true);
args.painter.ctx.lineTo(0, 0);
args.painter.ctx.closePath();
args.painter.ctx.stroke();
Expand Down Expand Up @@ -419,13 +431,18 @@ function _wireY(args, offset) {
* @param {!Rect} wholeRect
* @returns {!Rect}
*/
GatePainting.gateButtonRect = wholeRect => wholeRect.bottomHalf().skipTop(6).paddedBy(-7);
GatePainting.gateButtonRect = wholeRect => {
if (wholeRect.h > 50) {
return wholeRect.bottomHalf().skipTop(6).paddedBy(-7);
}
return wholeRect.bottomHalf().paddedBy(+2);
};

/**
* @param {!GateDrawParams} args
*/
GatePainting.paintGateButton = args => {
if (!args.isHighlighted || args.isInToolbox) {
if (!args.isHighlighted || args.isInToolbox || args.hand.isHoldingSomething()) {
return;
}

Expand All @@ -443,7 +460,7 @@ GatePainting.paintGateButton = args => {
buttonRect.w,
buttonRect.h);
args.painter.strokeRect(buttonRect, 'black');
}
};


/**
Expand Down
18 changes: 8 additions & 10 deletions src/gates/AllGates.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ 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"

import {seq} from "src/base/Seq.js"

let Gates = {};
Expand Down Expand Up @@ -229,14 +227,6 @@ Gates.TopToolboxGroups = [
VariousXGates.X4, VariousXGates.X4i,
]
},
{
hint: "Sixteenths",
gates: [
VariousZGates.Z8, VariousZGates.Z8i,
VariousYGates.Y8, VariousYGates.Y8i,
VariousXGates.X8, VariousXGates.X8i,
]
},
{
hint: "Spinning",
gates: [
Expand All @@ -245,6 +235,14 @@ Gates.TopToolboxGroups = [
PoweringGates.XForward, PoweringGates.XBackward,
]
},
{
hint: "Formulaic",
gates: [
ParametrizedRotationGates.FormulaicRotationZ, ParametrizedRotationGates.FormulaicRotationRz,
ParametrizedRotationGates.FormulaicRotationY, ParametrizedRotationGates.FormulaicRotationRy,
ParametrizedRotationGates.FormulaicRotationX, ParametrizedRotationGates.FormulaicRotationRx,
]
},
{
hint: "Parametrized",
gates: [
Expand Down
2 changes: 1 addition & 1 deletion src/gates/ExponentiatingGates.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,4 @@ ExponentiatingGates.all = [
ExponentiatingGates.ZForward
];

export {ExponentiatingGates}
export {ExponentiatingGates, XExp, YExp, ZExp}
7 changes: 7 additions & 0 deletions src/gates/InputGates.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ let makeSetInputGate = key => new GateBuilder().

return oldGate.withParam(val);
}).
setExtraDisableReasonFinder(args => {
let p = args.gate.param;
if (!Number.isInteger(p) || p < 0 || p > 1<<16) {
return 'bad\nvalue';
}
return undefined;
}).
gate.
withParam(2);

Expand Down
Loading

0 comments on commit 4f54538

Please sign in to comment.