diff --git a/package.json b/package.json index a20dd0e4..f55c4457 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": "1.9.1", + "version": "2.0.0", "homepage": "https://github.com/Strilanc/Quirk", "bugs": { "url": "https://github.com/Strilanc/Quirk/issues" diff --git a/src/base/Util.js b/src/base/Util.js index 7923c0d1..fb55aa0a 100644 --- a/src/base/Util.js +++ b/src/base/Util.js @@ -104,18 +104,17 @@ class Util { /** * @param {!int} n - * @returns {!int} + * @returns {!int} A value p such that 2**(p-1) < n <= 2**p. */ static ceilLg2(n) { if (n <= 1) { return 0; } let p = Math.ceil(Math.log2(n)); - let v = 1 << p; - if (v < n) { + if (1<
= n*2) { + if (n <= 1<<(p-1)) { return p - 1; } return p; @@ -123,18 +122,17 @@ class Util { /** * @param {!int} n - * @returns {!int} + * @returns {!int} A value p such that 2**p <= n < 2**(p+1). */ static floorLg2(n) { if (n <= 1) { return 0; } let p = Math.floor(Math.log2(n)); - let v = 1 << p; - if (v*2 < n) { + if (1<<(p+1) <= n) { return p + 1; } - if (v > n) { + if (n < 1<
Output gate connections.
- for (let letter of ["A", "B"]) {
+ for (let letter of Gates.InputGates.Letters) {
let key = `Input Range ${letter}`;
let altInKey = `Input Default ${letter}`;
let altOutKey = `Input NO_DEFAULT Range ${letter}`;
diff --git a/src/gates/AllGates.js b/src/gates/AllGates.js
index ee0fd925..315caee5 100644
--- a/src/gates/AllGates.js
+++ b/src/gates/AllGates.js
@@ -14,7 +14,8 @@ import {HalfTurnGates} from "src/gates/HalfTurnGates.js"
import {InputGates} from "src/gates/InputGates.js"
import {InterleaveBitsGates} from "src/gates/InterleaveBitsGates.js"
import {MeasurementGate} from "src/gates/MeasurementGate.js"
-import {ModularArithmeticGates} from "src/gates/ModularArithmeticGates.js"
+import {ModularIncrementGates} from "src/gates/ModularIncrementGates.js"
+import {ModularAdditionGates} from "src/gates/ModularAdditionGates.js"
import {ModularMultiplicationGates} from "src/gates/ModularMultiplicationGates.js"
import {MultiplicationGates} from "src/gates/MultiplicationGates.js"
import {MultiplyAccumulateGates} from "src/gates/MultiplyAccumulateGates.js"
@@ -72,7 +73,8 @@ Gates.FourierTransformGates = FourierTransformGates;
Gates.HalfTurns = HalfTurnGates;
Gates.InputGates = InputGates;
Gates.InterleaveBitsGates = InterleaveBitsGates;
-Gates.ModularArithmeticGates = ModularArithmeticGates;
+Gates.ModularIncrementGates = ModularIncrementGates;
+Gates.ModularAdditionGates = ModularAdditionGates;
Gates.ModularMultiplicationGates = ModularMultiplicationGates;
Gates.MultiplicationGates = MultiplicationGates;
Gates.MultiplyAccumulateGates = MultiplyAccumulateGates;
@@ -118,7 +120,8 @@ Gates.KnownToSerializer = [
...FourierTransformGates.all,
...HalfTurnGates.all,
...InterleaveBitsGates.all,
- ...ModularArithmeticGates.all,
+ ...ModularAdditionGates.all,
+ ...ModularIncrementGates.all,
...ModularMultiplicationGates.all,
...MultiplicationGates.all,
...MultiplyAccumulateGates.all,
@@ -282,8 +285,8 @@ Gates.BottomToolboxGroups = [
{
hint: "Modular",
gates: [
- ModularArithmeticGates.IncrementModRFamily.ofSize(2), ModularArithmeticGates.DecrementModRFamily.ofSize(2),
- ModularArithmeticGates.PlusAModRFamily.ofSize(2), ModularArithmeticGates.MinusAModRFamily.ofSize(2),
+ ModularIncrementGates.IncrementModRFamily.ofSize(2), ModularIncrementGates.DecrementModRFamily.ofSize(2),
+ ModularAdditionGates.PlusAModRFamily.ofSize(2), ModularAdditionGates.MinusAModRFamily.ofSize(2),
ModularMultiplicationGates.TimesAModRFamily.ofSize(2),
ModularMultiplicationGates.TimesAModRInverseFamily.ofSize(2),
ModularMultiplicationGates.TimesBToTheAModRFamily.ofSize(2),
diff --git a/src/gates/ArithmeticGates.js b/src/gates/ArithmeticGates.js
index e86b73f4..8dae5425 100644
--- a/src/gates/ArithmeticGates.js
+++ b/src/gates/ArithmeticGates.js
@@ -42,7 +42,7 @@ ArithmeticGates.IncrementFamily = Gate.buildFamily(1, 16, (span, builder) => bui
ArithmeticGates.DecrementFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
setSerializedId("dec" + span).
- setSymbol("-1").
+ setSymbol("−1").
setTitle("Decrement Gate").
setBlurb("Subtracts 1 from the little-endian number represented by a block of qubits.").
setActualEffectToShaderProvider(ctx => offsetShader.withArgs(
diff --git a/src/gates/InputGates.js b/src/gates/InputGates.js
index d5256704..78fe8a9d 100644
--- a/src/gates/InputGates.js
+++ b/src/gates/InputGates.js
@@ -90,7 +90,7 @@ let makeSetInputGate = key => new GateBuilder().
return oldGate;
}
- let val = Number.parseInt(txt);
+ let val = parseInt(txt);
if (!Number.isInteger(val) || val < 0 || val >= 1<<16) {
alert(`'${txt}' isn't an integer between 0 and 65535. Keeping ${oldGate.param}.`);
return oldGate;
@@ -109,6 +109,7 @@ InputGates.InputRevBFamily = makeInputGate('B', true);
InputGates.SetA = makeSetInputGate('A');
InputGates.SetB = makeSetInputGate('B');
InputGates.SetR = makeSetInputGate('R');
+InputGates.Letters = ["A", "B", "R"];
InputGates.all = [
...InputGates.InputAFamily.all,
diff --git a/src/gates/ModularAdditionGates.js b/src/gates/ModularAdditionGates.js
new file mode 100644
index 00000000..5189e068
--- /dev/null
+++ b/src/gates/ModularAdditionGates.js
@@ -0,0 +1,63 @@
+import {Gate} from "src/circuit/Gate.js"
+import {ketArgs, ketShaderPermute, ketInputGateShaderCode} from "src/circuit/KetShaderUtil.js"
+import {Util} from "src/base/Util.js"
+import {WglArg} from "src/webgl/WglArg.js"
+import {modulusTooBigChecker} from "src/gates/ModularIncrementGates.js"
+
+let ModularAdditionGates = {};
+
+const MODULAR_ADDITION_SHADER = ketShaderPermute(
+ `
+ uniform float factor;
+ ${ketInputGateShaderCode('A')}
+ ${ketInputGateShaderCode('R')}
+ `,
+ `
+ float r = read_input_R();
+ if (out_id >= r) {
+ return out_id;
+ }
+ float d = read_input_A();
+ d *= factor;
+ d = mod(d, r);
+ float result = mod(out_id + r - d, r);
+
+ // Despite sanity, I consistently get result=33 instead of result=0 when out_id=0, d=0, r=33.
+ // HACK: Fix it by hand.
+ if (result >= r) {
+ result -= r;
+ }
+
+ return result;
+ `);
+
+ModularAdditionGates.PlusAModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
+ setSerializedId("+AmodR" + span).
+ setSymbol("+A\nmod R").
+ setTitle("Modular Addition Gate").
+ setBlurb("Adds input A into the target, mod input R.\nOnly affects values below R.").
+ setRequiredContextKeys("Input Range A", "Input Range R").
+ setExtraDisableReasonFinder(modulusTooBigChecker("R", span)).
+ setActualEffectToShaderProvider(ctx => MODULAR_ADDITION_SHADER.withArgs(
+ ...ketArgs(ctx, span, ['A', 'R']),
+ WglArg.float("factor", +1))).
+ setKnownEffectToParametrizedPermutation((t, a, b) => t < b ? (t + a) % b : t));
+
+ModularAdditionGates.MinusAModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
+ setSerializedId("-AmodR" + span).
+ setSymbol("−A\nmod R").
+ setTitle("Modular Subtraction Gate").
+ setBlurb("Subtracts input A out of the target, mod input R.\nOnly affects values below R.").
+ setRequiredContextKeys("Input Range A", "Input Range R").
+ setExtraDisableReasonFinder(modulusTooBigChecker("R", span)).
+ setActualEffectToShaderProvider(ctx => MODULAR_ADDITION_SHADER.withArgs(
+ ...ketArgs(ctx, span, ['A', 'R']),
+ WglArg.float("factor", -1))).
+ setKnownEffectToParametrizedPermutation((t, a, b) => t < b ? Util.properMod(t - a, b) : t));
+
+ModularAdditionGates.all = [
+ ...ModularAdditionGates.PlusAModRFamily.all,
+ ...ModularAdditionGates.MinusAModRFamily.all,
+];
+
+export {ModularAdditionGates}
diff --git a/src/gates/ModularArithmeticGates.js b/src/gates/ModularArithmeticGates.js
deleted file mode 100644
index f99c651c..00000000
--- a/src/gates/ModularArithmeticGates.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import {Gate} from "src/circuit/Gate.js"
-import {ketArgs, ketShaderPermute, ketInputGateShaderCode} from "src/circuit/KetShaderUtil.js"
-import {Util} from "src/base/Util.js"
-import {WglArg} from "src/webgl/WglArg.js"
-
-let ModularArithmeticGates = {};
-
-/**
- * @param {!string} inputKey
- * @param {!int} span
- * @returns {!function(!GateCheckArgs) : (undefined|!string)}
- */
-let modulusTooBigChecker = (inputKey, span) => args => {
- let r = args.context.get('Input Range ' + inputKey);
- let d = args.context.get('Input Default ' + inputKey);
- if (r !== undefined && r.length > span) {
- return "mod\ntoo\nbig";
- }
- if (r === undefined && d !== undefined && d > 1<= r
- ? out_id
- // HACK: sometimes mod(value-equal-to-r, r) returns r instead of 0. The perturbation works around it.
- : floor(mod(out_id + r - amount, r - 0.000001));`);
-
-ModularArithmeticGates.IncrementModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
- setSerializedId("incmodR" + span).
- setSymbol("+1\nmod R").
- setTitle("Modular Increment Gate").
- setBlurb("Adds 1 into the target, but wraps R-1 to 0.\n" +
- "Only affects values less than R.").
- setRequiredContextKeys("Input Range R").
- setExtraDisableReasonFinder(modulusTooBigChecker("R", span)).
- setActualEffectToShaderProvider(ctx => MODULAR_INCREMENT_SHADER.withArgs(
- ...ketArgs(ctx, span, ['R']),
- WglArg.float("amount", +1))).
- setKnownEffectToParametrizedPermutation((t, a) => t < a ? (t + 1) % a : t));
-
-ModularArithmeticGates.DecrementModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
- setSerializedId("decmodR" + span).
- setSymbol("−1\nmod R").
- setTitle("Modular Decrement Gate").
- setBlurb("Subtracts 1 out of the target, but wraps 0 to R-1.\n" +
- "Only affects values less than R.").
- setRequiredContextKeys("Input Range R").
- setExtraDisableReasonFinder(modulusTooBigChecker("R", span)).
- setActualEffectToShaderProvider(ctx => MODULAR_INCREMENT_SHADER.withArgs(
- ...ketArgs(ctx, span, ['R']),
- WglArg.float("amount", -1))).
- setKnownEffectToParametrizedPermutation((t, a) => t < a ? Util.properMod(t - 1, a) : t));
-
-const MODULAR_ADDITION_SHADER = ketShaderPermute(
- `
- uniform float factor;
- ${ketInputGateShaderCode('A')}
- ${ketInputGateShaderCode('R')}
- `,
- `
- float d = read_input_A();
- float r = read_input_R();
- d *= factor;
- d = mod(d, r);
- return out_id >= r
- ? out_id
- // HACK: sometimes mod(value-equal-to-r, r) returns r instead of 0. The perturbation works around it.
- : floor(mod(out_id + r - d, r - 0.000001) + 0.5);`);
-
-ModularArithmeticGates.PlusAModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
- setSerializedId("+AmodR" + span).
- setSymbol("+A\nmod R").
- setTitle("Modular Addition Gate").
- setBlurb("Adds input A into the target, mod input R.\nOnly affects values below R.").
- setRequiredContextKeys("Input Range A", "Input Range R").
- setExtraDisableReasonFinder(modulusTooBigChecker("R", span)).
- setActualEffectToShaderProvider(ctx => MODULAR_ADDITION_SHADER.withArgs(
- ...ketArgs(ctx, span, ['A', 'R']),
- WglArg.float("factor", +1))).
- setKnownEffectToParametrizedPermutation((t, a, b) => t < b ? (t + a) % b : t));
-
-ModularArithmeticGates.MinusAModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
- setSerializedId("-AmodR" + span).
- setSymbol("−A\nmod R").
- setTitle("Modular Subtraction Gate").
- setBlurb("Subtracts input A out of the target, mod input R.\nOnly affects values below R.").
- setRequiredContextKeys("Input Range A", "Input Range R").
- setExtraDisableReasonFinder(modulusTooBigChecker("R", span)).
- setActualEffectToShaderProvider(ctx => MODULAR_ADDITION_SHADER.withArgs(
- ...ketArgs(ctx, span, ['A', 'R']),
- WglArg.float("factor", -1))).
- setKnownEffectToParametrizedPermutation((t, a, b) => t < b ? Util.properMod(t - a, b) : t));
-
-ModularArithmeticGates.all = [
- ...ModularArithmeticGates.IncrementModRFamily.all,
- ...ModularArithmeticGates.DecrementModRFamily.all,
- ...ModularArithmeticGates.PlusAModRFamily.all,
- ...ModularArithmeticGates.MinusAModRFamily.all,
-];
-
-export {ModularArithmeticGates, modulusTooBigChecker}
diff --git a/src/gates/ModularIncrementGates.js b/src/gates/ModularIncrementGates.js
new file mode 100644
index 00000000..366b6955
--- /dev/null
+++ b/src/gates/ModularIncrementGates.js
@@ -0,0 +1,68 @@
+import {Gate} from "src/circuit/Gate.js"
+import {ketArgs, ketShaderPermute, ketInputGateShaderCode} from "src/circuit/KetShaderUtil.js"
+import {Util} from "src/base/Util.js"
+import {WglArg} from "src/webgl/WglArg.js"
+
+let ModularIncrementGates = {};
+
+/**
+ * @param {!string} inputKey
+ * @param {!int} span
+ * @returns {!function(!GateCheckArgs) : (undefined|!string)}
+ */
+let modulusTooBigChecker = (inputKey, span) => args => {
+ let r = args.context.get('Input Range ' + inputKey);
+ let d = args.context.get('Input Default ' + inputKey);
+ if (r !== undefined && r.length > span) {
+ return "mod\ntoo\nbig";
+ }
+ if (r === undefined && d !== undefined && d > 1<= r
+ ? out_id
+ // HACK: sometimes mod(value-equal-to-r, r) returns r instead of 0. The perturbation works around it.
+ : floor(mod(out_id + r - amount, r - 0.000001));`);
+
+ModularIncrementGates.IncrementModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
+ setSerializedId("incmodR" + span).
+ setSymbol("+1\nmod R").
+ setTitle("Modular Increment Gate").
+ setBlurb("Adds 1 into the target, but wraps R-1 to 0.\n" +
+ "Only affects values less than R.").
+ setRequiredContextKeys("Input Range R").
+ setExtraDisableReasonFinder(modulusTooBigChecker("R", span)).
+ setActualEffectToShaderProvider(ctx => MODULAR_INCREMENT_SHADER.withArgs(
+ ...ketArgs(ctx, span, ['R']),
+ WglArg.float("amount", +1))).
+ setKnownEffectToParametrizedPermutation((t, a) => t < a ? (t + 1) % a : t));
+
+ModularIncrementGates.DecrementModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
+ setSerializedId("decmodR" + span).
+ setSymbol("−1\nmod R").
+ setTitle("Modular Decrement Gate").
+ setBlurb("Subtracts 1 out of the target, but wraps 0 to R-1.\n" +
+ "Only affects values less than R.").
+ setRequiredContextKeys("Input Range R").
+ setExtraDisableReasonFinder(modulusTooBigChecker("R", span)).
+ setActualEffectToShaderProvider(ctx => MODULAR_INCREMENT_SHADER.withArgs(
+ ...ketArgs(ctx, span, ['R']),
+ WglArg.float("amount", -1))).
+ setKnownEffectToParametrizedPermutation((t, a) => t < a ? Util.properMod(t - 1, a) : t));
+
+ModularIncrementGates.all = [
+ ...ModularIncrementGates.IncrementModRFamily.all,
+ ...ModularIncrementGates.DecrementModRFamily.all,
+];
+
+export {ModularIncrementGates, modulusTooBigChecker}
diff --git a/src/gates/ModularMultiplicationGates.js b/src/gates/ModularMultiplicationGates.js
index c7487f47..4bba40d6 100644
--- a/src/gates/ModularMultiplicationGates.js
+++ b/src/gates/ModularMultiplicationGates.js
@@ -5,30 +5,21 @@ import {
ketShaderPermute,
ketInputGateShaderCode
} from "src/circuit/KetShaderUtil.js"
-import {modulusTooBigChecker} from "src/gates/ModularArithmeticGates.js"
+import {modulusTooBigChecker} from "src/gates/ModularIncrementGates.js"
+import {BIG_MUL_MOD_SHADER_CODE} from "src/gates/MultiplyAccumulateGates.js"
import {Util} from "src/base/Util.js"
import {WglArg} from "src/webgl/WglArg.js"
let ModularMultiplicationGates = {};
+const MUL_STEP = 6;
+
const MODULAR_INVERSE_SHADER_CODE = `
vec2 _mod_mul_step(vec2 v, float q) {
return vec2(v.y - q * v.x, v.x);
}
- // Avoids large multiplications that lose precision.
- float times_mod(float b, float f, float modulus) {
- float t = 0.0;
- for (int k = 0; k < ${Config.MAX_WIRE_COUNT}; k++) {
- if (mod(f, 2.0) == 1.0) {
- f -= 1.0;
- t = mod(t + b, modulus);
- }
- b = mod(b * 2.0, modulus);
- f /= 2.0;
- }
- return t;
- }
+ ${BIG_MUL_MOD_SHADER_CODE}
float modular_multiplicative_inverse(float value, float modulus) {
vec2 s = vec2(0.0, 1.0);
@@ -69,9 +60,9 @@ const POW_MOD_SHADER_CODE = `
for (int k = 0; k < ${Config.MAX_WIRE_COUNT}; k++) {
if (mod(exponent, 2.0) == 1.0) {
exponent -= 1.0;
- f = times_mod(f, base, modulus);
+ f = big_mul_mod(f, base, modulus);
}
- base = times_mod(base, base, modulus);
+ base = big_mul_mod(base, base, modulus);
exponent /= 2.0;
}
return f;
@@ -171,7 +162,7 @@ const MODULAR_MULTIPLICATION_SHADER = ketShaderPermute(
if (v == -1.0 || out_id >= modulus) {
return out_id;
}
- return times_mod(out_id, v, modulus);
+ return big_mul_mod(out_id, v, modulus);
`);
const MODULAR_INVERSE_MULTIPLICATION_SHADER = ketShaderPermute(
@@ -187,7 +178,7 @@ const MODULAR_INVERSE_MULTIPLICATION_SHADER = ketShaderPermute(
if (modular_multiplicative_inverse(input_a, modulus) == -1.0 || out_id >= modulus) {
return out_id;
}
- return times_mod(out_id, input_a, modulus);
+ return big_mul_mod(out_id, input_a, modulus);
`);
const MODULAR_POWER_MULTIPLICATION_SHADER = ketShaderPermute(
@@ -206,7 +197,7 @@ const MODULAR_POWER_MULTIPLICATION_SHADER = ketShaderPermute(
if (f == -1.0 || out_id >= modulus) {
return out_id;
}
- return times_mod(out_id, f, modulus);
+ return big_mul_mod(out_id, f, modulus);
`);
ModularMultiplicationGates.TimesAModRFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
diff --git a/src/gates/MultiplicationGates.js b/src/gates/MultiplicationGates.js
index c77990d9..d01b64bd 100644
--- a/src/gates/MultiplicationGates.js
+++ b/src/gates/MultiplicationGates.js
@@ -20,7 +20,7 @@ const MULTIPLICATION_SHADER = ketShaderPermute(
if (v == -1.0) {
return out_id;
}
- return mod(out_id * v, span);
+ return big_mul_mod(out_id, v, span);
`);
const INVERSE_MULTIPLICATION_SHADER = ketShaderPermute(
@@ -34,7 +34,7 @@ const INVERSE_MULTIPLICATION_SHADER = ketShaderPermute(
if (modular_multiplicative_inverse(input_a, span) == -1.0) {
return out_id;
}
- return mod(out_id * input_a, span);
+ return big_mul_mod(out_id, input_a, span);
`);
MultiplicationGates.TimesAFamily = Gate.buildFamily(1, 16, (span, builder) => builder.
diff --git a/src/gates/MultiplyAccumulateGates.js b/src/gates/MultiplyAccumulateGates.js
index 9080c959..ab55c7c9 100644
--- a/src/gates/MultiplyAccumulateGates.js
+++ b/src/gates/MultiplyAccumulateGates.js
@@ -1,3 +1,4 @@
+import {Config} from "src/Config.js";
import {Gate} from "src/circuit/Gate.js"
import {GatePainting} from "src/draw/GatePainting.js"
import {ketArgs, ketShaderPermute, ketInputGateShaderCode} from "src/circuit/KetShaderUtil.js"
@@ -22,16 +23,34 @@ const makeScaledMultiplyAddPermutation = (span, scaleFactor) => e => {
return a | (b << sa) | (c << (sa+sb));
};
+const MUL_STEP = 6;
+const BIG_MUL_MOD_SHADER_CODE = `
+ // Avoids large multiplications that lose precision.
+ float big_mul_mod(float b, float f, float modulus) {
+ float t = 0.0;
+ float r;
+ for (int k = 0; k < ${Math.ceil(Config.MAX_WIRE_COUNT/MUL_STEP)}; k++) {
+ r = mod(f, ${1<