100 | There are "controlled" versions of many operators such as controlledSwap, controlledX, controlledY, etc.
101 | For all of these, the controlBits must be specified before the target bits.
102 | (There is presently no controlled version of qft).
103 |
104 |
105 | Most of the bit specifiers can be single numbers (0 being least significant),
106 | arrays of numbers,
107 | bit ranges (e.g. {from: 5, to:15},
108 | or the constant jsqubits.ALL.
109 | Currently, the exception to this is applyFunction as this cannot take arrays.
110 | Also, while the controlledSwap method is
111 | able to take the various types of bit specifiers as control bits,
112 | it (and the swap) operator can only take single numbers for the two bits to be swapped.
113 |
119 |
120 | Copyright 2012 David Kemp. Licensed under
121 | the MIT license.
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/spec/simple_algorithms.spec.js:
--------------------------------------------------------------------------------
1 | import * as chai from 'chai';
2 | import jsqubits from '../lib/index.js';
3 | const jsqubitsmath = jsqubits.QMath;
4 | const {expect} = chai;
5 |
6 | describe('Simple Quantum Algorithms', function() {
7 |
8 | var shuffle = function (a) {
9 | for (let i = 0; i < a.length; i++) {
10 | const j = Math.floor(Math.random() * a.length);
11 | const x = a[i];
12 | a[i] = a[j];
13 | a[j] = x;
14 | }
15 | };
16 |
17 | describe('Super dense coding', function() {
18 | const superDense = function (input) {
19 | let state = jsqubits('|00>').hadamard(0).cnot(0, 1);
20 |
21 | // Alice prepares her qbit
22 | const alice = 1;
23 | if (input.charAt(0) === '1') {
24 | state = state.z(alice);
25 | }
26 | if (input.charAt(1) === '1') {
27 | state = state.x(alice);
28 | }
29 |
30 | // Alice sends her qbit to Bob
31 | const bob = 0;
32 | state = state.cnot(alice, bob).hadamard(alice);
33 | return state.measure(jsqubits.ALL).asBitString();
34 | };
35 |
36 | it('should transmit 00', function() {
37 | expect(superDense('00')).to.equal('00');
38 | });
39 |
40 | it('should transmit 01', function() {
41 | expect(superDense('01')).to.equal('01');
42 | });
43 |
44 | it('should transmit 10', function() {
45 | expect(superDense('10')).to.equal('10');
46 | });
47 |
48 | it('should transmit 11', function() {
49 | expect(superDense('11')).to.equal('11');
50 | });
51 | });
52 |
53 | describe('Simple search', function() {
54 | const createOracle = function (match) { return function (x) { return x === match ? 1 : 0; }; };
55 |
56 | const simpleSearch = function (f) {
57 | const inputBits = {from: 1, to: 2};
58 | return jsqubits('|001>')
59 | .hadamard(jsqubits.ALL)
60 | .applyFunction(inputBits, 0, f)
61 | .hadamard(inputBits)
62 | .z(inputBits)
63 | .controlledZ(2, 1)
64 | .hadamard(inputBits)
65 | .measure(inputBits)
66 | .result;
67 | };
68 |
69 | it('should find f00', function() {
70 | expect(simpleSearch(createOracle(0))).to.equal(0);
71 | });
72 |
73 | it('should find f01', function() {
74 | expect(simpleSearch(createOracle(1))).to.equal(1);
75 | });
76 |
77 | it('should find f10', function() {
78 | expect(simpleSearch(createOracle(2))).to.equal(2);
79 | });
80 |
81 | it('should find f11', function() {
82 | expect(simpleSearch(createOracle(3))).to.equal(3);
83 | });
84 | });
85 |
86 | describe('Quantum Teleportation', function() {
87 | const applyTeleportation = function (state) {
88 | const alicesMeasurement = state.cnot(2, 1).hadamard(2).measure({from: 1, to: 2});
89 | let resultingState = alicesMeasurement.newState;
90 | if (alicesMeasurement.result & 1) {
91 | resultingState = resultingState.x(0);
92 | }
93 | if (alicesMeasurement.result & 2) {
94 | resultingState = resultingState.z(0);
95 | }
96 | return resultingState;
97 | };
98 |
99 | it('should support transmition of quantum state from Alice to Bob', function() {
100 | const stateToBeTransmitted = jsqubits('|0>').rotateX(0, Math.PI / 3).rotateZ(0, Math.PI / 5);
101 | const initialState = jsqubits('|000>').hadamard(1).cnot(1, 0).rotateX(2, Math.PI / 3)
102 | .rotateZ(2, Math.PI / 5);
103 | const stateToBeTransmitted0 = stateToBeTransmitted.amplitude('|0>');
104 | const stateToBeTransmitted1 = stateToBeTransmitted.amplitude('|1>');
105 | const finalState = applyTeleportation(initialState);
106 | // By this stage, only bit zero has not been measured and it should have the same state the original state to be transmitted.
107 | let receivedAmplitudeFor0 = null;
108 | let receivedAmplitudeFor1 = null;
109 | finalState.each((stateWithAmplitude) => {
110 | if (stateWithAmplitude.asNumber() % 2 == 0) {
111 | if (receivedAmplitudeFor0 != null) throw 'Should only have one state with bit 0 being 0';
112 | receivedAmplitudeFor0 = stateWithAmplitude.amplitude;
113 | } else {
114 | if (receivedAmplitudeFor1 != null) throw 'Should only have one state with bit 0 being 1';
115 | receivedAmplitudeFor1 = stateWithAmplitude.amplitude;
116 | }
117 | });
118 | expect(receivedAmplitudeFor0.closeTo(stateToBeTransmitted0)).to.be.true;
119 | expect(receivedAmplitudeFor1.closeTo(stateToBeTransmitted1)).to.be.true;
120 | });
121 | });
122 |
123 | describe("Deutsch's algorithm", function() {
124 | const deutsch = function (f) {
125 | return jsqubits('|01>').hadamard(jsqubits.ALL).applyFunction(1, 0, f).hadamard(jsqubits.ALL)
126 | .measure(1).result;
127 | };
128 |
129 | it('should compute 0 for fixed function returning 1', function() {
130 | const f = function (x) { return 1; };
131 | expect(deutsch(f)).to.equal(0);
132 | });
133 |
134 | it('should compute 0 for fixed function returning 0', function() {
135 | const f = function (x) { return 0; };
136 | expect(deutsch(f)).to.equal(0);
137 | });
138 |
139 | it('should compute 1 for identity function', function() {
140 | const f = function (x) { return x; };
141 | expect(deutsch(f)).to.equal(1);
142 | });
143 |
144 | it('should compute 1 for not function', function() {
145 | const f = function (x) { return (x + 1) % 2; };
146 | expect(deutsch(f)).to.equal(1);
147 | });
148 | });
149 |
150 | describe('Deutsch-Jozsa algorithm', function() {
151 | const deutschJozsa = function (f) {
152 | const inputBits = {from: 1, to: 3};
153 | const result = jsqubits('|0001>')
154 | .hadamard(jsqubits.ALL)
155 | .applyFunction(inputBits, 0, f)
156 | .hadamard(inputBits)
157 | .measure(inputBits)
158 | .result;
159 | return result === 0;
160 | };
161 |
162 | const createBalancedFunction = function () {
163 | // Return 0 for exactly half the possible inputs and 1 for the rest.
164 | const nums = [0, 1, 2, 3, 4, 5, 6, 7];
165 | shuffle(nums);
166 | return function (x) { return nums[x] < 4 ? 0 : 1; };
167 | };
168 |
169 | it('should return true if function always returns zero', function() {
170 | expect(deutschJozsa((x) => { return 0; })).to.equal(true);
171 | });
172 |
173 | it('should return true if function always returns one', function() {
174 | expect(deutschJozsa((x) => { return 1; })).to.equal(true);
175 | });
176 |
177 | it('should return false if function is balanced', function() {
178 | expect(deutschJozsa(createBalancedFunction())).to.equal(false);
179 | });
180 | });
181 |
182 | describe("Simon's algorithm", function() {
183 | const singleRunOfSimonsCircuit = function (f, numbits) {
184 | const inputBits = {from: numbits, to: 2 * numbits - 1};
185 | const targetBits = {from: 0, to: numbits - 1};
186 | const qbits = new jsqubits.QState(2 * numbits)
187 | .hadamard(inputBits)
188 | .applyFunction(inputBits, targetBits, f)
189 | .hadamard(inputBits);
190 | return qbits.measure(inputBits).result;
191 | };
192 |
193 | // TODO: Make this a litte easier to read!
194 | const findPotentialSolution = function (f, numBits) {
195 | let nullSpace = null;
196 | const results = [];
197 | let estimatedNumberOfIndependentSolutions = 0;
198 | for (let count = 0; count < 10 * numBits; count++) {
199 | const result = singleRunOfSimonsCircuit(f, numBits);
200 | if (results.indexOf(result) < 0) {
201 | results.push(result);
202 | estimatedNumberOfIndependentSolutions++;
203 | if (estimatedNumberOfIndependentSolutions == numBits - 1) {
204 | nullSpace = jsqubitsmath.findNullSpaceMod2(results, numBits);
205 | if (nullSpace.length == 1) break;
206 | estimatedNumberOfIndependentSolutions = numBits - nullSpace.length;
207 | }
208 | }
209 | }
210 | if (nullSpace === null) throw 'Could not find a solution';
211 | return nullSpace[0];
212 | };
213 |
214 | const simonsAlgorithm = function (f, numBits) {
215 | const solution = findPotentialSolution(f, numBits);
216 | return (f(0) === f(solution)) ? solution : 0;
217 | };
218 |
219 | it('should find the right key (not identity)', function() {
220 | const testFunction = function (x) {
221 | const mapping = ['101', '010', '000', '110', '000', '110', '101', '010'];
222 | return parseInt(mapping[x], 2);
223 | };
224 | expect(simonsAlgorithm(testFunction, 3).toString(2)).to.equal('110');
225 | });
226 |
227 | it('should find the right key (identity)', function() {
228 | const mapping = [0, 1, 2, 3, 4, 5, 6, 7];
229 | shuffle(mapping);
230 | const permutation = function (x) {
231 | return mapping[x];
232 | };
233 | expect(simonsAlgorithm(permutation, 3)).to.equal(0);
234 | });
235 | });
236 | });
237 |
--------------------------------------------------------------------------------
/lib/QState.js:
--------------------------------------------------------------------------------
1 | import Measurement, {QStateComponent} from './Measurement.js';
2 | import Complex from './Complex.js';
3 | import validateArgs from './utils/validateArgs.js';
4 | import typecheck from './utils/typecheck.js';
5 |
6 | function parseBitString(bitString) {
7 | // Strip optional 'ket' characters to support |0101>
8 | bitString = bitString.replace(/^\|/, '').replace(/>$/, '');
9 | return {value: parseInt(bitString, 2), length: bitString.length};
10 | }
11 |
12 | function sparseAssign(array, index, value) {
13 | // Try to avoid assigning values and try to make zero exactly zero.
14 | if (value.magnitude() > QState.roundToZero) {
15 | array[index] = value;
16 | }
17 | }
18 |
19 | /*
20 | Add amplitude to the existing amplitude for state in the amplitudes object
21 | Keep the object sparse by deleting values close to zero.
22 | */
23 | function sparseAdd(amplitudes, state, amplitude) {
24 | const newAmplitude = (amplitudes[state] || Complex.ZERO).add(amplitude);
25 | if (newAmplitude.magnitude() > QState.roundToZero) {
26 | amplitudes[state] = newAmplitude;
27 | } else {
28 | delete amplitudes[state];
29 | }
30 | }
31 |
32 | function convertBitQualifierToBitRange(bits, numBits) {
33 | if (bits == null) {
34 | throw new Error('bit qualification must be supplied');
35 | } else if (bits === QState.ALL) {
36 | return {from: 0, to: numBits - 1};
37 | } else if (typecheck.isNumber(bits)) {
38 | return {from: bits, to: bits};
39 | } else if (bits.from != null && bits.to != null) {
40 | if (bits.from > bits.to) {
41 | throw new Error('bit range must have "from" being less than or equal to "to"');
42 | }
43 | return bits;
44 | } else {
45 | throw new Error('bit qualification must be either: a number, QState.ALL, or {from: n, to: m}');
46 | }
47 | }
48 |
49 |
50 | function validateControlAndTargetBitsDontOverlap(controlBits, targetBits) {
51 | // TODO: Find out if it would sometimes be faster to put one of the bit collections into a hash-set first.
52 | // Also consider allowing validation to be disabled.
53 | for (let i = 0; i < controlBits.length; i++) {
54 | const controlBit = controlBits[i];
55 | for (let j = 0; j < targetBits.length; j++) {
56 | if (controlBit === targetBits[j]) {
57 | throw new Error('control and target bits must not be the same nor overlap');
58 | }
59 | }
60 | }
61 | }
62 |
63 | function chooseRandomBasisState(qState) {
64 | const randomNumber = qState.random();
65 | let randomStateString;
66 | let accumulativeSquareAmplitudeMagnitude = 0;
67 | qState.each((stateWithAmplitude) => {
68 | const magnitude = stateWithAmplitude.amplitude.magnitude();
69 | accumulativeSquareAmplitudeMagnitude += magnitude * magnitude;
70 | randomStateString = stateWithAmplitude.index;
71 | return accumulativeSquareAmplitudeMagnitude <= randomNumber;
72 | });
73 | return parseInt(randomStateString, 10);
74 | }
75 |
76 | function bitRangeAsArray(low, high) {
77 | if (low > high) {
78 | throw new Error('bit range must have `from` being less than or equal to `to`');
79 | }
80 | const result = [];
81 | for (let i = low; i <= high; i++) {
82 | result.push(i);
83 | }
84 | return result;
85 | }
86 |
87 | function convertBitQualifierToBitArray(bits, numBits) {
88 | if (bits == null) {
89 | throw new Error('bit qualification must be supplied');
90 | }
91 | if (typecheck.isArray(bits)) {
92 | return bits;
93 | }
94 | if (typecheck.isNumber(bits)) {
95 | return [bits];
96 | }
97 | if (bits === QState.ALL) {
98 | return bitRangeAsArray(0, numBits - 1);
99 | }
100 | if (bits.from != null && bits.to != null) {
101 | return bitRangeAsArray(bits.from, bits.to);
102 | }
103 | throw new Error('bit qualification must be either: a number, an array of numbers, QState.ALL, or {from: n, to: m}');
104 | }
105 |
106 |
107 | function createBitMask(bits) {
108 | let mask = null;
109 | if (bits) {
110 | mask = 0;
111 | for (let i = 0; i < bits.length; i++) {
112 | mask += (1 << bits[i]);
113 | }
114 | }
115 | return mask;
116 | }
117 |
118 | const hadamardMatrix = [
119 | [Complex.SQRT1_2, Complex.SQRT1_2],
120 | [Complex.SQRT1_2, Complex.SQRT1_2.negate()]
121 | ];
122 |
123 | const xMatrix = [
124 | [Complex.ZERO, Complex.ONE],
125 | [Complex.ONE, Complex.ZERO]
126 | ];
127 |
128 | const yMatrix = [
129 | [Complex.ZERO, new Complex(0, -1)],
130 | [new Complex(0, 1), Complex.ZERO]
131 | ];
132 |
133 | const zMatrix = [
134 | [Complex.ONE, Complex.ZERO],
135 | [Complex.ZERO, Complex.ONE.negate()]
136 | ];
137 |
138 | const sMatrix = [
139 | [Complex.ONE, Complex.ZERO],
140 | [Complex.ZERO, new Complex(0, 1)]
141 | ];
142 |
143 | const tMatrix = [
144 | [Complex.ONE, Complex.ZERO],
145 | [Complex.ZERO, new Complex(Math.SQRT1_2, Math.SQRT1_2)]
146 | ];
147 |
148 | export default class QState {
149 | constructor(numBits, amplitudes) {
150 | validateArgs(arguments, 1, 'new QState() must be supplied with number of bits (optionally with amplitudes as well)');
151 | amplitudes = amplitudes || [Complex.ONE];
152 |
153 | this.numBits = () => {
154 | return numBits;
155 | };
156 |
157 | this.amplitude = (basisState) => {
158 | const numericIndex = typecheck.isString(basisState) ? parseBitString(basisState).value : basisState;
159 | return amplitudes[numericIndex] || Complex.ZERO;
160 | };
161 |
162 | this.each = (callBack) => {
163 | const indices = Object.keys(amplitudes);
164 | for (let i = 0; i < indices.length; i++) {
165 | const index = indices[i];
166 | const returnValue = callBack(new QStateComponent(numBits, index, amplitudes[index]));
167 | // NOTE: Want to continue on void and null returns!
168 | if (returnValue === false) break;
169 | }
170 | };
171 | }
172 |
173 | static fromBits(bitString) {
174 | validateArgs(arguments, 1, 1, 'Must supply a bit string');
175 | const parsedBitString = parseBitString(bitString);
176 | const amplitudes = {};
177 | amplitudes[parsedBitString.value] = Complex.ONE;
178 | return new QState(parsedBitString.length, amplitudes);
179 | }
180 |
181 |
182 | multiply(amount) {
183 | if (typecheck.isNumber(amount)) {
184 | amount = new Complex(amount);
185 | }
186 | const amplitudes = {};
187 | this.each((oldAmplitude) => {
188 | amplitudes[oldAmplitude.index] = oldAmplitude.amplitude.multiply(amount);
189 | });
190 | return new QState(this.numBits(), amplitudes);
191 | }
192 |
193 | add(otherState) {
194 | const amplitudes = {};
195 | this.each((stateWithAmplitude) => {
196 | amplitudes[stateWithAmplitude.index] = stateWithAmplitude.amplitude;
197 | });
198 | otherState.each((stateWithAmplitude) => {
199 | const existingValue = amplitudes[stateWithAmplitude.index] || Complex.ZERO;
200 | amplitudes[stateWithAmplitude.index] = stateWithAmplitude.amplitude.add(existingValue);
201 | });
202 | return new QState(this.numBits(), amplitudes);
203 | }
204 |
205 | subtract(otherState) {
206 | return this.add(otherState.multiply(-1));
207 | }
208 |
209 | tensorProduct(otherState) {
210 | const amplitudes = {};
211 | this.each((basisWithAmplitudeA) => {
212 | otherState.each((otherBasisWithAmplitude) => {
213 | const newBasisState = (basisWithAmplitudeA.asNumber() << otherState.numBits()) + otherBasisWithAmplitude.asNumber();
214 | const newAmplitude = basisWithAmplitudeA.amplitude.multiply(otherBasisWithAmplitude.amplitude);
215 | amplitudes[newBasisState] = newAmplitude;
216 | });
217 | });
218 | return new QState(this.numBits() + otherState.numBits(), amplitudes);
219 | }
220 |
221 | kron(otherState) {
222 | return this.tensorProduct(otherState);
223 | }
224 |
225 | static applyOperatorMatrix(matrix, bitValue, amplitude) {
226 | return [
227 | matrix[0][bitValue].multiply(amplitude),
228 | matrix[1][bitValue].multiply(amplitude)
229 | ];
230 | }
231 |
232 | controlledHadamard(controlBits, targetBits) {
233 | validateArgs(arguments, 2, 2, 'Must supply control and target bits to controlledHadamard()');
234 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, hadamardMatrix);
235 | }
236 |
237 | hadamard(targetBits) {
238 | validateArgs(arguments, 1, 1, 'Must supply target bits to hadamard() as either a single index or a range.');
239 | return this.controlledHadamard(null, targetBits);
240 | }
241 |
242 | controlledXRotation(controlBits, targetBits, angle) {
243 | validateArgs(arguments, 3, 3, 'Must supply control bits, target bits, and an angle, to controlledXRotation()');
244 | const halfAngle = angle / 2;
245 | const cosine = new Complex(Math.cos(halfAngle));
246 | const negativeISine = new Complex(0, -Math.sin(halfAngle));
247 |
248 | const rotationMatrix = [
249 | [cosine, negativeISine],
250 | [negativeISine, cosine]
251 | ];
252 |
253 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, rotationMatrix);
254 | }
255 |
256 | rotateX(targetBits, angle) {
257 | validateArgs(arguments, 2, 2, 'Must supply target bits and angle to rotateX.');
258 | return this.controlledXRotation(null, targetBits, angle);
259 | }
260 |
261 | controlledYRotation(controlBits, targetBits, angle) {
262 | validateArgs(arguments, 3, 3, 'Must supply control bits, target bits, and an angle, to controlledYRotation()');
263 | const halfAngle = angle / 2;
264 | const cosine = new Complex(Math.cos(halfAngle));
265 | const sine = new Complex(Math.sin(halfAngle));
266 | const rotationMatrix = [
267 | [cosine, sine.negate()],
268 | [sine, cosine]
269 | ];
270 |
271 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, rotationMatrix);
272 | }
273 |
274 |
275 | rotateY(targetBits, angle) {
276 | validateArgs(arguments, 2, 2, 'Must supply target bits and angle to rotateY.');
277 | return this.controlledYRotation(null, targetBits, angle);
278 | }
279 |
280 | controlledZRotation(controlBits, targetBits, angle) {
281 | validateArgs(arguments, 3, 3, 'Must supply control bits, target bits, and an angle to controlledZRotation()');
282 | const halfAngle = angle / 2;
283 | const cosine = new Complex(Math.cos(halfAngle));
284 | const iSine = new Complex(0, Math.sin(halfAngle));
285 | const rotationMatrix = [
286 | [cosine.subtract(iSine), Complex.ZERO],
287 | [Complex.ZERO, cosine.add(iSine)]
288 | ];
289 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, rotationMatrix);
290 | }
291 |
292 | rotateZ(targetBits, angle) {
293 | validateArgs(arguments, 2, 2, 'Must supply target bits and angle to rotateZ.');
294 | return this.controlledZRotation(null, targetBits, angle);
295 | }
296 |
297 | controlledR(controlBits, targetBits, angle) {
298 | validateArgs(arguments, 3, 3, 'Must supply control and target bits, and an angle to controlledR().');
299 | const cosine = new Complex(Math.cos(angle));
300 | const iSine = new Complex(0, Math.sin(angle));
301 | const rotationMatrix = [
302 | [Complex.ONE, Complex.ZERO],
303 | [Complex.ZERO, cosine.add(iSine)]
304 | ];
305 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, rotationMatrix);
306 | }
307 |
308 | r(targetBits, angle) {
309 | validateArgs(arguments, 2, 2, 'Must supply target bits and angle to r().');
310 | return this.controlledR(null, targetBits, angle);
311 | }
312 |
313 |
314 | R(targetBits, angle) {
315 | return this.r(targetBits, angle);
316 | }
317 |
318 | controlledX(controlBits, targetBits) {
319 | validateArgs(arguments, 2, 2, 'Must supply control and target bits to cnot() / controlledX().');
320 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, xMatrix);
321 | }
322 |
323 | cnot(controlBits, targetBits) {
324 | return this.controlledX(controlBits, targetBits);
325 | }
326 |
327 | x(targetBits) {
328 | validateArgs(arguments, 1, 1, 'Must supply target bits to x() / not().');
329 | return this.controlledX(null, targetBits);
330 | }
331 |
332 | not(targetBits) {
333 | return this.x(targetBits);
334 | }
335 |
336 | X(targetBits) {
337 | return this.x(targetBits);
338 | }
339 |
340 | controlledY(controlBits, targetBits) {
341 | validateArgs(arguments, 2, 2, 'Must supply control and target bits to controlledY().');
342 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, yMatrix);
343 | }
344 |
345 | y(targetBits) {
346 | validateArgs(arguments, 1, 1, 'Must supply target bits to y().');
347 | return this.controlledY(null, targetBits);
348 | }
349 |
350 | Y(targetBits) {
351 | return this.y(targetBits);
352 | }
353 |
354 | controlledZ(controlBits, targetBits) {
355 | validateArgs(arguments, 2, 2, 'Must supply control and target bits to controlledZ().');
356 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, zMatrix);
357 | }
358 |
359 | z(targetBits) {
360 | validateArgs(arguments, 1, 1, 'Must supply target bits to z().');
361 | return this.controlledZ(null, targetBits);
362 | }
363 |
364 | Z(targetBits) {
365 | return this.z(targetBits);
366 | }
367 |
368 | controlledS(controlBits, targetBits) {
369 | // Note this could actually be implemented as controlledR(controlBits, targetBits, PI/2)
370 | validateArgs(arguments, 2, 2, 'Must supply control and target bits to controlledS().');
371 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, sMatrix);
372 | }
373 |
374 | s(targetBits) {
375 | validateArgs(arguments, 1, 1, 'Must supply target bits to s().');
376 | return this.controlledS(null, targetBits);
377 | }
378 |
379 | S(targetBits) {
380 | return this.s(targetBits);
381 | }
382 |
383 | controlledT(controlBits, targetBits) {
384 | // Note this could actually be implemented as controlledR(controlBits, targetBits, PI/4)
385 | validateArgs(arguments, 2, 2, 'Must supply control and target bits to controlledT().');
386 | return this.controlledApplicationOfqBitOperator(controlBits, targetBits, tMatrix);
387 | }
388 |
389 | t(targetBits) {
390 | validateArgs(arguments, 1, 1, 'Must supply target bits to t().');
391 | return this.controlledT(null, targetBits);
392 | }
393 |
394 | T(targetBits) {
395 | return this.t(targetBits);
396 | }
397 |
398 | controlledSwap(controlBits, targetBit1, targetBit2) {
399 | validateArgs(arguments, 3, 3, 'Must supply controlBits, targetBit1, and targetBit2 to controlledSwap()');
400 | const newAmplitudes = {};
401 | if (controlBits != null) {
402 | controlBits = convertBitQualifierToBitArray(controlBits, this.numBits());
403 | }
404 | // TODO: make sure targetBit1 and targetBit2 are not contained in controlBits.
405 | const controlBitMask = createBitMask(controlBits);
406 | const bit1Mask = 1 << targetBit1;
407 | const bit2Mask = 1 << targetBit2;
408 | this.each((stateWithAmplitude) => {
409 | const state = stateWithAmplitude.asNumber();
410 | let newState = state;
411 | if (controlBits == null || ((state & controlBitMask) === controlBitMask)) {
412 | const newBit2 = ((state & bit1Mask) >> targetBit1) << targetBit2;
413 | const newBit1 = ((state & bit2Mask) >> targetBit2) << targetBit1;
414 | newState = (state & ~bit1Mask & ~bit2Mask) | newBit1 | newBit2;
415 | }
416 | newAmplitudes[newState] = stateWithAmplitude.amplitude;
417 | });
418 | return new QState(this.numBits(), newAmplitudes);
419 | }
420 |
421 | swap(targetBit1, targetBit2) {
422 | validateArgs(arguments, 2, 2, 'Must supply targetBit1 and targetBit2 to swap()');
423 | return this.controlledSwap(null, targetBit1, targetBit2);
424 | }
425 |
426 | /**
427 | * Toffoli takes one or more control bits (conventionally two) and one target bit.
428 | */
429 | toffoli(/* controlBit, controlBit, ..., targetBit */) {
430 | validateArgs(arguments, 2, 'At least one control bit and a target bit must be supplied to calls to toffoli()');
431 | const targetBit = arguments[arguments.length - 1];
432 | const controlBits = [];
433 | for (let i = 0; i < arguments.length - 1; i++) {
434 | controlBits.push(arguments[i]);
435 | }
436 | return this.controlledX(controlBits, targetBit);
437 | }
438 |
439 | static applyToOneBit(controlBits, targetBit, operatorMatrix, qState) {
440 | const newAmplitudes = {};
441 | const targetBitMask = 1 << targetBit;
442 | const inverseTargetBitMask = ~targetBitMask;
443 | const controlBitMask = createBitMask(controlBits);
444 |
445 | qState.each((stateWithAmplitude) => {
446 | const state = stateWithAmplitude.asNumber();
447 | if (controlBits == null || ((state & controlBitMask) === controlBitMask)) {
448 | const bitValue = ((targetBitMask & state) > 0) ? 1 : 0;
449 | const result = QState.applyOperatorMatrix(operatorMatrix, bitValue, stateWithAmplitude.amplitude);
450 | const zeroState = state & inverseTargetBitMask;
451 | const oneState = state | targetBitMask;
452 | sparseAdd(newAmplitudes, zeroState, result[0]);
453 | sparseAdd(newAmplitudes, oneState, result[1]);
454 | } else {
455 | newAmplitudes[state] = stateWithAmplitude.amplitude;
456 | }
457 | });
458 |
459 | return new QState(qState.numBits(), newAmplitudes);
460 | }
461 |
462 | controlledApplicationOfqBitOperator(controlBits, targetBits, operatorMatrix) {
463 | validateArgs(arguments, 3, 3, 'Must supply control bits, target bits, and qbitFunction to controlledApplicationOfqBitOperator().');
464 | const targetBitArray = convertBitQualifierToBitArray(targetBits, this.numBits());
465 | let controlBitArray = null;
466 | if (controlBits != null) {
467 | controlBitArray = convertBitQualifierToBitArray(controlBits, this.numBits());
468 | validateControlAndTargetBitsDontOverlap(controlBitArray, targetBitArray);
469 | }
470 | let result = this;
471 | for (let i = 0; i < targetBitArray.length; i++) {
472 | const targetBit = targetBitArray[i];
473 | result = QState.applyToOneBit(controlBitArray, targetBit, operatorMatrix, result);
474 | }
475 | return result;
476 | }
477 |
478 | applyFunction(inputBits, targetBits, functionToApply) {
479 | function validateTargetBitRangesDontOverlap(inputBitRange, targetBitRange) {
480 | if ((inputBitRange.to >= targetBitRange.from) && (targetBitRange.to >= inputBitRange.from)) {
481 | throw new Error('control and target bits must not be the same nor overlap');
482 | }
483 | }
484 |
485 | validateArgs(arguments, 3, 3, 'Must supply control bits, target bits, and functionToApply to applyFunction().');
486 | const qState = this;
487 | const inputBitRange = convertBitQualifierToBitRange(inputBits, this.numBits());
488 | const targetBitRange = convertBitQualifierToBitRange(targetBits, this.numBits());
489 | validateTargetBitRangesDontOverlap(inputBitRange, targetBitRange);
490 | const newAmplitudes = {};
491 | const statesThatCanBeSkipped = {};
492 | const highBitMask = (1 << (inputBitRange.to + 1)) - 1;
493 | const targetBitMask = ((1 << (1 + targetBitRange.to - targetBitRange.from)) - 1) << targetBitRange.from;
494 |
495 | this.each((stateWithAmplitude) => {
496 | const state = stateWithAmplitude.asNumber();
497 | if (statesThatCanBeSkipped[stateWithAmplitude.index]) return;
498 | const input = (state & highBitMask) >> inputBitRange.from;
499 | const result = (functionToApply(input) << targetBitRange.from) & targetBitMask;
500 | const resultingState = state ^ result;
501 | if (resultingState === state) {
502 | sparseAssign(newAmplitudes, state, stateWithAmplitude.amplitude);
503 | } else {
504 | statesThatCanBeSkipped[resultingState] = true;
505 | sparseAssign(newAmplitudes, state, qState.amplitude(resultingState));
506 | sparseAssign(newAmplitudes, resultingState, stateWithAmplitude.amplitude);
507 | }
508 | });
509 |
510 | return new QState(this.numBits(), newAmplitudes);
511 | }
512 |
513 | // Define random() as a function so that clients can replace it with their own
514 | // e.g.
515 | // jsqubits.QState.prototype.random = function() {.....};
516 | random() {
517 | return Math.random();
518 | }
519 |
520 | normalize() {
521 | const amplitudes = {};
522 | let sumOfMagnitudeSqaures = 0;
523 | this.each((stateWithAmplitude) => {
524 | const magnitude = stateWithAmplitude.amplitude.magnitude();
525 | sumOfMagnitudeSqaures += magnitude * magnitude;
526 | });
527 | const scale = new Complex(1 / Math.sqrt(sumOfMagnitudeSqaures));
528 | this.each((stateWithAmplitude) => {
529 | amplitudes[stateWithAmplitude.index] = stateWithAmplitude.amplitude.multiply(scale);
530 | });
531 | return new QState(this.numBits(), amplitudes);
532 | }
533 |
534 | measure(bits) {
535 | validateArgs(arguments, 1, 1, 'Must supply bits to be measured to measure().');
536 | const numBits = this.numBits();
537 | const bitArray = convertBitQualifierToBitArray(bits, numBits);
538 | const chosenState = chooseRandomBasisState(this);
539 | const bitMask = createBitMask(bitArray);
540 | const maskedChosenState = chosenState & bitMask;
541 |
542 | const newAmplitudes = {};
543 | this.each((stateWithAmplitude) => {
544 | const state = stateWithAmplitude.asNumber();
545 | if ((state & bitMask) === maskedChosenState) {
546 | newAmplitudes[state] = stateWithAmplitude.amplitude;
547 | }
548 | });
549 |
550 | // Measurement outcome is the "value" of the measured bits.
551 | // It probably only makes sense when the bits make an adjacent block.
552 | let measurementOutcome = 0;
553 | for (let bitIndex = numBits - 1; bitIndex >= 0; bitIndex--) {
554 | if (bitArray.indexOf(bitIndex) >= 0) {
555 | measurementOutcome <<= 1;
556 | if (chosenState & (1 << bitIndex)) {
557 | measurementOutcome += 1;
558 | }
559 | }
560 | }
561 |
562 | const newState = new QState(this.numBits(), newAmplitudes).normalize();
563 | return new Measurement(bitArray.length, measurementOutcome, newState);
564 | }
565 |
566 | qft(targetBits) {
567 | function qft(qstate, targetBitArray) {
568 | const bitIndex = targetBitArray[0];
569 | if (targetBitArray.length > 1) {
570 | qstate = qft(qstate, targetBitArray.slice(1));
571 | for (let index = 1; index < targetBitArray.length; index++) {
572 | const otherBitIndex = targetBitArray[index];
573 | const angle = 2 * Math.PI / (1 << (index + 1));
574 | qstate = qstate.controlledR(bitIndex, otherBitIndex, angle);
575 | }
576 | }
577 | return qstate.hadamard(bitIndex);
578 | }
579 |
580 | function reverseBits(qstate, targetBitArray) {
581 | while (targetBitArray.length > 1) {
582 | qstate = qstate.swap(targetBitArray[0], targetBitArray[targetBitArray.length - 1]);
583 | targetBitArray = targetBitArray.slice(1, targetBitArray.length - 1);
584 | }
585 | return qstate;
586 | }
587 |
588 | validateArgs(arguments, 1, 1, 'Must supply bits to be measured to qft().');
589 | const targetBitArray = convertBitQualifierToBitArray(targetBits, this.numBits());
590 | const newState = qft(this, targetBitArray);
591 | return reverseBits(newState, targetBitArray);
592 | }
593 |
594 | eql(other) {
595 | function lhsAmplitudesHaveMatchingRhsAmplitudes(lhs, rhs) {
596 | let result = true;
597 | lhs.each((stateWithAmplitude) => {
598 | result = stateWithAmplitude.amplitude.eql(rhs.amplitude(stateWithAmplitude.asNumber()));
599 | // Returning false short-circuits our "each" method.
600 | // This is cludgy. Really should be using a forAll method.
601 | return result;
602 | });
603 | return result;
604 | }
605 |
606 | if (!other) return false;
607 | if (!(other instanceof QState)) return false;
608 | if (this.numBits() !== other.numBits()) return false;
609 | return lhsAmplitudesHaveMatchingRhsAmplitudes(this, other) &&
610 | lhsAmplitudesHaveMatchingRhsAmplitudes(other, this);
611 | }
612 |
613 | equal(other) {
614 | return this.eql(other);
615 | }
616 |
617 | toString() {
618 | function formatAmplitude(amplitude, formatFlags) {
619 | const amplitudeString = amplitude.format(formatFlags);
620 | return amplitudeString === '1' ? '' : `(${amplitudeString})`;
621 | }
622 |
623 | function compareStatesWithAmplitudes(a, b) {
624 | return a.asNumber() - b.asNumber();
625 | }
626 |
627 | function sortedNonZeroStates(qState) {
628 | const nonZeroStates = [];
629 | qState.each((stateWithAmplitude) => {
630 | nonZeroStates.push(stateWithAmplitude);
631 | });
632 | nonZeroStates.sort(compareStatesWithAmplitudes);
633 | return nonZeroStates;
634 | }
635 |
636 | let stateWithAmplitude;
637 | let result = '';
638 | const formatFlags = {decimalPlaces: 4};
639 | const nonZeroStates = sortedNonZeroStates(this);
640 | for (let i = 0; i < nonZeroStates.length; i++) {
641 | if (result !== '') result += ' + ';
642 | stateWithAmplitude = nonZeroStates[i];
643 | result = `${result + formatAmplitude(stateWithAmplitude.amplitude, formatFlags)}|${stateWithAmplitude.asBitString()}>`;
644 | }
645 | return result;
646 | }
647 | }
648 |
649 | // Amplitudes with magnitudes smaller than QState.roundToZero this are rounded off to zero.
650 | QState.roundToZero = 0.0000001;
651 |
652 | // Make this a static constants once Safari supports it
653 | QState.ALL = "ALL";
654 |
--------------------------------------------------------------------------------
/docs/jsqubitsManual.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | jsqubits User Manual
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
jsqubits User Manual
19 |
20 |
21 | Function list:
22 |
23 |
24 |
25 | jsqubits is a JavaScript library for quantum computation simulation.
26 | Try the online
27 | jsqubits runner.
28 | Alternatively, you can download and use the JavaScript jsqubits.js library from the
29 | jsqubits github repository.
30 | It is fully self contained: no third party libraries are required.
31 | You can use it to implement quantum algorithms using JavaScript like this:
32 |
60 | QState is used to encapsulate the state of a quantum computer.
61 | You create one by either calling its constructor or using the convenient jsqubits(bitString) function.
62 |
63 |
64 | The jsqubits function takes a string zeros and ones representing the state of its qubits.
65 | The string may optionally be a "ket" in the Dirac or "bra-ket" notation.
66 |
67 |
68 |
69 |
70 |
jsqubits("0101")
71 |
creates a 4 qubit QState in the simple basis state of
72 |
|0101>
73 |
74 |
75 |
jsqubits("|0101>")
76 |
also creates a 4 qubit QState of
77 |
|0101>
78 |
79 |
80 |
81 |
82 |
83 | If you want your initial state to be in a super-position of states,
84 | then you can use use a number of convenience methods.
85 | QState.add()
86 | can be used to add one quantum state to another in a super-position,
87 | QState.normalize()
88 | can be used to normalize the amplitudes to ensure that the square of their magnitudes sums to 1,
89 | and QState.multiply()
90 | can be used to multiply all a state's amplitudes by any complex number.
91 | For example,
92 |
106 | Another way to create an initial super-position of states is to use the QState constructor directly,
107 | passing it the number of qubits, and a (sparse) array of complex number amplitudes. For example
108 |
109 |
110 |
111 | var a = []; a[0] = a[1] = a[4] = a[5] = jsqubits.complex(0.5, 0);
112 | new jsqubits.QState(4, a)
113 |
114 |
Currently, instead of copying the supplied amplitude array, a reference to it is used, and so any subsequent modifications will affect the QState object.
123 |
All QState operators return new QState objects; they do NOT modify the existing QState object.
124 |
125 |
126 |
127 |
128 |
129 |
130 |
Single qubit operators
131 |
132 |
x(targetBits)
133 |
y(targetBits)
134 |
z(targetBits)
135 |
r(targetBits, angle)
136 |
s(targetBits)
137 |
t(targetBits)
138 |
hadamard(targetBits)
139 |
not(targetBits)
140 |
rotateX(targetBits, angle)
141 |
rotateY(targetBits, angle)
142 |
rotateZ(targetBits, angle)
143 |
144 |
145 | A number of operators operate on a single qubit.
146 | For these, you must specify the index of the bit to be operated on, with the right most bit being bit 0,
147 | and the left most bit being bit n-1 for an n bit state.
148 | As a convenience, you can also specify a range of bits using an object with "from" and "to" properties,
149 | and the operator will be applied to each of the bits in that range (inclusive).
150 | The constant jsqubits.ALL can be used to apply the operator to all the bits.
151 | For most operators (except where indicated), you may also use an array of bit indexes.
152 |
153 |
154 |
155 | Here are some examples using the Pauli X operator.
156 |
157 |
158 |
159 |
160 |
161 |
jsqubits("|0000>").x(0)
162 |
gives
163 |
|0001>
164 |
165 |
166 |
jsqubits("|0000>").x({from: 0, to: 2})
167 |
gives
168 |
|0111>
169 |
170 |
171 |
jsqubits("|0000>").x(jsqubits.ALL)
172 |
gives
173 |
|1111>
174 |
175 |
176 |
jsqubits("|0000>").x([0,2])
177 |
gives
178 |
|0101>
179 |
180 |
181 |
182 |
183 |
184 | All qubit manipulations return a new QState and leave the original one untouched.
185 | Consider the following code:
186 |
187 |
188 |
189 | var state1 = jsqubits('|00>');
190 | var state2 = state1.x(1);
191 | "state2 is " + state2 + " but state1 is still " + state1
192 |
193 |
194 |
195 | This results in
196 | "state2 is |10> but state1 is still |00>"
197 |
198 |
199 |
200 | There are single bit operators for the Pauli operators X, Y, and Z.
201 | These can be invoked as x(), y(), z(), or as X(), Y(), or Z().
202 | There is also a hadamard() function.
203 | There is a "not" function, which is just an alias for X.
204 |
205 |
206 |
207 |
208 |
209 |
jsqubits("|00>").x(0)
210 |
gives
211 |
|01>
212 |
213 |
214 |
jsqubits("|01>").x(0)
215 |
gives
216 |
|00>
217 |
218 |
219 |
jsqubits("|00>").y(0)
220 |
gives
221 |
(i)|01>
222 |
223 |
224 |
jsqubits("|01>").y(0)
225 |
gives
226 |
(-i)|00>
227 |
228 |
229 |
jsqubits("|00>").z(0)
230 |
gives
231 |
|00>
232 |
233 |
234 |
jsqubits("|01>").z(0)
235 |
gives
236 |
(-1)|01>
237 |
238 |
239 |
jsqubits("|00>").not(0)
240 |
gives
241 |
|01>
242 |
243 |
244 |
jsqubits("|01>").not(0)
245 |
gives
246 |
|00>
247 |
248 |
249 |
jsqubits("|00>").hadamard(0)
250 |
gives
251 |
(0.7071)|00> + (0.7071)|01>
252 |
253 |
254 |
jsqubits("|01>").hadamard(0)
255 |
gives
256 |
(0.7071)|00> + (-0.7071)|01>
257 |
258 |
259 |
260 |
261 |
262 | The R operator is also known as the "phase shift" operator.
263 | It leaves the |0> state unchanged, but modifies the amplitude of the |1> state by eiθ,
264 | There are also the S and T operators (also known as the phase gate and π/8 gate respectively),
265 | which are equivalent to r(π/2) and r(π/4) respectively.
266 |
267 |
268 |
269 |
270 |
271 |
jsqubits("|00>").r(0, Math.PI/3)
272 |
gives
273 |
|00>
274 |
275 |
276 |
jsqubits("|01>").r(0, Math.PI/3)
277 |
gives
278 |
(0.5+0.866i)|01>
279 |
280 |
281 |
jsqubits("|00>").s(0)
282 |
gives
283 |
|00>
284 |
285 |
286 |
jsqubits("|01>").s(0)
287 |
gives
288 |
(i)|01>
289 |
290 |
291 |
jsqubits("|00>").t(0)
292 |
gives
293 |
|00>
294 |
295 |
296 |
jsqubits("|01>").t(0)
297 |
gives
298 |
(0.7071+0.7071i)|01>
299 |
300 |
301 |
302 |
303 |
304 | Other single qubit operators are those that rotate around the X, Y, and Z axis of the Bloch sphere.
305 | These take an angle and apply the operation
306 | e-iθX/2,
307 | e-iθY/2 or
308 | e-iθZ/2.
309 |
363 | There are controlled versions of all the single qubit operators.
364 | For these you must specify control bits as well as target bits.
365 | As with the target bits, the control bits may be a single bit index,
366 | a range of bits using an object with "from" and "to" properties,
367 | or an array of bit indexes.
368 | The operation is only performed when all of the control bits are ones.
369 | Note that the control and target bits must not have any bits in common.
370 |
371 |
372 |
373 | Here are some examples using the Pauli X operator.
374 |
375 |
376 |
377 |
378 |
379 |
jsqubits("|0000>").controlledX(2, 0)
380 |
leaves the state unchanged:
381 |
|0000>
382 |
383 |
384 |
jsqubits("|0100>").controlledX(2, 0)
385 |
flips bit zero to give:
386 |
|0101>
387 |
388 |
389 |
jsqubits("|0010>").controlledX([1,3], 0)
390 |
leaves the state unchanged:
391 |
|0010>
392 |
393 |
394 |
jsqubits("|1010>").controlledX([1,3], 0)
395 |
flips bit zero to give:
396 |
|1011>
397 |
398 |
399 |
jsqubits("|1010>").controlledX({from:1, to: 3}, 0)
400 |
leaves the state unchanged:
401 |
|1010>
402 |
403 |
404 |
jsqubits("|1110>").controlledX({from:1, to: 3}, 0)
405 |
flips bit zero to give:
406 |
|1111>
407 |
408 |
409 |
jsqubits("|1010>").controlledX([2,3], [0,1])
410 |
leaves the state unchanged:
411 |
|1010>
412 |
413 |
414 |
jsqubits("|1110>").controlledX([2,3], [0,1])
415 |
flips bit zero and one give:
416 |
|1101>
417 |
418 |
419 |
420 |
421 |
422 | In addition to controlledX(), there are controlledY(), controlledZ(), controlledR(), controlledS(), controlledT(),
423 | and controlledHadamard() functions.
424 | The function cnot() is an alias for controlledX().
425 | There are also controlledXRotation(), controlledYRotation(), and controlledZRotation() functions.
426 |
445 | The swap and controlledSwap operators allow you to swap two qubits:
446 |
447 |
448 |
449 |
450 |
jsqubits("|1110>").swap(1, 0)
451 |
gives
452 |
|1101>
453 |
454 |
455 |
jsqubits("|1110>").controlledSwap([2,3], 0, 1)
456 |
gives
457 |
|1101>
458 |
459 |
460 |
461 |
462 |
463 |
464 |
Measurement
465 |
466 |
measure(targetBits)
467 |
468 |
469 | The measurement operator is intended to simulate the act of measuring one or more of the qubits.
470 | As usual, you may specify a single bit index,
471 | a range of bits using an object with "from" and "to" properties,
472 | an array of bit indexes,
473 | or the constant jsqubits.ALL.
474 | As with all QState functions, the meaurement() function does not actually modify the object,
475 | but instead returns a Measurement object with
476 | a "newState" field containing the new quantum state (as a new QState object),
477 | and a "result" field containing the integer value of the resulting measurement
478 | (ie., the integer consisting of only the measured bits).
479 |
511 | Measurement objects have a convenient asBitString() method that returns the state of the measured qubits as a string of 0's and 1's.
512 | So, for example, jsqubits("|0110>").measure({from:1, to: 3}).asBitString() will result in
513 | the string 011.
514 |
515 |
516 |
517 | The random number generator used during measurement is exposed as
518 | a function called "random" on instances of QState and hence may be over-ridden
519 | by replacing the prototype function or replacing it on individual objects.
520 | Here is how you can get measurements to always choose the first basis state with
521 | a non-zero amplitude:
522 |
539 | Given that the controlledX() operator can take multiple control bits,
540 | a separate Toffoli operator is redundant,
541 | but has been added for the sake of convenience.
542 | It takes one or more control bit indexes as separate arguments,
543 | followed by a target bit index (which actually can instead be a bit range or array).
544 |
545 |
546 |
547 |
548 |
549 |
jsqubits("|0001>").toffoli(0, 2, 3)
550 |
leaves the state unchanged:
551 |
|0001>
552 |
553 |
554 |
jsqubits("|0101>").toffoli(0, 2, 3)
555 |
flips bit three to give:
556 |
|1101>
557 |
558 |
559 |
560 |
561 |
562 |
563 |
Quantum Fourier Transform
564 |
565 |
qft(targetBits)
566 |
567 |
568 | The qft method is a convenience method for computing the quantum fourier transform of a set of qubits.
569 | It is implemented on top of other more primitive operators, such as the Hadarmard and phase shift.
570 |
571 |
572 | jsqubits("|100>").qft([1,2]) will result in
573 | (0.5)|000> + (-0.5)|010> + (0.5)|100> + (-0.5)|110>.
574 |
583 | The applyFunction method takes
584 | an input bit specification,
585 | an output bit specification,
586 | and a function to apply.
587 | The input and output bit specifications must either be single bit indexes
588 | or bit ranges using "from" and "to" properties.
589 | Note that currently they cannot be arrays.
590 | The value of the bits specified by the input bit index (or range)
591 | is fed to the function, and the result is
592 | applied to the output bit (or range).
593 | For example,
594 |
603 | In the example, the function is passed the top two bits (ie, the value 2) and so it returns 3.
604 | This is to be applied to bits 0 to 2 and so the function's return value is
605 | treated as the bit string "011" and xor'ed with the bottom three bits to give:
606 | |10101>
607 |
608 |
609 |
610 |
611 |
Tensor Product
612 |
613 |
tensorProduct(otherState)
614 |
kron(otherState)
615 |
616 |
617 | The tensorProduct method of QState returns the Kronecker (or tensor) product.
618 | The kron method is an alias of tensorProduct.
619 |
620 |
621 | For example, jsqubits("|0110>").tensorProduct(jsqubits("|01>"))
622 | will result in |011001>.
623 |
624 |
625 |
626 |
627 |
amplitude
628 |
629 |
amplitude(basisState)
630 |
631 |
632 | The amplitude method can return the value of a specific amplitude.
633 |
634 |
635 |
636 |
637 |
jsqubits.roundToZero
638 |
639 | Note that, while the QState values are displayed to four decimal places, they are maintained to
640 | the maximum precision provided by the JavaScript implementation of floating point numbers,
641 | with the exception that numbers very close to zero are rounded off to zero.
642 | The value used for determining this rounding to zero is the variable jsqubits.roundToZero.
643 |
644 |
645 |
646 |
647 |
Complex
648 |
649 | Given that the amplitudes of a quantum state are complex numbers,
650 | jsqubits has a Complex class. The easiest way to create a complex number is using
651 | the method: jsqubits.complex(real, imaginary).
652 | If the number has no imaginary component, then the jsqubits.real(value)
653 | method is a convenient abbreviation of jsqubits.complex(value, 0).
654 |
741 |
742 |
743 | Automatic error checking of code samples has picked up some errors (highlighted in red).
744 |
745 |
746 | Unexpected error occurred: Unable to validate code!
747 |
748 |
749 | Code samples validated and found to be OK.
750 |
751 |