From b686996eb9a89cfb68f47c696229ee9edf93693e Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Sun, 16 Jul 2017 14:45:08 -0700 Subject: [PATCH 1/5] Fix typo in right-rotate gate's setup --- src/gates/CycleBitsGates.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gates/CycleBitsGates.js b/src/gates/CycleBitsGates.js index 0d3f7ff7..64538581 100644 --- a/src/gates/CycleBitsGates.js +++ b/src/gates/CycleBitsGates.js @@ -71,8 +71,8 @@ CycleBitsGates.CycleBitsFamily = Gate.buildFamily(2, 16, (span, builder) => buil CycleBitsGates.ReverseCycleBitsFamily = Gate.buildFamily(2, 16, (span, builder) => builder. setSerializedId(">>" + span). setSymbol(">>>"). - setSymbol("Right Rotate"). - setTitle("Rotates bits upward."). + setTitle("Right Rotate"). + setBlurb("Rotates bits upward."). setDrawer(cyclePainter(true)). setTooltipMatrixFunc(() => makeCycleBitsMatrix(-1, span)). setActualEffectToShaderProvider(ctx => cycleBitsShader(ctx, span, -1)). From 1dfaf6396b6a94dfd665e1fd7c2c52944ea1c3d8 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 18 Sep 2017 19:07:57 -0700 Subject: [PATCH 2/5] Use circled characters in X/Y post-selection gate symbols --- src/gates/PostSelectionGates.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gates/PostSelectionGates.js b/src/gates/PostSelectionGates.js index 014e0517..54650f87 100644 --- a/src/gates/PostSelectionGates.js +++ b/src/gates/PostSelectionGates.js @@ -55,7 +55,7 @@ PostSelectionGates.PostSelectOn = new GateBuilder(). /** @type {!Gate} */ PostSelectionGates.PostSelectAntiX = new GateBuilder(). setSerializedId("|+⟩⟨+|"). // The +/- drawing convention was switched, but the serialized id must stay the same. - setSymbol("|-⟩⟨-|"). + setSymbol("|⊖⟩⟨⊖|"). setTitle("Postselect X-Off"). setBlurb("Keeps ON+OFF states, discards/retries ON-OFF states."). setDrawer(POST_SELECT_DRAWER). @@ -65,7 +65,7 @@ PostSelectionGates.PostSelectAntiX = new GateBuilder(). /** @type {!Gate} */ PostSelectionGates.PostSelectX = new GateBuilder(). setSerializedId("|-⟩⟨-|"). // The +/- drawing convention was switched, but the serialized id must stay the same. - setSymbol("|+⟩⟨+|"). + setSymbol("|⊕⟩⟨⊕|"). setTitle("Postselect X-On"). setBlurb("Keeps ON-OFF states, discards/retries ON+OFF states."). setDrawer(POST_SELECT_DRAWER). @@ -75,7 +75,7 @@ PostSelectionGates.PostSelectX = new GateBuilder(). /** @type {!Gate} */ PostSelectionGates.PostSelectAntiY = new GateBuilder(). setSerializedId("|X⟩⟨X|"). // The cross/slash convention was switched, but the serialized id must stay the same. - setSymbol("|/⟩⟨/|"). + setSymbol("|⊘⟩⟨⊘|"). setTitle("Postselect Y-Off"). setBlurb("Keeps ON+iOFF states, discards ON-iOFF states."). setDrawer(POST_SELECT_DRAWER). @@ -85,7 +85,7 @@ PostSelectionGates.PostSelectAntiY = new GateBuilder(). /** @type {!Gate} */ PostSelectionGates.PostSelectY = new GateBuilder(). setSerializedId("|/⟩⟨/|"). // The cross/slash convention was switched, but the serialized id must stay the same. - setSymbol("|X⟩⟨X|"). + setSymbol("|⊗⟩⟨⊗|"). setTitle("Postselect Y-On"). setBlurb("Keeps ON-iOFF states, discards/retries ON+iOFF states."). setDrawer(POST_SELECT_DRAWER). From 91d9e7e3488f9866f3b84605e22770e63ce1c18e Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 18 Sep 2017 19:09:48 -0700 Subject: [PATCH 3/5] Bump version to 2.1 --- html/quirk.template.html | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/html/quirk.template.html b/html/quirk.template.html index ee62b76a..4b9a664b 100644 --- a/html/quirk.template.html +++ b/html/quirk.template.html @@ -35,7 +35,7 @@       - Version 2.0 + Version 2.1 diff --git a/package.json b/package.json index 32f0fccb..9d0d6804 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "title": "Quirk", "description": "A drag-and-drop toy for exploring and understanding small quantum circuits.", "license": "Apache-2.0", - "version": "2.0.0", + "version": "2.1.0", "homepage": "https://github.com/Strilanc/Quirk", "bugs": { "url": "https://github.com/Strilanc/Quirk/issues" From 54599d1682fdc7cdbfeb80ffe10eeebb97cc8ca6 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 19 Sep 2017 11:05:31 -0700 Subject: [PATCH 4/5] Fixed dynamic phase gradient gate decohering qubits due to precision loss --- src/gates/MultiplyAccumulateGates.js | 2 +- src/gates/PhaseGradientGates.js | 21 ++++++++++++-- test/circuit/CircuitStats.test.js | 41 ++++++++++++++++++++++++---- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/gates/MultiplyAccumulateGates.js b/src/gates/MultiplyAccumulateGates.js index 1016d9d4..933739b1 100644 --- a/src/gates/MultiplyAccumulateGates.js +++ b/src/gates/MultiplyAccumulateGates.js @@ -154,4 +154,4 @@ MultiplyAccumulateGates.all = [ ...MultiplyAccumulateGates.SquareSubtractInputFamily.all, ]; -export {MultiplyAccumulateGates, BIG_MUL_MOD_SHADER_CODE} +export {MultiplyAccumulateGates, BIG_MUL_MOD_SHADER_CODE, MUL_STEP} diff --git a/src/gates/PhaseGradientGates.js b/src/gates/PhaseGradientGates.js index 23c7e02a..6c1a674e 100644 --- a/src/gates/PhaseGradientGates.js +++ b/src/gates/PhaseGradientGates.js @@ -12,15 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {Config} from "src/Config.js"; import {Gate} from "src/circuit/Gate.js" import {GatePainting} from "src/draw/GatePainting.js" import {ketArgs, ketShaderPhase} from "src/circuit/KetShaderUtil.js" +import {MUL_STEP} from "src/gates/MultiplyAccumulateGates.js" import {WglArg} from "src/webgl/WglArg.js" const PHASE_GRADIENT_SHADER = ketShaderPhase( - 'uniform float factor;', ` - float angle = out_id * factor; + uniform float factor; + + /// Scales an angle by an integer factor. + /// Performs the multiplication gradually, to avoid losing precision. + float angle_mul(float base_angle, float whole_factor) { + float result = 0.0; + for (int k = 0; k < ${Math.ceil(Config.MAX_WIRE_COUNT/MUL_STEP)}; k++) { + result += base_angle * mod(whole_factor, ${1< CircuitDefinition.fromTextDiagram(new Ma ['-', undefined], ['+', undefined], ['|', undefined], - ['/', undefined] + ['/', null] ]), diagram); suite.testUsingWebGL("empty", () => { @@ -117,7 +118,7 @@ suite.testUsingWebGL('incoherent-amplitude-display', () => { suite.testUsingWebGL('coherent-amplitude-display', () => { let c = circuit(`-H-•-a/-- ---X-//-- - -H-------`, ['a', Gates.Displays.AmplitudeDisplayFamily.ofSize(2)]); + -H-------`, ['a', Gates.Displays.AmplitudeDisplayFamily]); let stats = CircuitStats.fromCircuitAtTime(c, 0); assertThat(stats.qubitDensityMatrix(Infinity, 0)).isApproximatelyEqualTo(Matrix.square(0.5, 0, 0, 0.5)); assertThat(stats.qubitDensityMatrix(Infinity, 1)).isApproximatelyEqualTo(Matrix.square(0.5, 0, 0, 0.5)); @@ -141,7 +142,7 @@ suite.testUsingWebGL('conditional-bloch-display', () => { suite.testUsingWebGL('probability-display', () => { let c = circuit(`-H-•-%- - ---X-/-`, ['%', Gates.Displays.ProbabilityDisplayFamily.ofSize(2)]); + ---X-/-`, ['%', Gates.Displays.ProbabilityDisplayFamily]); let stats = CircuitStats.fromCircuitAtTime(c, 0); assertThat(stats.qubitDensityMatrix(Infinity, 0)).isApproximatelyEqualTo(Matrix.square(0.5, 0, 0, 0.5)); assertThat(stats.customStatsForSlot(5, 0)).isApproximatelyEqualTo( @@ -151,7 +152,7 @@ suite.testUsingWebGL('probability-display', () => { suite.testUsingWebGL('controlled-multi-probability-display', () => { let c = circuit(`---◦- -H-%- - ---/-`, ['%', Gates.Displays.ProbabilityDisplayFamily.ofSize(2)]); + ---/-`, ['%', Gates.Displays.ProbabilityDisplayFamily]); let stats = CircuitStats.fromCircuitAtTime(c, 0); assertThat(stats.customStatsForSlot(3, 1)).isApproximatelyEqualTo( Matrix.col(0.5, 0.5, 0, 0)); @@ -159,7 +160,7 @@ suite.testUsingWebGL('controlled-multi-probability-display', () => { suite.testUsingWebGL('density-display', () => { let c = circuit(`-d/- - -//-`, ['d', Gates.Displays.DensityMatrixDisplayFamily.ofSize(2)]); + -//-`, ['d', Gates.Displays.DensityMatrixDisplayFamily]); let stats = CircuitStats.fromCircuitAtTime(c, 0); assertThat(stats.customStatsForSlot(1, 0)).isApproximatelyEqualTo( Matrix.square( @@ -172,7 +173,7 @@ suite.testUsingWebGL('density-display', () => { suite.testUsingWebGL('shifted-density-display', () => { let c = circuit(`---- -d/- - -//-`, ['d', Gates.Displays.DensityMatrixDisplayFamily.ofSize(2)]); + -//-`, ['d', Gates.Displays.DensityMatrixDisplayFamily]); let stats = CircuitStats.fromCircuitAtTime(c, 0); assertThat(stats.customStatsForSlot(1, 1)).isApproximatelyEqualTo( Matrix.square( @@ -262,3 +263,31 @@ suite.testUsingWebGL('survival-rates-controlled-postselection', () => { assertThat(stats.survivalRate(13)).isApproximatelyEqualTo(0.5); assertThat(stats.survivalRate(14)).isApproximatelyEqualTo(0.5); }); + +suite.testUsingWebGL('dynamic-phase-gradient-keeps-qubits-coherent', () => { + let stats = CircuitStats.fromCircuitAtTime( + circuit(`-H-P- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/- + -H-/-`, ['P', Gates.PhaseGradientGates.DynamicPhaseGradientFamily]), + 0.9); + + // Check coherence of each qubit. + for (let i = 0; i < 16; i++) { + let [x, y, z] = stats.qubitDensityMatrix(Infinity, i).qubitDensityMatrixToBlochVector(); + let r = x*x + y*y + z*z; + assertThat(r).withInfo({i, x, y, z}).isApproximatelyEqualTo(1); + } +}); From ad5664109c74b9c242c2b05b56ba45852491396f Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 19 Sep 2017 11:16:10 -0700 Subject: [PATCH 5/5] Refactored ketShaderPhase to use a shader-method returning an angle --- src/circuit/KetShaderUtil.js | 27 ++++++++++++++++++-------- src/gates/FourierTransformGates.js | 3 +-- src/gates/ParametrizedRotationGates.js | 3 +-- src/gates/PhaseGradientGates.js | 3 +-- test/circuit/KetShaderUtil.test.js | 2 +- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/circuit/KetShaderUtil.js b/src/circuit/KetShaderUtil.js index 882e2d60..0bf27955 100644 --- a/src/circuit/KetShaderUtil.js +++ b/src/circuit/KetShaderUtil.js @@ -86,20 +86,31 @@ const ketShaderPermute = (head, body, span=null) => ketShader( span); /** - * @param {!String} head - * @param {!String} body - * @param {null|!int=null} span + * Returns a shader that multiplies each of the amplitudes in a superposition by computed phase factors. + * + * @param {!String} head Header code defining shader methods, uniforms, etc. + * @param {!String} body The body of a shader method returning the number of radians to phase by. + * @param {null|!int=null} span The number of qubits this operation applies to, if known ahead of time. * @return {!{withArgs: !function(args: ...!WglArg) : !WglConfiguredShader}} */ const ketShaderPhase = (head, body, span=null) => ketShader( - head + `vec2 _ketgen_phase_for(float out_id) { ${body} }`, - 'return cmul(amp, _ketgen_phase_for(out_id));', + `${head} + float _ketgen_phase_for(float out_id) { + ${body} + } + `, + ` + float angle = _ketgen_phase_for(out_id); + return cmul(amp, vec2(cos(angle), sin(angle))); + `, span); /** - * @param {!CircuitEvalContext} ctx - * @param {undefined|!int=undefined} span - * @param {undefined|!Array.} input_letters + * Determines some arguments to give to a shader produced by one of the ketShader methods. + * + * @param {!CircuitEvalContext} ctx The context in which the ket shader is being applied. + * @param {undefined|!int=undefined} span The number of qubits this shader applies to (if wasn't known ahead of time). + * @param {undefined|!Array.} input_letters The input gates that this shader cares about. * @returns {!Array.} */ function ketArgs(ctx, span=undefined, input_letters=[]) { diff --git a/src/gates/FourierTransformGates.js b/src/gates/FourierTransformGates.js index 3f3182b3..c7d36088 100644 --- a/src/gates/FourierTransformGates.js +++ b/src/gates/FourierTransformGates.js @@ -35,8 +35,7 @@ const CONTROLLED_PHASE_GRADIENT_SHADER = ketShaderPhase( ` float hold = floor(out_id * 2.0 / span); float step = mod(out_id, span / 2.0); - float angle = hold * step * factor * 6.2831853071795864769 / span; - return vec2(cos(angle), sin(angle)); + return hold * step * factor * 6.2831853071795864769 / span; `); const FOURIER_TRANSFORM_MATRIX_MAKER = span => diff --git a/src/gates/ParametrizedRotationGates.js b/src/gates/ParametrizedRotationGates.js index 4f4b2e1d..fabfa6b3 100644 --- a/src/gates/ParametrizedRotationGates.js +++ b/src/gates/ParametrizedRotationGates.js @@ -71,8 +71,7 @@ const Z_TO_A_SHADER = ketShaderPhase( ${ketInputGateShaderCode('A')} `, ` - float angle = read_input_A() * out_id * factor / _gen_input_span_A; - return vec2(cos(angle), sin(angle)); + return read_input_A() * out_id * factor / _gen_input_span_A; `); ParametrizedRotationGates.XToA = new GateBuilder(). diff --git a/src/gates/PhaseGradientGates.js b/src/gates/PhaseGradientGates.js index 6c1a674e..80abfc1f 100644 --- a/src/gates/PhaseGradientGates.js +++ b/src/gates/PhaseGradientGates.js @@ -37,8 +37,7 @@ const PHASE_GRADIENT_SHADER = ketShaderPhase( } `, ` - float angle = angle_mul(factor, out_id); - return vec2(cos(angle), sin(angle)); + return angle_mul(factor, out_id); `); let PhaseGradientGates = {}; diff --git a/test/circuit/KetShaderUtil.test.js b/test/circuit/KetShaderUtil.test.js index 25dda0ba..5f222ee4 100644 --- a/test/circuit/KetShaderUtil.test.js +++ b/test/circuit/KetShaderUtil.test.js @@ -49,7 +49,7 @@ suite.testUsingWebGL("ketShaderPermute", () => { suite.testUsingWebGL("ketShaderPhase", () => { let shader = ketShaderPhase( '', - 'return vec2(cos(out_id/10.0), sin(out_id/10.0));', + 'return out_id/10.0;', 3); assertThatCircuitShaderActsLikeMatrix( ctx => shader.withArgs(...ketArgs(ctx)),