Skip to content

Commit

Permalink
Add parity controls
Browse files Browse the repository at this point in the history
- Add rawControls property to CircuitEvalContext
- Add Util.popcnt
- Add default output for Util.powerOfTwoness
- Add Parity section to toolbox
  • Loading branch information
Strilanc committed Aug 12, 2019
1 parent 1653c6b commit 2d0e00a
Show file tree
Hide file tree
Showing 16 changed files with 344 additions and 26 deletions.
33 changes: 26 additions & 7 deletions src/base/Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)));
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/circuit/CircuitComputeUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ function _extractStateStatsNeededByCircuitColumn(
circuitDefinition.numWires,
ctx.controls,
ctx.controlsTexture,
ctx.controls,
ctx.stateTrader,
Util.mergeMaps(
ctx.customContextFromGates,
Expand Down Expand Up @@ -185,6 +186,7 @@ function _advanceStateWithCircuitDefinitionColumn(
ctx.wireCount,
ctx.controls,
ctx.controlsTexture,
controls,
trader,
colContext);
let mainCtx = new CircuitEvalContext(
Expand All @@ -193,6 +195,7 @@ function _advanceStateWithCircuitDefinitionColumn(
ctx.wireCount,
controls,
controlTex,
controls,
trader,
colContext);

Expand Down
20 changes: 16 additions & 4 deletions src/circuit/CircuitDefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/circuit/CircuitEvalContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.<!string, *>} customContextFromGates
*/
Expand All @@ -34,6 +36,7 @@ class CircuitEvalContext {
wireCount,
controls,
controlsTexture,
rawControls,
stateTrader,
customContextFromGates) {
/** @type {!number} */
Expand All @@ -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} */
Expand Down Expand Up @@ -75,6 +80,7 @@ class CircuitEvalContext {
this.wireCount,
this.controls,
this.controlsTexture,
this.rawControls,
this.stateTrader,
this.customContextFromGates);
}
Expand Down
1 change: 1 addition & 0 deletions src/circuit/CircuitStats.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ class CircuitStats {
numWires,
Controls.NONE,
controlTex,
Controls.NONE,
stateTrader,
new Map()),
circuitDefinition,
Expand Down
36 changes: 29 additions & 7 deletions src/circuit/Controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand All @@ -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;
}

/**
Expand All @@ -68,12 +77,20 @@ class Controls {
return "No Controls";
}

return "Controls: ...__" + Seq.naturals().
takeWhile(i => (1<<i) <= this.inclusionMask).
map(this.desiredValueFor.bind(this)).
let range = Seq.naturals().takeWhile(i => (1<<i) <= (this.inclusionMask | this.parityMask));
let result = "Controls: ...__" + range.
map(e => 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;
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -125,7 +146,8 @@ class Controls {
shift(offset) {
return new Controls(
this.inclusionMask << offset,
this.desiredValueMask << offset)
this.desiredValueMask << offset,
this.parityMask << offset)
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/circuit/Gate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -417,7 +418,7 @@ class Gate {
}

/**
* @returns {undefined|!boolean}
* @returns {undefined|!string|!boolean}
*/
controlBit() {
return this._controlBit;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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}
Expand Down
8 changes: 8 additions & 0 deletions src/gates/AllGates.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,14 @@ Gates.TopToolboxGroups = [
Detectors.XDetector, Detectors.XDetectControlClear,
]
},
{
hint: "Parity",
gates: [
Controls.ZParityControl, undefined,
Controls.YParityControl, undefined,
Controls.XParityControl, undefined,
]
},
];

/** @type {!Array<!{hint: !string, gates: !Array<undefined|!Gate>}>} */
Expand Down
Loading

0 comments on commit 2d0e00a

Please sign in to comment.