From 58f57197495b1d57734c87314b6aaf8f0588d706 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Sat, 23 Mar 2019 22:50:45 -0700 Subject: [PATCH] Testing more cases of AmplitudesDisplay - Fixed reference vector comparison in several ways --- src/gates/AmplitudeDisplay.js | 38 ++++------ src/webgl/Shaders.js | 12 ++++ test/gates/AmplitudeDisplay.test.js | 104 +++++++++++++++++++++------- test/webgl/Shaders.test.js | 6 ++ 4 files changed, 113 insertions(+), 47 deletions(-) diff --git a/src/gates/AmplitudeDisplay.js b/src/gates/AmplitudeDisplay.js index b7ab542a..414b1438 100644 --- a/src/gates/AmplitudeDisplay.js +++ b/src/gates/AmplitudeDisplay.js @@ -82,23 +82,23 @@ function amplitudeDisplayStatTextures(stateKet, controls, controlsTexture, range // Compute the dot product of the largest vector against every other vector. trader.shadeAndTrade( - small_input => POINTWISE_CMUL_SHADER(small_input, ketJustAfterCycle), + small_input => POINTWISE_CMUL_CONJ_SHADER(small_input, ketJustAfterCycle), WglTexturePool.takeVec2Tex(involvedQubits)); ketJustAfterCycle.deallocByDepositingInPool("ketJustAfterCycle in makeAmplitudeSpanPipeline"); - for (let k = 0; k < broadcastQubits; k++) { - trader.shadeHalveAndTrade(Shaders.sumFoldVec2); + for (let k = 0; k < rangeLength; k++) { + trader.shadeHalveAndTrade(Shaders.sumFoldVec2Adjacents); } // Sum up the magnitudes of the dot products to get a quality metric for how well the largest vector worked. - trader.shadeAndTrade(AMPS_TO_ABS_MAGS_SHADER, WglTexturePool.takeVecFloatTex(rangeLength)); - for (let k = 0; k < rangeLength; k++) { + trader.shadeAndTrade(AMPS_TO_SQUARED_MAGS_SHADER, WglTexturePool.takeVecFloatTex(broadcastQubits)); + for (let k = 0; k < broadcastQubits; k++) { trader.shadeHalveAndTrade(Shaders.sumFoldFloat); } if (currentShaderCoder().float.needRearrangingToBeInVec4Format) { trader.shadeHalveAndTrade(Shaders.packFloatIntoVec4); } - let quality = trader.currentTexture; + let denormalizedQuality = trader.currentTexture; trader.currentTexture = rawKet; if (currentShaderCoder().vec2.needRearrangingToBeInVec4Format) { @@ -106,7 +106,7 @@ function amplitudeDisplayStatTextures(stateKet, controls, controlsTexture, range } let ket = trader.currentTexture; - return [ket, quality, incoherentKet]; + return [ket, denormalizedQuality, incoherentKet]; } /** @@ -117,7 +117,7 @@ function amplitudeDisplayStatTextures(stateKet, controls, controlsTexture, range */ function processOutputs(span, pixelGroups, circuitDefinition) { let [ketPixels, qualityPixels, rawIncoherentKetPixels] = pixelGroups; - let quality = qualityPixels[0]; + let denormalizedQuality = qualityPixels[0]; let n = 1 << span; let w = n === 2 ? 2 : 1 << Math.floor(Math.round(Math.log2(n))/2); let h = n/w; @@ -127,13 +127,14 @@ function processOutputs(span, pixelGroups, circuitDefinition) { for (let e of ketPixels) { unity += e*e; } + let quality = denormalizedQuality / unity; let incoherentKetPixels = new Float32Array(w * h * 2); let incoherentUnity = 0; for (let i = 0; i < n; i++) { incoherentUnity += rawIncoherentKetPixels[i]; incoherentKetPixels[i << 1] = Math.sqrt(rawIncoherentKetPixels[i]); } - if (isNaN(unity) || unity < 0.000001) { + if (isNaN(incoherentUnity) || incoherentUnity < 0.000001) { return { quality: 0.0, ket: Matrix.zero(w, h).times(NaN), @@ -191,14 +192,6 @@ const AMPS_TO_SQUARED_MAGS_SHADER = makePseudoShaderWithInputsAndOutputAndCode( return dot(ri, ri); }`); -const AMPS_TO_ABS_MAGS_SHADER = makePseudoShaderWithInputsAndOutputAndCode( - [Inputs.vec2('input')], - Outputs.float(), - `float outputFor(float k) { - vec2 ri = read_input(k); - return sqrt(dot(ri, ri)); - }`); - const MAGS_TO_INDEXED_MAGS_SHADER = makePseudoShaderWithInputsAndOutputAndCode( [Inputs.float('input')], Outputs.vec2(), @@ -222,17 +215,17 @@ const LOOKUP_KET_AT_INDEXED_MAG_SHADER = makePseudoShaderWithInputsAndOutputAndC return read_input(k + read_indexed_mag(0.0).x * len_output()); }`); -const POINTWISE_CMUL_SHADER = makePseudoShaderWithInputsAndOutputAndCode( +const POINTWISE_CMUL_CONJ_SHADER = makePseudoShaderWithInputsAndOutputAndCode( [Inputs.vec2('small_input'), Inputs.vec2('large_input')], Outputs.vec2(), ` - vec2 cmul(vec2 c1, vec2 c2) { - return mat2(c1.x, c1.y, -c1.y, c1.x) * c2; + vec2 cmul_conj(vec2 c1, vec2 c2) { + return mat2(c1.x, -c1.y, c1.y, c1.x) * c2; } vec2 outputFor(float k) { vec2 in1 = read_small_input(floor(mod(k + 0.5, len_small_input()))); vec2 in2 = read_large_input(k); - return cmul(in1, in2); + return cmul_conj(in1, in2); } `); @@ -367,10 +360,9 @@ let AmplitudeDisplayFamily = Gate.buildFamily(1, 16, (span, builder) => builder. export { AmplitudeDisplayFamily, AMPS_TO_SQUARED_MAGS_SHADER, - AMPS_TO_ABS_MAGS_SHADER, MAGS_TO_INDEXED_MAGS_SHADER, FOLD_MAX_INDEXED_MAG_SHADER, LOOKUP_KET_AT_INDEXED_MAG_SHADER, - POINTWISE_CMUL_SHADER, + POINTWISE_CMUL_CONJ_SHADER, amplitudeDisplayStatTextures, }; diff --git a/src/webgl/Shaders.js b/src/webgl/Shaders.js index 5e12c5b2..1400f3a6 100644 --- a/src/webgl/Shaders.js +++ b/src/webgl/Shaders.js @@ -182,6 +182,18 @@ Shaders.sumFoldVec2 = makePseudoShaderWithInputsAndOutputAndCode( return read_input(k) + read_input(k + len_output()); }`); +/** + * Adds the odd half of its input to the even half of its input. + * @param {!WglTexture} inp + * @returns {!WglConfiguredShader} + */ +Shaders.sumFoldVec2Adjacents = makePseudoShaderWithInputsAndOutputAndCode( + [Inputs.vec2('input')], + Outputs.vec2(), + `vec2 outputFor(float k) { + return read_input(k*2.0) + read_input(k*2.0 + 1.0); + }`); + /** * Adds the second half of its input into the first half. * @param {!WglTexture} inp diff --git a/test/gates/AmplitudeDisplay.test.js b/test/gates/AmplitudeDisplay.test.js index c0e3c753..79a096b9 100644 --- a/test/gates/AmplitudeDisplay.test.js +++ b/test/gates/AmplitudeDisplay.test.js @@ -16,16 +16,18 @@ import {Suite, assertThat} from "test/TestUtil.js" import { amplitudeDisplayStatTextures, AMPS_TO_SQUARED_MAGS_SHADER, - AMPS_TO_ABS_MAGS_SHADER, MAGS_TO_INDEXED_MAGS_SHADER, FOLD_MAX_INDEXED_MAG_SHADER, LOOKUP_KET_AT_INDEXED_MAG_SHADER, - POINTWISE_CMUL_SHADER, + POINTWISE_CMUL_CONJ_SHADER, } from "src/gates/AmplitudeDisplay.js" import {Complex} from "src/math/Complex.js" import {Controls} from "src/circuit/Controls.js" +import {CircuitDefinition} from "src/circuit/CircuitDefinition.js" +import {CircuitStats} from "src/circuit/CircuitStats.js" import {CircuitShaders} from "src/circuit/CircuitShaders.js" +import {Serializer} from "src/circuit/Serializer.js" import {seq} from "src/base/Seq.js" import {Shaders} from "src/webgl/Shaders.js" import {currentShaderCoder} from "src/webgl/ShaderCoders.js" @@ -48,22 +50,6 @@ suite.testUsingWebGL("AMPS_TO_SQUARED_MAGS_SHADER", () => { input.deallocByDepositingInPool(); }); -suite.testUsingWebGL("AMPS_TO_ABS_MAGS_SHADER", () => { - let input = Shaders.vec2Data(new Float32Array([ - 1,0, - 3,4, - -1,1, - 0,0.5 - ])).toVec2Texture(2); - assertThat(AMPS_TO_ABS_MAGS_SHADER(input).readVecFloatOutputs(2)).isApproximatelyEqualTo(new Float32Array([ - 1, - 5, - Math.sqrt(2), - 0.5, - ]), 0.001); - input.deallocByDepositingInPool(); -}); - suite.testUsingWebGL("MAGS_TO_INDEXED_MAGS_SHADER", () => { let input = Shaders.floatData(new Float32Array([ 2, @@ -111,7 +97,7 @@ suite.testUsingWebGL("LOOKUP_KET_AT_INDEXED_MAG_SHADER", () => { input.deallocByDepositingInPool(); }); -suite.testUsingWebGL("POINTWISE_CMUL_SHADER", () => { +suite.testUsingWebGL("POINTWISE_CMUL_CONJ_SHADER", () => { let small_input = Shaders.vec2Data(new Float32Array([ 1, 2, ])).toVec2Texture(0); @@ -121,11 +107,11 @@ suite.testUsingWebGL("POINTWISE_CMUL_SHADER", () => { 0, 1, 4, 9, 16, 25, 36, 49, 1, 2, 4, 8, 16, 32, 64, 128, ])).toVec2Texture(4); - assertThat(POINTWISE_CMUL_SHADER(small_input, large_input).readVec2Outputs(4)).isApproximatelyEqualTo(new Float32Array([ - -2, 1, -4, 7, -6, 13, -8, 19, - -4, 7, -9, 17, -15, 35, -21, 53, - -2, 1, -14, 17, -34, 57, -62, 121, - -3, 4, -12, 16, -48, 64, -192, 256 + assertThat(POINTWISE_CMUL_CONJ_SHADER(small_input, large_input).readVec2Outputs(4)).isApproximatelyEqualTo(new Float32Array([ + 2, 1, 8, -1, 14, -3, 20, -5, + 8, -1, 19, -3, 37, -9, 55, -15, + 2, 1, 22, 1, 66, -7, 134, -23, + 5, 0, 20, 0, 80, 0, 320, 0 ]), 0.001); small_input.deallocByDepositingInPool(); large_input.deallocByDepositingInPool(); @@ -159,3 +145,73 @@ suite.testUsingWebGL("makeAmplitudeSpanPipeline_coherent", () => { qualityData.deallocByDepositingInPool(); incoherentKetData.deallocByDepositingInPool(); }); + +suite.testUsingWebGL("AmplitudesDisplayWithOtherQubit_Minus", () => { + let stats = CircuitStats.fromCircuitAtTime( + Serializer.fromJson(CircuitDefinition, {cols:[["Amps2"]],init:[0,0,"-"]}), + 0); + let out = stats.toReadableJson(); + assertThat(out.displays[0].data.ket).isApproximatelyEqualTo([ + {real: 1, imag: 0}, + {real: 0, imag: 0}, + {real: 0, imag: 0}, + {real: 0, imag: 0}, + ]); + assertThat(out.displays[0].data.coherence_measure).isApproximatelyEqualTo(1); +}); + +suite.testUsingWebGL("AmplitudesDisplayWithOtherQubit_i", () => { + let stats = CircuitStats.fromCircuitAtTime( + Serializer.fromJson(CircuitDefinition, {cols:[["Amps2"]],init:[0,0,"i"]}), + 0); + let out = stats.toReadableJson(); + assertThat(out.displays[0].data.ket).isApproximatelyEqualTo([ + {real: 1, imag: 0}, + {real: 0, imag: 0}, + {real: 0, imag: 0}, + {real: 0, imag: 0}, + ]); + assertThat(out.displays[0].data.coherence_measure).isApproximatelyEqualTo(1); +}); + +suite.testUsingWebGL("AmplitudesDisplayWithOtherQubit_own_i", () => { + let stats = CircuitStats.fromCircuitAtTime( + Serializer.fromJson(CircuitDefinition, {cols:[["Amps2"]],init:["i",0,1]}), + 0); + let out = stats.toReadableJson(); + assertThat(out.displays[0].data.ket).isApproximatelyEqualTo([ + {real: Math.sqrt(0.5), imag: 0}, + {real: 0, imag: Math.sqrt(0.5)}, + {real: 0, imag: 0}, + {real: 0, imag: 0}, + ]); + assertThat(out.displays[0].data.coherence_measure).isApproximatelyEqualTo(1); +}); + +suite.testUsingWebGL("AmplitudesDisplayIncoherent_sqrt_x", () => { + let stats = CircuitStats.fromCircuitAtTime( + Serializer.fromJson(CircuitDefinition, {cols:[[1,"•","X^½"], ["Amps2"]],init:[0,"+",1]}), + 0); + let out = stats.toReadableJson(); + assertThat(out.displays[0].data.incoherentKet).isApproximatelyEqualTo([ + Math.sqrt(0.5), + 0, + Math.sqrt(0.5), + 0, + ]); + assertThat(out.displays[0].data.coherence_measure).isLessThan(0.85); +}); + +suite.testUsingWebGL("AmplitudesDisplayIncoherent_hadamard", () => { + let stats = CircuitStats.fromCircuitAtTime( + Serializer.fromJson(CircuitDefinition, {cols:[[1,"•","H"], ["Amps2"]],init:[0,"+",1]}), + 0); + let out = stats.toReadableJson(); + assertThat(out.displays[0].data.incoherentKet).isApproximatelyEqualTo([ + Math.sqrt(0.5), + 0, + Math.sqrt(0.5), + 0, + ]); + assertThat(out.displays[0].data.coherence_measure).isLessThan(0.85); +}); diff --git a/test/webgl/Shaders.test.js b/test/webgl/Shaders.test.js index 2f8f729c..9c5e6b45 100644 --- a/test/webgl/Shaders.test.js +++ b/test/webgl/Shaders.test.js @@ -120,6 +120,12 @@ suite.testUsingWebGL("sumFold", () => { 0,4, 2,4 ])); + assertThat(Shaders.sumFoldVec2Adjacents(coords).readVec2Outputs(2)).isEqualTo(new Float32Array([ + 1,0, + 1,2, + 1,4, + 1,6, + ])); coords.deallocByDepositingInPool(); let solid = makePseudoShaderWithInputsAndOutputAndCode([], Outputs.vec4(), `