From 2d0e00af6b8ad0956fff2982638946f62d2d623c Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 12 Aug 2019 19:43:34 -0300 Subject: [PATCH] Add parity controls - Add rawControls property to CircuitEvalContext - Add Util.popcnt - Add default output for Util.powerOfTwoness - Add Parity section to toolbox --- src/base/Util.js | 33 ++++- src/circuit/CircuitComputeUtil.js | 3 + src/circuit/CircuitDefinition.js | 20 ++- src/circuit/CircuitEvalContext.js | 6 + src/circuit/CircuitStats.js | 1 + src/circuit/Controls.js | 36 ++++- src/circuit/Gate.js | 15 ++- src/gates/AllGates.js | 8 ++ src/gates/Controls.js | 125 +++++++++++++++++- test/CircuitOperationTestUtil.js | 2 + test/base/Util.test.js | 21 +++ test/circuit/CircuitDefinition.test.js | 9 +- test/circuit/Serializer.test.js | 1 + test/gates/AllGates.test.js | 4 + test/gates/Controls.test.js | 85 ++++++++++++ .../gates/Impossible_UniversalNotGate.test.js | 1 + 16 files changed, 344 insertions(+), 26 deletions(-) diff --git a/src/base/Util.js b/src/base/Util.js index f35004ef..4c0f6f62 100644 --- a/src/base/Util.js +++ b/src/base/Util.js @@ -152,6 +152,24 @@ class Util { return p; } + /** + * Counts the number of set bits in an integer. + * + * @param {!int} i + * @returns {!int} + */ + static popcnt(i) { + if (i < 0) { + return Math.POSITIVE_INFINITY; + } + let t = 0; + while (i > 0) { + i &= i - 1; + t++; + } + return t; + } + /** * Determines how multiply-even a number is; how many times you can divide it by 2 before getting an odd result. * Odd numbers have 0 power-of-two-ness, multiples of 2 that aren't multiples of 4 have 1 power-of-two-ness, @@ -160,18 +178,19 @@ class Util { * Note that zero has infinite power-of-two-ness. * * @param {!int} i - * @returns {!int} + * @param {T=} zeroResult The value to return when i == 0. Defaults to positive infinity (because you can divide + * zero by two as many times as you want and still get an integer). + * @returns {T|!int} + * @template T */ - static powerOfTwoness(i) { + static powerOfTwoness(i, zeroResult=Math.POSITIVE_INFINITY) { if (i === 0) { - return Math.POSITIVE_INFINITY; + return zeroResult; } if (i < 0) { - return Util.powerOfTwoness(-i); + return Util.powerOfTwoness(-i, zeroResult); } - let lowMask = i ^ (i - 1); - let lowBit = i & lowMask; - return Math.round(Math.log2(lowBit)); + return Math.round(Math.log2(i & ~(i - 1))); } /** diff --git a/src/circuit/CircuitComputeUtil.js b/src/circuit/CircuitComputeUtil.js index 5ef3c8f5..b93af72a 100644 --- a/src/circuit/CircuitComputeUtil.js +++ b/src/circuit/CircuitComputeUtil.js @@ -133,6 +133,7 @@ function _extractStateStatsNeededByCircuitColumn( circuitDefinition.numWires, ctx.controls, ctx.controlsTexture, + ctx.controls, ctx.stateTrader, Util.mergeMaps( ctx.customContextFromGates, @@ -185,6 +186,7 @@ function _advanceStateWithCircuitDefinitionColumn( ctx.wireCount, ctx.controls, ctx.controlsTexture, + controls, trader, colContext); let mainCtx = new CircuitEvalContext( @@ -193,6 +195,7 @@ function _advanceStateWithCircuitDefinitionColumn( ctx.wireCount, controls, controlTex, + controls, trader, colContext); diff --git a/src/circuit/CircuitDefinition.js b/src/circuit/CircuitDefinition.js index 2b9201a3..5bbe9d5a 100644 --- a/src/circuit/CircuitDefinition.js +++ b/src/circuit/CircuitDefinition.js @@ -876,18 +876,30 @@ class CircuitDefinition { if (col < 0 || col >= this.columns.length) { return Controls.NONE; } - let result = Controls.NONE; let column = this.columns[col]; + let includeMask = 0; + let desireMask = 0; + let parityMask = 0; for (let i = 0; i < column.gates.length; i++) { let gate = column.gates[i]; if (gate !== undefined && this.gateAtLocIsDisabledReason(col, i) === undefined) { let bit = gate.controlBit(); - if (bit !== undefined) { - result = result.and(Controls.bit(i, bit)); + if (bit === 'parity') { + parityMask |= 1 << i; + } else if (bit !== undefined) { + includeMask |= 1 << i; + if (bit) { + desireMask |= 1 << i; + } } } } - return result; + if (parityMask !== 0) { + let parityBit = parityMask & ~(parityMask - 1); + desireMask |= parityBit; + includeMask |= parityBit; + } + return new Controls(includeMask, desireMask, parityMask); } /** diff --git a/src/circuit/CircuitEvalContext.js b/src/circuit/CircuitEvalContext.js index 357738d5..7583e5f1 100644 --- a/src/circuit/CircuitEvalContext.js +++ b/src/circuit/CircuitEvalContext.js @@ -26,6 +26,8 @@ class CircuitEvalContext { * @param {!int} wireCount * @param {!Controls} controls * @param {!WglTexture} controlsTexture + * @param {!Controls} rawControls The controls of the gate column, made available so that before/after operations + * can use this information (even though they are not themselves controlled). * @param {!WglTextureTrader} stateTrader * @param {!Map.} customContextFromGates */ @@ -34,6 +36,7 @@ class CircuitEvalContext { wireCount, controls, controlsTexture, + rawControls, stateTrader, customContextFromGates) { /** @type {!number} */ @@ -47,6 +50,8 @@ class CircuitEvalContext { this.wireCount = wireCount; /** @type {!Controls} */ this.controls = controls; + /** @type {!Controls} */ + this.rawControls = rawControls; /** @type {!WglTexture} */ this.controlsTexture = controlsTexture; /** @type {!WglTextureTrader} */ @@ -75,6 +80,7 @@ class CircuitEvalContext { this.wireCount, this.controls, this.controlsTexture, + this.rawControls, this.stateTrader, this.customContextFromGates); } diff --git a/src/circuit/CircuitStats.js b/src/circuit/CircuitStats.js index 80801fad..a886feff 100644 --- a/src/circuit/CircuitStats.js +++ b/src/circuit/CircuitStats.js @@ -344,6 +344,7 @@ class CircuitStats { numWires, Controls.NONE, controlTex, + Controls.NONE, stateTrader, new Map()), circuitDefinition, diff --git a/src/circuit/Controls.js b/src/circuit/Controls.js index c4a70924..aad76e26 100644 --- a/src/circuit/Controls.js +++ b/src/circuit/Controls.js @@ -25,17 +25,25 @@ class Controls { /** * @param {!int} inclusionMask. * @param {!int} desiredValueMask + * @param {!int=0} parityMask * @property {!int} inclusionMask. * @property {!int} desiredValueMask */ - constructor(inclusionMask, desiredValueMask) { + constructor(inclusionMask, desiredValueMask, parityMask=0) { if ((desiredValueMask & ~inclusionMask) !== 0) { throw new DetailedError("Desired un-included bits", {inclusionMask, desiredValueMask}); } + if (parityMask !== 0 && Util.popcnt(inclusionMask & parityMask) !== 1) { + throw new DetailedError("Exactly one parity bit must be in the inclusion mask", + {inclusionMask, parityMask}); + } + /** @type {!int} */ this.inclusionMask = inclusionMask; /** @type {!int} */ this.desiredValueMask = desiredValueMask; + /** @type {!int} */ + this.parityMask = parityMask; } /** @@ -57,7 +65,8 @@ class Controls { isEqualTo(other) { return other instanceof Controls && this.inclusionMask === other.inclusionMask && - this.desiredValueMask === other.desiredValueMask; + this.desiredValueMask === other.desiredValueMask && + this.parityMask === other.parityMask; } /** @@ -68,12 +77,20 @@ class Controls { return "No Controls"; } - return "Controls: ...__" + Seq.naturals(). - takeWhile(i => (1< (1< this.desiredValueFor(e)). map(e => e === undefined ? "_" : e ? "1" : "0"). reverse(). join(""); + if (this.parityMask !== 0) { + result += "\n parity: ...__" + range. + map(e => this.parityMask & (1 << e)). + map(e => e ? "1" : "_"). + reverse(). + join("") + } + return result; } /** @@ -113,9 +130,13 @@ class Controls { if ((other.desiredValueMask & this.inclusionMask) !== (this.desiredValueMask & other.inclusionMask)) { throw new DetailedError("Contradictory controls.", {"this": this, other}) } + if ((other.parityMask & this.inclusionMask) !== 0 || (this.parityMask & other.inclusionMask) !== 0) { + throw new DetailedError("Can't intersect parity controls.", {"this": this, other}) + } return new Controls( this.inclusionMask | other.inclusionMask, - this.desiredValueMask | other.desiredValueMask); + this.desiredValueMask | other.desiredValueMask, + this.parityMask | other.parityMask); } /** @@ -125,7 +146,8 @@ class Controls { shift(offset) { return new Controls( this.inclusionMask << offset, - this.desiredValueMask << offset) + this.desiredValueMask << offset, + this.parityMask << offset) } } diff --git a/src/circuit/Gate.js b/src/circuit/Gate.js index c27ea2d0..67c5cff1 100644 --- a/src/circuit/Gate.js +++ b/src/circuit/Gate.js @@ -152,9 +152,10 @@ class Gate { /** * Determines if this gate conditions or anti-conditions other operations or not. - * Note that 'False' means 'anti-control', not 'not a control'. Use undefined for 'not a control'. + * Note that 'False' means 'anti-control', not 'not a control'. Use undefined for 'not a control'. Also, + * this value may be set to "parity" to indicate a parity control. * Non-computational-basis controls also use this mechanism, but with before/after operations. - * @type {undefined|!boolean} + * @type {undefined|!string|!boolean} * @private */ this._controlBit = undefined; @@ -417,7 +418,7 @@ class Gate { } /** - * @returns {undefined|!boolean} + * @returns {undefined|!string|!boolean} */ controlBit() { return this._controlBit; @@ -827,6 +828,11 @@ class GateBuilder { } /** + * Indicates that the gate may temporarily, but not permanently, transform the system state. + * + * The gate may also be part of a pair that together permanently changes the state (e.g. an input gate with an + * addition gate), but the other gate in the pair will not be marked as having no effect. + * * @returns {!GateBuilder} */ promiseHasNoNetEffectOnStateVector() { @@ -858,7 +864,8 @@ class GateBuilder { /** * Sets meta-properties to indicate a gate is a control. - * @param {!boolean} bit: Whether gate is a control or anti-control. Use before/after operations for flexibility. + * @param {!boolean|!string} bit: Whether gate is a control (True), anti-control (False), or parity control + * ("parity"). Use before/after operations for flexibility. * @param {!boolean} guaranteedClassical Whether or not the control can be used to control permutations of classical * wires, even if placed on a coherent wire. * @returns {!GateBuilder} diff --git a/src/gates/AllGates.js b/src/gates/AllGates.js index 5298f380..27544b78 100644 --- a/src/gates/AllGates.js +++ b/src/gates/AllGates.js @@ -261,6 +261,14 @@ Gates.TopToolboxGroups = [ Detectors.XDetector, Detectors.XDetectControlClear, ] }, + { + hint: "Parity", + gates: [ + Controls.ZParityControl, undefined, + Controls.YParityControl, undefined, + Controls.XParityControl, undefined, + ] + }, ]; /** @type {!Array}>} */ diff --git a/src/gates/Controls.js b/src/gates/Controls.js index 1b72690f..062f3dad 100644 --- a/src/gates/Controls.js +++ b/src/gates/Controls.js @@ -17,6 +17,10 @@ import {GatePainting} from "src/draw/GatePainting.js" import {GateShaders} from "src/circuit/GateShaders.js" import {HalfTurnGates} from "src/gates/HalfTurnGates.js" import {QuarterTurnGates} from "src/gates/QuarterTurnGates.js" +import {Config} from "src/Config.js" +import {ketArgs, ketShaderPermute} from "src/circuit/KetShaderUtil.js"; +import {WglArg} from "src/webgl/WglArg.js"; +import {Util} from "src/base/Util.js"; let Controls = {}; @@ -167,13 +171,132 @@ Controls.YControl = new GateBuilder(). }). gate; +const PARITY_SHADER = ketShaderPermute( + ` + uniform float parityMask; + `, + ` + float bitPos = 1.0; + float result = 0.5; + for (int i = 0; i < ${Config.MAX_WIRE_COUNT}; i++) { + float maskBit = mod(floor(parityMask/bitPos), 2.0); + float posBit = mod(floor(full_out_id/bitPos), 2.0); + float flip = maskBit * posBit; + result += flip; + bitPos *= 2.0; + } + return mod(result, 2.0) - 0.5;`, + 1); + +/** + * Applies a multi-target CNOT operation, merging the parities onto a single qubit (or reversing that process). + * + * Note that this method is invoked for each parity control, but only the last one in the column is supposed to + * perform the operation (or, when uncomputing, the first one). + * + * @param {!CircuitEvalContext} ctx + * @param {!boolean} order + */ +function parityGatherScatter(ctx, order) { + let c = ctx.rawControls; + let isLast = 2 << ctx.row > c.parityMask; + let isFirst = 1 << ctx.row === (c.parityMask & ~(c.parityMask - 1)); + if (order ? isLast : isFirst) { + ctx.applyOperation(PARITY_SHADER.withArgs( + ...ketArgs(ctx.withRow(Util.ceilLg2(c.parityMask & c.inclusionMask))), + WglArg.float('parityMask', c.parityMask) + )); + } +} + +/** + * @param {!string} name + * @returns {!function(args: !GateDrawParams)} + */ +function parityDrawer(name) { + return args => { + if (args.isInToolbox || args.isHighlighted) { + GatePainting.paintBackground(args); + GatePainting.paintOutline(args); + } + let center = args.rect.paddedBy(-10); + args.painter.fillRect(center); + args.painter.strokeRect(center); + args.painter.fillRect(center.paddedBy(-4).skipBottom(-6).skipTop(-6)); + args.painter.printLine(name, center, 0.5, undefined, undefined, undefined, 0); + args.painter.printLine('par', center, 0.5, 'red', 10, undefined, 1); + } +} + +Controls.XParityControl = new GateBuilder(). + setSerializedIdAndSymbol("xpar"). + setTitle("Parity Control (X)"). + setBlurb("Includes a qubit's X observable in the column parity control.\n" + + "Gates in the same column only apply if an odd number of parity controls are satisfied."). + setActualEffectToUpdateFunc(() => {}). + promiseEffectIsStable(). + promiseEffectIsUnitary(). + promiseHasNoNetEffectOnStateVector(). + markAsControlExpecting('parity'). + setSetupCleanupEffectToUpdateFunc( + ctx => { + HalfTurnGates.H.customOperation(ctx); + parityGatherScatter(ctx, true); + }, + ctx => { + parityGatherScatter(ctx, false); + HalfTurnGates.H.customOperation(ctx); + }). + setDrawer(parityDrawer('X')). + gate; + +Controls.YParityControl = new GateBuilder(). + setSerializedIdAndSymbol("ypar"). + setTitle("Parity Control (Y)"). + setBlurb("Includes a qubit's Y observable in the column parity control.\n" + + "Gates in the same column only apply if an odd number of parity controls are satisfied."). + setActualEffectToUpdateFunc(() => {}). + promiseEffectIsStable(). + promiseEffectIsUnitary(). + promiseHasNoNetEffectOnStateVector(). + markAsControlExpecting('parity'). + setSetupCleanupEffectToUpdateFunc( + ctx => { + GateShaders.applyMatrixOperation(ctx, QuarterTurnGates.SqrtXForward._knownMatrix); + parityGatherScatter(ctx, true); + }, + ctx => { + parityGatherScatter(ctx, false); + GateShaders.applyMatrixOperation(ctx, QuarterTurnGates.SqrtXBackward._knownMatrix); + }). + setDrawer(parityDrawer('Y')). + gate; + +Controls.ZParityControl = new GateBuilder(). + setSerializedIdAndSymbol("zpar"). + setTitle("Parity Control (Z)"). + setBlurb("Includes a qubit's Z observable in the column parity control.\n" + + "Gates in the same column only apply if an odd number of parity controls are satisfied."). + promiseHasNoNetEffectOnStateVector(). + markAsControlExpecting('parity'). + setSetupCleanupEffectToUpdateFunc( + ctx => parityGatherScatter(ctx, true), + ctx => parityGatherScatter(ctx, false)). + setActualEffectToUpdateFunc(() => {}). + promiseEffectIsUnitary(). + setDrawer(parityDrawer('Z')). + gate; + Controls.all = [ Controls.Control, Controls.AntiControl, Controls.XAntiControl, Controls.XControl, Controls.YAntiControl, - Controls.YControl + Controls.YControl, + Controls.XParityControl, + Controls.YParityControl, + Controls.ZParityControl, ]; export {Controls} diff --git a/test/CircuitOperationTestUtil.js b/test/CircuitOperationTestUtil.js index ff0c8771..220e4524 100644 --- a/test/CircuitOperationTestUtil.js +++ b/test/CircuitOperationTestUtil.js @@ -208,6 +208,7 @@ function assertThatCircuitMutationActsLikeMatrix_single(updateAction, matrix, fo wireCount, controls, controlsTexture, + controls, trader, new Map()); updateAction(ctx); @@ -255,6 +256,7 @@ function assertThatCircuitUpdateActsLikePermutation(wireCount, updateAction, per wireCount, Controls.NONE, controlsTexture, + Controls.NONE, trader, new Map()); updateAction(ctx); diff --git a/test/base/Util.test.js b/test/base/Util.test.js index 3ba641c5..10ae3e64 100644 --- a/test/base/Util.test.js +++ b/test/base/Util.test.js @@ -54,6 +54,24 @@ suite.test("ceilLg2", () => { assertThat(Util.ceilLg2((1<<20)+1)).isEqualTo(21); }); +suite.test("popcnt", () => { + assertThat(Util.popcnt(-2)).isEqualTo(Math.POSITIVE_INFINITY); + assertThat(Util.popcnt(-1)).isEqualTo(Math.POSITIVE_INFINITY); + assertThat(Util.popcnt(0)).isEqualTo(0); + assertThat(Util.popcnt(1)).isEqualTo(1); + assertThat(Util.popcnt(2)).isEqualTo(1); + assertThat(Util.popcnt(3)).isEqualTo(2); + assertThat(Util.popcnt(4)).isEqualTo(1); + assertThat(Util.popcnt(5)).isEqualTo(2); + assertThat(Util.popcnt(6)).isEqualTo(2); + assertThat(Util.popcnt(7)).isEqualTo(3); + assertThat(Util.popcnt(8)).isEqualTo(1); + assertThat(Util.popcnt(9)).isEqualTo(2); + assertThat(Util.popcnt((1<<20)-1)).isEqualTo(20); + assertThat(Util.popcnt((1<<20))).isEqualTo(1); + assertThat(Util.popcnt((1<<20)+1)).isEqualTo(2); +}); + suite.test("floorLg2", () => { assertThat(Util.floorLg2(0)).isEqualTo(0); assertThat(Util.floorLg2(1)).isEqualTo(0); @@ -105,7 +123,10 @@ suite.test("ceilingPowerOf2", () => { suite.test("powerOfTwoness", () => { assertThat(Util.powerOfTwoness(-2)).isEqualTo(1); assertThat(Util.powerOfTwoness(-1)).isEqualTo(0); + assertThat(Util.powerOfTwoness(-1, 'z')).isEqualTo(0); assertThat(Util.powerOfTwoness(0)).isEqualTo(Math.POSITIVE_INFINITY); + assertThat(Util.powerOfTwoness(0, 'z')).isEqualTo('z'); + assertThat(Util.powerOfTwoness(1, 'z')).isEqualTo(0); assertThat(Util.powerOfTwoness(1)).isEqualTo(0); assertThat(Util.powerOfTwoness(2)).isEqualTo(1); assertThat(Util.powerOfTwoness(3)).isEqualTo(0); diff --git a/test/circuit/CircuitDefinition.test.js b/test/circuit/CircuitDefinition.test.js index 0b7d5cb6..bab7788b 100644 --- a/test/circuit/CircuitDefinition.test.js +++ b/test/circuit/CircuitDefinition.test.js @@ -583,14 +583,17 @@ suite.test("colControls", () => { assertThat(c.colControls(17)).isEqualTo(Controls.bit(0, true)); assertThat(c.colControls(102)).isEqualTo(Controls.NONE); - let c2 = circuit(`--●○○- - -X○●s- - ---s●-`); + let c2 = circuit(`--●○○-P- + -X○●s-P- + ---s●-X-`, ['P', Gates.Controls.ZParityControl]); assertThat(c2.colControls(0)).isEqualTo(Controls.NONE); assertThat(c2.colControls(1)).isEqualTo(Controls.NONE); assertThat(c2.colControls(2)).isEqualTo(new Controls(3, 1)); assertThat(c2.colControls(3)).isEqualTo(new Controls(3, 2)); assertThat(c2.colControls(4)).isEqualTo(new Controls(5, 4)); + assertThat(c2.colControls(5)).isEqualTo(Controls.NONE); + assertThat(c2.colControls(6)).isEqualTo(new Controls(1, 1, 3)); + assertThat(c2.colControls(7)).isEqualTo(Controls.NONE); }); suite.test("locIsControlWireStarter", () => { diff --git a/test/circuit/Serializer.test.js b/test/circuit/Serializer.test.js index a6d8ba0d..9ce23371 100644 --- a/test/circuit/Serializer.test.js +++ b/test/circuit/Serializer.test.js @@ -178,6 +178,7 @@ suite.test("roundTrip_circuitDefinition", () => { const IDS_THAT_SHOULD_BE_KNOWN = [ "•", "◦", "⊕", "⊖", "⊗", "(/)", + "xpar", "ypar", "zpar", "|0⟩⟨0|", "|1⟩⟨1|", "|+⟩⟨+|", "|-⟩⟨-|", "|X⟩⟨X|", "|/⟩⟨/|", "Measure", "XDetector", "YDetector", "ZDetector", diff --git a/test/gates/AllGates.test.js b/test/gates/AllGates.test.js index a59c45df..e6fb9563 100644 --- a/test/gates/AllGates.test.js +++ b/test/gates/AllGates.test.js @@ -53,6 +53,7 @@ let reconstructMatrixFromGateCustomOperation = (gate, time) => { numQubits, Controls.NONE, control, + Controls.NONE, trader, new Map()); gate.customOperation(ctx); @@ -184,6 +185,9 @@ suite.test("knownDoNothingGateFamilies", () => { // Operation modifiers technically do things, but we assign the effects to the operation itself. '•', '◦', + 'xpar', + 'ypar', + 'zpar', 'inputA1', 'inputB1', 'inputR1', diff --git a/test/gates/Controls.test.js b/test/gates/Controls.test.js index e160d032..1e0dac4e 100644 --- a/test/gates/Controls.test.js +++ b/test/gates/Controls.test.js @@ -21,6 +21,11 @@ import {Gates} from "src/gates/AllGates.js" import {Complex} from "src/math/Complex.js" import {Matrix} from "src/math/Matrix.js" +import {Util} from "src/base/Util.js" +import {advanceStateWithCircuit} from "src/circuit/CircuitComputeUtil.js"; +import { + assertThatCircuitUpdateActsLikeMatrix, +} from "test/CircuitOperationTestUtil.js"; let suite = new Suite("Gates.Controls"); @@ -105,3 +110,83 @@ suite.testUsingWebGL('Y-control', () => { assertControlOverlapState(c, 0, [1, i]); assertControlOverlapState(c, 1, [-1, i]); }); + +suite.testUsingWebGL('Z-parity', () => { + assertThatCircuitUpdateActsLikeMatrix( + ctx => advanceStateWithCircuit( + ctx, + new CircuitDefinition(2, [new GateColumn([ + Gates.Controls.ZParityControl, + Gates.HalfTurns.Z + ])]), + false), + Matrix.generateDiagonal(1 << 2, i => i === 3 ? -1 : 1)); + + assertThatCircuitUpdateActsLikeMatrix( + ctx => advanceStateWithCircuit( + ctx, + new CircuitDefinition(4, [new GateColumn([ + Gates.Controls.ZParityControl, + undefined, + Gates.Controls.ZParityControl, + Gates.HalfTurns.Z]) + ]), + false), + Matrix.generateDiagonal(1 << 4, i => Util.popcnt(i & 5) % 2 === 1 && ((i & 8) !== 0) ? -1 : 1)); +}); + +suite.testUsingWebGL('X-parity', () => { + assertThatCircuitUpdateActsLikeMatrix( + ctx => advanceStateWithCircuit( + ctx, + new CircuitDefinition(4, [ + new GateColumn([ + Gates.HalfTurns.H, + Gates.HalfTurns.H, + Gates.HalfTurns.H, + undefined, + ]), + new GateColumn([ + Gates.Controls.XParityControl, + Gates.Controls.XParityControl, + Gates.Controls.XParityControl, + Gates.HalfTurns.Z + ]), + new GateColumn([ + Gates.HalfTurns.H, + Gates.HalfTurns.H, + Gates.HalfTurns.H, + undefined, + ]), + ]), + false), + Matrix.generateDiagonal(1 << 4, i => Util.popcnt(i & 7) % 2 === 1 && ((i & 8) !== 0) ? -1 : 1)); +}); + +suite.testUsingWebGL('X-parity', () => { + assertThatCircuitUpdateActsLikeMatrix( + ctx => advanceStateWithCircuit( + ctx, + new CircuitDefinition(4, [ + new GateColumn([ + Gates.QuarterTurns.SqrtXBackward, + Gates.QuarterTurns.SqrtXBackward, + Gates.QuarterTurns.SqrtXBackward, + undefined, + ]), + new GateColumn([ + Gates.Controls.YParityControl, + Gates.Controls.YParityControl, + Gates.Controls.YParityControl, + Gates.HalfTurns.Z, + ]), + new GateColumn([ + Gates.QuarterTurns.SqrtXForward, + Gates.QuarterTurns.SqrtXForward, + Gates.QuarterTurns.SqrtXForward, + undefined, + ]), + ]), + false), + Matrix.generateDiagonal(1 << 4, i => Util.popcnt(i & 7) % 2 === 1 && ((i & 8) !== 0) ? -1 : 1)); +}); diff --git a/test/gates/Impossible_UniversalNotGate.test.js b/test/gates/Impossible_UniversalNotGate.test.js index d6df27b5..fbd359a9 100644 --- a/test/gates/Impossible_UniversalNotGate.test.js +++ b/test/gates/Impossible_UniversalNotGate.test.js @@ -38,6 +38,7 @@ suite.testUsingWebGL('universalNot', () => { 2, control, controlTex, + control, trader, new Map()); try {