diff --git a/src/circuit/Gate.js b/src/circuit/Gate.js index bafe9020..0e07856e 100644 --- a/src/circuit/Gate.js +++ b/src/circuit/Gate.js @@ -180,6 +180,12 @@ class Gate { * @type {undefined | !function(!int) : !int} */ this.knownBitPermutationFunc = undefined; + /** + * When a permutation factors into sub-permutations over subsets of the bits, this map indexes each bit with + * an id for the group that it belongs to. + * @type {!Array.} + */ + this.knownBitPermutationGroupMasks = undefined; /** * Indicates that a gate is a phasing function, and that it phases each computation basis state by the amount * returned by this function. @@ -278,6 +284,7 @@ class Gate { g.knownPermutationFuncTakingInputs = this.knownPermutationFuncTakingInputs; g.customColumnContextProvider = this.customColumnContextProvider; g.customDisableReasonFinder = this.customDisableReasonFinder; + g.knownBitPermutationGroupMasks = this.knownBitPermutationGroupMasks; return g; } @@ -568,11 +575,12 @@ class GateBuilder { * Provides a function equivalent to how the gate rearranges wires, for checking in tests if the gate's behavior is * correct. * @param {!function(!int) : !int} knownBitPermutationFunc Returns the output of the permutation for a - * given input, assuming the gate is exactly sized to the overall circuit. + * given input, assuming the gate is exactly sized to the overall circuit. * @returns {!GateBuilder} */ setKnownEffectToBitPermutation(knownBitPermutationFunc) { this.gate.knownBitPermutationFunc = knownBitPermutationFunc; + this.gate.knownBitPermutationGroupMasks = permutationGrouping(knownBitPermutationFunc, this.gate.height); this.gate._isDefinitelyUnitary = true; this.gate._stableDuration = Infinity; this.gate._hasNoEffect = false; @@ -928,4 +936,28 @@ class GateBuilder { } } +/** + * @param {!function(!int) : !int} knownBitPermutationFunc Returns the output of the permutation for a + * given input, assuming the gate is exactly sized to the overall circuit. + * @param {!int} height + * @returns {!Array.} + */ +function permutationGrouping(knownBitPermutationFunc, height) { + let seen = new Set(); + let result = []; + for (let i = 0; i < height; i++) { + let mask = 0; + let j = i; + while (!seen.has(j)) { + seen.add(j); + mask |= 1 << j; + j = knownBitPermutationFunc(j); + } + if (mask !== 0) { + result.push(mask); + } + } + return result; +} + export {Gate, GateBuilder} diff --git a/src/circuit/GateColumn.js b/src/circuit/GateColumn.js index fbaca1db..f809ebb8 100644 --- a/src/circuit/GateColumn.js +++ b/src/circuit/GateColumn.js @@ -110,6 +110,21 @@ class GateColumn { return false; } + /** + * @returns {!int} + */ + controlMask() { + let mask = 0; + for (let i = 0; i < this.gates.length; i++) { + if (this.gates[i] !== undefined && + this.gates[i].definitelyHasNoEffect() && + this.gates[i].isControl()) { + mask |= 1 << i; + } + } + return mask; + } + /** * @param {!int} inputMeasureMask * @param {!int} row @@ -170,25 +185,41 @@ class GateColumn { let g = this.gates[row]; let mask = ((1 << g.height) - 1) << row; let maskMeasured = mask & inputMeasureMask; - if (maskMeasured !== 0) { + if (maskMeasured !== 0 && g.knownBitPermutationFunc === undefined) { // Don't try to superpose measured qubits. if (g.effectMightCreateSuperpositions()) { return "no\nremix\n(sorry)"; } + // Don't try to mix measured and coherent qubits, or coherently mix measured qubits. if (g.effectMightPermutesStates()) { - // Only permutations that respect bit boundaries can be performed on mixed qubits. - if (maskMeasured !== mask && (g.knownBitPermutationFunc === undefined || - this.hasMeasuredControl(inputMeasureMask))) { + if (maskMeasured !== mask || this.hasCoherentControl(inputMeasureMask)) { return "no\nremix\n(sorry)"; } + } + } - // Permutations affecting classical states can't have quantum controls. - if (this.hasCoherentControl(inputMeasureMask)) { + // Check permutation subgroups for bad mixing of measured and coherent qubits. + if (g.knownBitPermutationGroupMasks !== undefined) { + for (let maskGroup of g.knownBitPermutationGroupMasks) { + let isSingleton = ((maskGroup - 1) & maskGroup) === 0; + if (isSingleton) { + continue; + } + + maskGroup <<= row; + let hasCoherentQubits = (maskGroup & inputMeasureMask) !== maskGroup; + let hasMeasuredQubits = (maskGroup & inputMeasureMask) !== 0; + let coherentControl = this.hasCoherentControl(inputMeasureMask); + let controlled = this.hasControl(inputMeasureMask); + let coherentControlledMixingOfMeasured = hasMeasuredQubits && coherentControl; + let controlledMixingOfCoherentAndMeasured = hasCoherentQubits && hasMeasuredQubits && controlled; + if (coherentControlledMixingOfMeasured || controlledMixingOfCoherentAndMeasured) { return "no\nremix\n(sorry)"; } } } + return undefined; } diff --git a/test/circuit/CircuitDefinition.test.js b/test/circuit/CircuitDefinition.test.js index 66970dcf..36d076c9 100644 --- a/test/circuit/CircuitDefinition.test.js +++ b/test/circuit/CircuitDefinition.test.js @@ -901,6 +901,66 @@ suite.test("gateAtLocIsDisabledReason", () => { bad(1, 0, `MR`, ['R', Gates.Detectors.XDetectControlClear]); bad(1, 0, `MR`, ['R', Gates.Detectors.YDetectControlClear]); good(1, 0, `MR`, ['R', Gates.Detectors.ZDetectControlClear]); + + // Permutation sub-groups. + good(2, 0, `--P- + --/- + --/- + --/- + H-●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + good(2, 0, `-MP- + --/- + --/- + -M/- + H-●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + good(2, 0, `--P- + --/- + --/- + -M/- + HM●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + good(2, 0, `-MP- + --/- + --/- + -M/- + HM●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + good(2, 0, `--P- + -M/- + -M/- + --/- + HM●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + good(2, 0, `-MP- + -M/- + -M/- + -M/- + HM●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + bad(2, 0, `--P- + -M/- + -M/- + --/- + H-●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + bad(2, 0, `--P- + -M/- + --/- + --/- + HM●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + + // Non-trivial subgroup. + good(2, 0, `--P- + -M/- + -M/- + --/- + -M/- + --/- + --/- + HM●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); + bad(2, 0, `--P- + -M/- + --/- + -M/- + -M/- + --/- + --/- + HM●-`, ['P', Gates.InterleaveBitsGates.InterleaveBitsGateFamily]); }); suite.test("gateAtLocIsDisabledReason_controls", () => {