├── .nvmrc ├── .gitignore ├── examples ├── algorithms │ ├── README.md │ ├── all.js │ ├── deutsch.js │ ├── simpleSearch.js │ ├── bernsteinVazirani.js │ ├── superDenseCoding.js │ ├── deutschJozsa.js │ ├── groverSearch.js │ ├── quantumTeleportation.js │ ├── specialPeriodFinding.js │ ├── simonsAlg.js │ ├── generalPeriodFinding.js │ └── factoring.js └── simpleWebPage │ └── index.html ├── jsqubitsRunner ├── examples │ ├── deutsch.js.example │ ├── simpleSearch.js.example │ ├── superDenseCoding.js.example │ ├── bernsteinVazirani.js.example │ ├── deutschJozsa.js.example │ ├── oneCleanQbit.js │ ├── groverSearch.js.example │ ├── specialPeriodFinding.js.example │ ├── quantumTeleportation.js.example │ ├── simonsAlg.js.example │ ├── generalPeriodFinding.js.example │ └── factoring.js.example └── jsqubitsRunner.html ├── eslint.config.js ├── lib ├── utils │ ├── validateArgs.js │ └── typecheck.js ├── index.js ├── Measurement.js ├── Complex.js ├── QMath.js └── QState.js ├── resources ├── css │ ├── jsqubits.css │ ├── reset.css │ └── runner.css └── js │ ├── jsqubitsManual.js │ └── jsqubitsRunner.js ├── .github └── workflows │ └── node.js.yml ├── package.json ├── LICENSE ├── spec ├── qft.spec.js ├── utils │ └── typecheck.spec.js ├── qmath.spec.js ├── factoring.spec.js ├── complex.spec.js ├── simple_algorithms.spec.js └── qstate.spec.js ├── README.md └── docs └── jsqubitsManual.html /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.12.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *swo 10 | *swp 11 | 12 | pids 13 | logs 14 | results 15 | 16 | node_modules 17 | bower_components 18 | dist 19 | -------------------------------------------------------------------------------- /examples/algorithms/README.md: -------------------------------------------------------------------------------- 1 | To run an algorithm, use `npm`: 2 | 3 | eg (note that the path is relative to the location of `package.json`). 4 | 5 | $ npm install 6 | $ npm run example examples/algorithms/deutsch.js 7 | 8 | Or, to run them all: 9 | 10 | $ npm install 11 | $ npm run all-examples 12 | 13 | You can of course run them directly using `node`: 14 | 15 | $ npm install 16 | $ node ./deutsch.js 17 | -------------------------------------------------------------------------------- /examples/algorithms/all.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Load and run all the algorithms... 3 | */ 4 | 5 | import './bernsteinVazirani.js' 6 | import './deutsch.js' 7 | import './deutschJozsa.js' 8 | import './factoring.js' 9 | import './generalPeriodFinding.js' 10 | import './groverSearch.js' 11 | import './quantumTeleportation.js' 12 | import './simonsAlg.js' 13 | import './simpleSearch.js' 14 | import './specialPeriodFinding.js' 15 | import './superDenseCoding.js' 16 | 17 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/deutsch.js.example: -------------------------------------------------------------------------------- 1 | /* 2 | * Deutsch's Algorithm. 3 | * Determine the value of (f(0) + f(1)) mod 2 with a single invocation of f (where f is a single bit function) 4 | */ 5 | 6 | function deutsch(f) { 7 | return jsqubits('|01>').hadamard(jsqubits.ALL).applyFunction(1, 0, f).hadamard(jsqubits.ALL).measure(1).result; 8 | }; 9 | 10 | 11 | var f = promptForFunction("Enter a function that maps {0,1} to {0,1}", "function(x) {return (x + 1) % 2;}"); 12 | 13 | log("(f(0) + f(1)) mod 2 = " + deutsch(f)); -------------------------------------------------------------------------------- /examples/algorithms/deutsch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Deutsch's Algorithm. 3 | * Determine the value of (f(0) + f(1)) mod 2 with a single invocation of f (where f is a single bit function) 4 | */ 5 | 6 | import jsqubits from '../../lib/index.js' 7 | 8 | export function deutsch(f) { 9 | return jsqubits('|01>') 10 | .hadamard(jsqubits.ALL) 11 | .applyFunction(1, 0, f) 12 | .hadamard(jsqubits.ALL) 13 | .measure(1).result; 14 | } 15 | 16 | const f = function (x) { 17 | return (x + 1) % 2; 18 | }; 19 | 20 | console.log(`(f(0) + f(1)) mod 2 = ${deutsch(f)}`); 21 | 22 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import mocha from "eslint-plugin-mocha"; 2 | 3 | export default [ 4 | { 5 | files: ["lib/**/*.js"], 6 | languageOptions: { 7 | ecmaVersion: 2021, 8 | sourceType: "module", 9 | globals: { 10 | NodeJS: "readonly" 11 | } 12 | }, 13 | rules: {} 14 | }, 15 | { 16 | files: ["spec/**/*.spec.js"], 17 | languageOptions: { 18 | ecmaVersion: 2021, 19 | sourceType: "module" 20 | }, 21 | plugins: { mocha }, 22 | rules: { 23 | ...mocha.configs["recommended"].rules 24 | } 25 | } 26 | ]; 27 | -------------------------------------------------------------------------------- /lib/utils/validateArgs.js: -------------------------------------------------------------------------------- 1 | function validateArgs(args, minimum, ...remainingArgs) { 2 | let maximum = 10000; 3 | let message = `Must supply at least ${minimum} parameters.`; 4 | if (remainingArgs.length > 2) throw new Error('Internal error: too many arguments to validateArgs'); 5 | if (remainingArgs.length === 2) { 6 | [maximum, message] = remainingArgs; 7 | } else if (remainingArgs.length === 1) { 8 | [message] = remainingArgs; 9 | } 10 | if (args.length < minimum || args.length > maximum) { 11 | throw message; 12 | } 13 | } 14 | 15 | export default validateArgs; 16 | -------------------------------------------------------------------------------- /resources/css/jsqubits.css: -------------------------------------------------------------------------------- 1 | code.block { 2 | display: block; 3 | white-space: pre; 4 | } 5 | 6 | code { 7 | padding-left: 0.2em; 8 | padding-right: 0.2em; 9 | } 10 | 11 | .code { 12 | font-family: monospace; 13 | } 14 | 15 | table.samples td { 16 | padding: 0em 1em 0em 1em; 17 | } 18 | 19 | .error { 20 | background-color: red; 21 | } 22 | 23 | .warning { 24 | background-color: yellow; 25 | } 26 | 27 | .success { 28 | background-color: #90ee90; 29 | } 30 | 31 | #tableOfContents a { 32 | padding-right: 0.5em; 33 | } 34 | 35 | #validateCodeSamplesButton { 36 | font-size: xx-small; 37 | } 38 | 39 | div.author { 40 | font-size: small; 41 | } 42 | 43 | .withAuthor { 44 | margin-bottom: 0; 45 | } -------------------------------------------------------------------------------- /examples/algorithms/simpleSearch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * A simple search algorithm. 3 | * Given an oracle that returns true for only one number between zero and three, 4 | * the simple search function can determine which it is with only a single invocation. 5 | */ 6 | 7 | import jsqubits from '../../lib/index.js' 8 | 9 | export function simpleSearch(f) { 10 | const inputBits = { 11 | from: 1, 12 | to: 2 13 | }; 14 | return jsqubits('|001>') 15 | .hadamard(jsqubits.ALL) 16 | .applyFunction(inputBits, 0, f) 17 | .hadamard(inputBits) 18 | .z(inputBits) 19 | .controlledZ(2, 1) 20 | .hadamard(inputBits) 21 | .measure(inputBits) 22 | .result; 23 | }; 24 | 25 | 26 | const f = function (x) { 27 | return x === 2 ? 1 : 0; 28 | }; 29 | console.log(`f(x) = 1 for x = ${simpleSearch(f)}`); 30 | -------------------------------------------------------------------------------- /examples/simpleWebPage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jsqubits example 5 | 6 | 7 |

jsqubits sample

8 |

9 | Applying the Hadamard operator followed by the T operator to |0> gives 10 |

11 |

12 | 234756 mod 15 = 13 |

14 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/simpleSearch.js.example: -------------------------------------------------------------------------------- 1 | /* 2 | * A simple search algorithm. 3 | * Given an oracle that returns true for only one number between zero and three, 4 | * the simple search function can determine which it is with only a single invocation. 5 | */ 6 | 7 | function simpleSearch(f) { 8 | var inputBits = {from: 1, to: 2}; 9 | return jsqubits('|001>') 10 | .hadamard(ALL) 11 | .applyFunction(inputBits, 0, f) 12 | .hadamard(inputBits) 13 | .z(inputBits) 14 | .controlledZ(2, 1) 15 | .hadamard(inputBits) 16 | .measure(inputBits) 17 | .result; 18 | }; 19 | 20 | var f = promptForFunction("Enter a function f: f(x) = 1 for a single x between 0 and 3, and f(x) = 0 otherwise", "function(x) {return x === 2 ? 1 : 0}"); 21 | 22 | log("f(x) = 1 for x = " + simpleSearch(f)); -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | import QState from './QState.js'; 2 | import Complex from './Complex.js'; 3 | import * as QMath from './QMath.js'; 4 | import Measurement, {QStateComponent} from './Measurement.js'; 5 | 6 | export function Qubits(bitString) { 7 | return QState.fromBits(bitString); 8 | } 9 | 10 | Qubits.complex = (real, imaginary) => { 11 | return new Complex(real, imaginary); 12 | }; 13 | 14 | Qubits.real = (real) => { 15 | return new Complex(real, 0); 16 | }; 17 | 18 | Qubits.QMath = QMath; 19 | Qubits.Complex = Complex; 20 | Qubits.QState = QState; 21 | Qubits.Measurement = Measurement; 22 | Qubits.QStateComponent = QStateComponent; 23 | Qubits.ALL = QState.ALL; 24 | 25 | Qubits.ZERO = Complex.ZERO; 26 | Qubits.ONE = Complex.ONE; 27 | Qubits.SQRT2 = Complex.SQRT2; 28 | Qubits.SQRT1_2 = Complex.SQRT1_2; 29 | 30 | export default Qubits; 31 | // Also export as jsqubits to maintain backward compatibility 32 | export {Qubits as jsqubits}; 33 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [20.x, 22.x, 24.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v5 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v5 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm ci 29 | - run: npm run build --if-present 30 | - run: npm test 31 | -------------------------------------------------------------------------------- /lib/Measurement.js: -------------------------------------------------------------------------------- 1 | 2 | function padState(state, numBits) { 3 | const paddingLength = numBits - state.length; 4 | for (let i = 0; i < paddingLength; i++) { 5 | state = `0${state}`; 6 | } 7 | return state; 8 | } 9 | 10 | export default class Measurement { 11 | constructor(numBits, result, newState) { 12 | this.numBits = numBits; 13 | this.result = result; 14 | this.newState = newState; 15 | } 16 | 17 | toString() { 18 | return `{result: ${this.result}, newState: ${this.newState}}`; 19 | } 20 | 21 | asBitString() { 22 | return padState(this.result.toString(2), this.numBits); 23 | } 24 | } 25 | 26 | export class QStateComponent { 27 | constructor(numBits, index, amplitude) { 28 | this.numBits = numBits; 29 | this.index = index; 30 | this.amplitude = amplitude; 31 | } 32 | 33 | asNumber() { 34 | return parseInt(this.index, 10); 35 | } 36 | 37 | asBitString() { 38 | return padState(parseInt(this.index, 10).toString(2), this.numBits); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsqubits", 3 | "version": "2.1.3", 4 | "type": "module", 5 | "description": "Quantum computation simulation JavaScript library", 6 | "keywords": [ 7 | "quantum" 8 | ], 9 | "license": "MIT", 10 | "author": "David Kemp ", 11 | "files": [ 12 | "lib" 13 | ], 14 | "devDependencies": { 15 | "chai": "^6.2.1", 16 | "eslint": "^9.39.1", 17 | "eslint-plugin-mocha": "^11.2.0", 18 | "mocha": "^11.7.5", 19 | "sinon": "^21.0.0" 20 | }, 21 | "overrides": { 22 | "glob": "^13.0.0" 23 | }, 24 | "scripts": { 25 | "lint": "eslint --ext .js ./lib ./spec", 26 | "test-only": "mocha $NODE_DEBUG_OPTION --reporter spec \"spec/**/*.spec.js\"", 27 | "test": "npm run lint && npm run test-only", 28 | "example": "node $NODE_DEBUG_OPTION", 29 | "all-examples": "node $NODE_DEBUG_OPTION examples/algorithms/all.js", 30 | "clean": "rm -rf node_modules dist bower_components" 31 | }, 32 | "main": "./lib/index.js", 33 | "repository": "git://github.com/davidbkemp/jsqubits.git", 34 | "homepage": "https://davidbkemp.github.io/jsqubits/" 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012 David Kemp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction,including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice (including the next paragraph) 14 | shall be included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/superDenseCoding.js.example: -------------------------------------------------------------------------------- 1 | /* 2 | * Super Dense Coding. 3 | * If Alice and Bob share a pair of entangled qubits, then Alice can encode two classical bits into her one entangled qubit, 4 | * send it to Bob, and Bob can decode it with the help of his entangled qubit. 5 | */ 6 | 7 | function superDense(input) { 8 | var state = jsqubits('|00>').add(jsqubits('|11>')).normalize(); 9 | 10 | log("Initial Bell State: " + state); 11 | 12 | // Alice prepares her qbit 13 | var alice = 1; 14 | if (input.charAt(0) === '1') { 15 | state = state.z(alice); 16 | } 17 | if (input.charAt(1) === '1') { 18 | state = state.x(alice); 19 | } 20 | 21 | log("State after Alice prepares her qubit (the first one): " + state); 22 | // Alice sends her qbit to Bob 23 | var bob = 0; 24 | state = state.cnot(alice, bob).hadamard(alice); 25 | 26 | log("State after Bob receives Alice's qubit and 'decodes' it: " + state); 27 | return state.measure(ALL).asBitString(); 28 | }; 29 | 30 | var input = prompt("Two bit string to send", "10"); 31 | var transmittedState = superDense(input); 32 | log("Decoded string is: " + transmittedState); 33 | -------------------------------------------------------------------------------- /examples/algorithms/bernsteinVazirani.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Bernstein-Vazirani Algorithm: 3 | * Given f: f(x) = x.u, determine u. 4 | */ 5 | 6 | import jsqubits from '../../lib/index.js' 7 | 8 | export function bernsteinVazirani(f, numbits) { 9 | // Create a |-> state as the target qubit. 10 | const targetQubit = jsqubits('|0>').subtract(jsqubits('|1>')).normalize(); 11 | const inputQubits = new jsqubits.QState(numbits); 12 | const initialState = inputQubits.tensorProduct(targetQubit); 13 | 14 | const inputBits = {from: 1, to: numbits}; 15 | const targetBit = 0; 16 | return initialState 17 | .hadamard(inputBits) 18 | .applyFunction(inputBits, targetBit, f) 19 | .hadamard(inputBits) 20 | .measure(inputBits) 21 | .asBitString(); 22 | } 23 | 24 | export function createHiddenStringFunction(hiddenString) { 25 | const hiddenStringAsNumber = parseInt(hiddenString, 2); 26 | return function (x) { 27 | let product = x & hiddenStringAsNumber; 28 | let result = 0; 29 | while (product > 0) { 30 | if (product % 2 === 1) result++; 31 | product >>= 1; 32 | } 33 | return result; 34 | }; 35 | } 36 | 37 | const f = createHiddenStringFunction('01101'); 38 | console.log(`Hidden string is: ${bernsteinVazirani(f, 5)}`); 39 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/bernsteinVazirani.js.example: -------------------------------------------------------------------------------- 1 | /* 2 | * Bernstein-Vazirani Algorithm: 3 | * Given f: f(x) = x.u, determine u. 4 | */ 5 | 6 | function bernsteinVazirani(f, numbits) { 7 | // Create a |-> state as the target qubit. 8 | var targetQubit = jsqubits("|0>").subtract(jsqubits("|1>")).normalize(); 9 | var inputQubits = new jsqubits.QState(numbits); 10 | var initialState = inputQubits.tensorProduct(targetQubit); 11 | 12 | var inputBits = {from: 1, to: numbits}; 13 | var targetBit = 0; 14 | return initialState 15 | .hadamard(inputBits) 16 | .applyFunction(inputBits, targetBit, f) 17 | .hadamard(inputBits) 18 | .measure(inputBits) 19 | .asBitString(); 20 | }; 21 | 22 | function createHiddenStringFunction(hiddenString) { 23 | var hiddenStringAsNumber = parseInt(hiddenString, 2); 24 | return function(x) { 25 | var product = x & hiddenStringAsNumber; 26 | var result = 0; 27 | while (product > 0) { 28 | if (product % 2 === 1) result++; 29 | product = product >> 1; 30 | } 31 | return result; 32 | } 33 | }; 34 | 35 | var f = createHiddenStringFunction("01101"); 36 | log("Hidden string is: " + bernsteinVazirani(f, 5)); 37 | -------------------------------------------------------------------------------- /resources/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /examples/algorithms/superDenseCoding.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Super Dense Coding. 3 | * If Alice and Bob share a pair of entangled qubits, then Alice can encode two classical bits into her one entangled qubit, 4 | * send it to Bob, and Bob can decode it with the help of his entangled qubit. 5 | */ 6 | 7 | import jsqubits from '../../lib/index.js' 8 | 9 | export function superDense(input) { 10 | let state = jsqubits('|00>') 11 | .add(jsqubits('|11>')) 12 | .normalize(); 13 | 14 | console.log(`Initial Bell State: ${state}`); 15 | 16 | // Alice prepares her qbit 17 | const alice = 1; 18 | if (input.charAt(0) === '1') { 19 | state = state.z(alice); 20 | } 21 | if (input.charAt(1) === '1') { 22 | state = state.x(alice); 23 | } 24 | 25 | console.log(`State after Alice prepares her qubit (the first one): ${state}`); 26 | // Alice sends her qbit to Bob 27 | const bob = 0; 28 | state = state.cnot(alice, bob) 29 | .hadamard(alice); 30 | 31 | console.log(`State after Bob receives Alice's qubit and 'decodes' it: ${state}`); 32 | return state.measure(jsqubits.ALL) 33 | .asBitString(); 34 | }; 35 | 36 | // var input = prompt("Two bit string to send", "10"); 37 | const input = '10'; 38 | const result = superDense(input); 39 | console.log(`Decoded string is: ${result}`); 40 | -------------------------------------------------------------------------------- /lib/utils/typecheck.js: -------------------------------------------------------------------------------- 1 | const TYPES = { 2 | NULL: '[object Null]', 3 | OBJECT: '[object Object]', 4 | ARRAY: '[object Array]', 5 | STRING: '[object String]', 6 | NUMBER: '[object Number]', 7 | BOOLEAN: '[object Boolean]', 8 | DATE: '[object Date]', 9 | UNDEFINED: '[object Undefined]', 10 | REGEXP: '[object RegExp]', 11 | }; 12 | 13 | const getType = (prop) => { 14 | return Object.prototype.toString.call(prop); 15 | }; 16 | 17 | const isNull = (prop) => { 18 | return getType(prop) === TYPES.NULL; 19 | }; 20 | 21 | const isObject = (prop) => { 22 | return getType(prop) === TYPES.OBJECT; 23 | }; 24 | 25 | const isArray = (prop) => { 26 | return getType(prop) === TYPES.ARRAY; 27 | }; 28 | 29 | const isString = (prop) => { 30 | return getType(prop) === TYPES.STRING; 31 | }; 32 | 33 | const isNumber = (prop) => { 34 | return getType(prop) === TYPES.NUMBER; 35 | }; 36 | 37 | const isBoolean = (prop) => { 38 | return getType(prop) === TYPES.BOOLEAN; 39 | }; 40 | 41 | const isDate = (prop) => { 42 | return getType(prop) === TYPES.DATE; 43 | }; 44 | 45 | const isUndefined = (prop) => { 46 | return getType(prop) === TYPES.UNDEFINED; 47 | }; 48 | 49 | const isRegExp = (prop) => { 50 | return getType(prop) === TYPES.REGEXP; 51 | }; 52 | 53 | export default { 54 | isNull, 55 | isObject, 56 | isArray, 57 | isString, 58 | isNumber, 59 | isBoolean, 60 | isDate, 61 | isUndefined, 62 | isRegExp, 63 | }; 64 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/deutschJozsa.js.example: -------------------------------------------------------------------------------- 1 | /* 2 | * Deutsch-Jozsa Algorithm 3 | * Given a function over n bits that returns 0 or 1, and is guaranteed to be either constant or balanced 4 | * the Deutsch-Jozsa algorithm determines whether it is constant or balanced after only one invocation of the function. 5 | * Returns true if the function is constant. 6 | */ 7 | 8 | function deutschJozsa(f) { 9 | var inputBits = {from: 1, to: 3}; 10 | var result = jsqubits('|0001>') 11 | .hadamard(ALL) 12 | .applyFunction(inputBits, 0, f) 13 | .hadamard(inputBits) 14 | .measure(inputBits) 15 | .result; 16 | return result === 0; 17 | }; 18 | 19 | function shuffle(a) { 20 | for (var i = 0; i < a.length; i++) { 21 | var j = Math.floor(Math.random() * a.length); 22 | var x = a[i]; 23 | a[i] = a[j]; 24 | a[j] = x; 25 | } 26 | }; 27 | 28 | function createBalancedFunction() { 29 | // Return 0 for exactly half the possible inputs and 1 for the rest. 30 | var nums = [0,1,2,3,4,5,6,7]; 31 | shuffle(nums); 32 | return function(x) { return nums[x] < 4 ? 0 : 1 }; 33 | }; 34 | 35 | log("deutschJozsa(function(x) { return 0; }) equals " + deutschJozsa(function(x) { return 0; })); 36 | log("deutschJozsa(function(x) { return 1; }) equals " + deutschJozsa(function(x) { return 1; })); 37 | log("deutschJozsa(balancedFunction) equals " + deutschJozsa(createBalancedFunction())); 38 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/oneCleanQbit.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jqc1 is able to take a unitary function f and determine its trace in an efficient manner. 3 | */ 4 | 5 | function dqc1(f, n) { 6 | const real = traceReal(f, n); 7 | const imaginary = traceImaginary(f, n); 8 | return jsqubits.complex(real, imaginary); 9 | } 10 | 11 | function traceReal(f, n) { 12 | return traceComponent(f, n, (state) => { return state.rotateY(n, -Math.PI / 2); }); 13 | } 14 | 15 | function traceImaginary(f, n) { 16 | return -traceComponent(f, n, (state) => { return state.rotateX(n, Math.PI / 2); }); 17 | } 18 | 19 | // Find the trace of f on n qbits along one of the X or Y axis 20 | function traceComponent(f, n, rotation) { 21 | const numberOfMixedStates = Math.pow(2, n); 22 | const numberOfSamples = 1000; 23 | let sum = 0; 24 | for (let i = 0; i < numberOfSamples; i++) { 25 | const amplitudes = {}; 26 | amplitudes[Math.floor(Math.random() * numberOfMixedStates)] = jsqubits.complex(1, 0); 27 | let state = new jsqubits.QState(n + 1, amplitudes).hadamard(n); 28 | state = f(state); 29 | sum += rotation(state).measure(n).result * -2 + 1; 30 | } 31 | const trace = numberOfMixedStates * sum / numberOfSamples; 32 | return trace; 33 | } 34 | 35 | const identity = function (state) { return state; }; 36 | const pauli_x = function (state) { return state.controlledX(1, 0); }; 37 | const rotate_y = function (state) { return state.controlledYRotation(1, 0, Math.PI / 2); }; 38 | // dqc1(identity, 1); 39 | dqc1(rotate_y, 1); 40 | -------------------------------------------------------------------------------- /examples/algorithms/deutschJozsa.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Deutsch-Jozsa Algorithm 3 | * Given a function over n bits that returns 0 or 1, and is guaranteed to be either constant or balanced 4 | * the Deutsch-Jozsa algorithm determines whether it is constant or balanced after only one invocation of the function. 5 | * Returns true if the function is constant. 6 | */ 7 | 8 | import jsqubits from '../../lib/index.js' 9 | 10 | export function deutschJozsa(f) { 11 | const inputBits = { 12 | from: 1, 13 | to: 3 14 | }; 15 | const result = jsqubits('|0001>') 16 | .hadamard(jsqubits.ALL) 17 | .applyFunction(inputBits, 0, f) 18 | .hadamard(inputBits) 19 | .measure(inputBits) 20 | .result; 21 | return result === 0; 22 | }; 23 | 24 | function shuffle(a) { 25 | for (let i = 0; i < a.length; i++) { 26 | const j = Math.floor(Math.random() * a.length); 27 | const x = a[i]; 28 | a[i] = a[j]; 29 | a[j] = x; 30 | } 31 | } 32 | 33 | export function createBalancedFunction() { 34 | // Return 0 for exactly half the possible inputs and 1 for the rest. 35 | const nums = [0, 1, 2, 3, 4, 5, 6, 7]; 36 | shuffle(nums); 37 | return function (x) { 38 | return nums[x] < 4 ? 0 : 1; 39 | }; 40 | }; 41 | 42 | console.log(`deutschJozsa(function(x) { return 0; }) equals ${deutschJozsa(() => { 43 | return 0; 44 | })}`); 45 | console.log(`deutschJozsa(function(x) { return 1; }) equals ${deutschJozsa(() => { 46 | return 1; 47 | })}`); 48 | console.log(`deutschJozsa(function(x) { return x; }) equals ${deutschJozsa((x) => { 49 | return x; 50 | })}`); 51 | console.log(`deutschJozsa(balancedFunction) equals ${deutschJozsa(createBalancedFunction())}`); 52 | -------------------------------------------------------------------------------- /resources/js/jsqubitsManual.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | // Find code samples and compare the actual output with the stated output. 3 | $(() => { 4 | const validateCodeSamples = function () { 5 | let totalErrors = 0; 6 | 7 | $('.validationMessage').hide(); 8 | $('#codeValidationInProgress').show(); 9 | $('*[data-sampleref]').each(function () { 10 | try { 11 | const id = $(this).attr('data-sampleref'); 12 | const jscode = $(`#${id}`).text(); 13 | const result = eval(jscode).toString(); 14 | let expected = $(this).text(); 15 | if ($(this).attr('data-eval') === 'true') expected = eval(expected); 16 | if ($.trim(expected) !== $.trim(result)) throw 'no match'; 17 | } catch (e) { 18 | $(this).addClass('error'); 19 | totalErrors++; 20 | } 21 | }); 22 | 23 | $('#codeValidationInProgress').hide(); 24 | if (totalErrors > 0) { 25 | $('#codeValidationFailure').show(); 26 | } else { 27 | $('#codeValidationSuccess').show(); 28 | } 29 | }; 30 | 31 | $('#validateCodeSamplesButton').on('click', validateCodeSamples); 32 | 33 | const indexItems = []; 34 | const indexMap = []; 35 | $('ul.index li, h2.index').map(function () { 36 | const indexItem = $(this).text().split('(', 1)[0]; 37 | indexItems.push(indexItem); 38 | indexMap[indexItem] = $(this).closest('.section').attr('id'); 39 | }); 40 | indexItems.sort(); 41 | for (const i in indexItems) { 42 | if (indexItems.hasOwnProperty(i)) { 43 | const indexItem = indexItems[i]; 44 | const element = $('').attr('href', `#${indexMap[indexItem]}`).text(`${indexItem} `); 45 | $('#tableOfContents').append(element); 46 | } 47 | } 48 | }); 49 | }(jQuery)); 50 | -------------------------------------------------------------------------------- /resources/js/jsqubitsRunner.js: -------------------------------------------------------------------------------- 1 | // jsqubits 2 | // https://davidbkemp.github.io/jsqubits/ 3 | // (c) 2012 David Kemp 4 | // jsqubits may be freely distributed under the MIT license. 5 | 6 | (function (global, $) { 7 | global.ALL = 'ALL'; 8 | 9 | global.log = function (str) { 10 | $('#console').append($('
').text(str)); 11 | }; 12 | 13 | global.promptForFunction = function (message, example) { 14 | const input = prompt(message, example); 15 | let f; 16 | eval(`f = ${input}`); 17 | return f; 18 | }; 19 | 20 | function clearConsole() { 21 | $('#result').text(''); 22 | $('#console').html(''); 23 | } 24 | 25 | function clearAll() { 26 | $('#code').val(''); 27 | clearConsole(); 28 | } 29 | 30 | $(() => { 31 | $('#run').click(() => { 32 | clearConsole(); 33 | let result; 34 | try { 35 | result = eval($('#code').get(0).value); 36 | if ((typeof result === 'object') && result.toString) { 37 | result = result.toString(); 38 | } else if (typeof result === 'number') { 39 | result = `${result}`; 40 | } 41 | } catch (e) { 42 | result = e.message ? e.message : e; 43 | } 44 | if (result) { 45 | $('#result').text(result); 46 | } 47 | }); 48 | 49 | $('#clear').click(() => { 50 | clearAll(); 51 | $('#example').attr('value', 'none'); 52 | }); 53 | 54 | $('#example').change(function () { 55 | const selectedExample = $(this).val(); 56 | if (selectedExample === 'none') return; 57 | 58 | $.get(`examples/${selectedExample}.js.example`, (data) => { 59 | clearAll(); 60 | $('#code').val(data); 61 | }) 62 | .fail(() => { alert('Sorry. Something went wrong.'); }); 63 | }); 64 | }); 65 | }(this, jQuery)); 66 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/groverSearch.js.example: -------------------------------------------------------------------------------- 1 | /** 2 | * Grover's search algorithm. 3 | * See https://en.wikipedia.org/wiki/Grover's_algorithm 4 | */ 5 | 6 | var numBits = 8; 7 | var range = 1 << numBits; 8 | var inputBits = {from: 1, to: numBits}; 9 | var requiredNumberOfAmplifications = Math.floor(Math.sqrt(range) * Math.PI / 4); 10 | var attempts = 0; 11 | 12 | function search(f, callBack) { 13 | if (attempts === 6) { 14 | log("Giving up after " + attempts + " attempts"); 15 | callBack("failed"); 16 | return; 17 | } 18 | attempts++; 19 | 20 | var qstate = new jsqubits.QState(numBits).tensorProduct(jsqubits("|1>")).hadamard(jsqubits.ALL); 21 | var amplications = 0; 22 | 23 | function amplify() { 24 | if (amplications++ === requiredNumberOfAmplifications ) { 25 | var result = qstate.measure(inputBits).result; 26 | if (f(result) === 1) { 27 | callBack(result); 28 | return; 29 | } 30 | log("Failed result: " + result); 31 | search(f, callBack); 32 | return; 33 | } 34 | // Phase flip the target. 35 | qstate = qstate.applyFunction(inputBits, 0, f); 36 | // Reflect about the mean 37 | qstate = qstate.hadamard(inputBits).applyFunction(inputBits, 0, function(x){return x === 0 ? 1 : 0;}).hadamard(inputBits); 38 | log("Amplified " + amplications + " times."); 39 | setTimeout(amplify, 50); 40 | } 41 | 42 | amplify(); 43 | } 44 | 45 | var f = promptForFunction("Enter a function: f(x) = 1 for only one x less than " + range + " and f(x) = 0 otherwise", 46 | "function(x) {return x === 97 ? 1 : 0;}"); 47 | 48 | var startTime = new Date(); 49 | search(f, function(result) { 50 | log("The desired value is " + result); 51 | log("Time taken in seconds: " + ((new Date().getTime()) - startTime.getTime()) / 1000); 52 | }); 53 | -------------------------------------------------------------------------------- /examples/algorithms/groverSearch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Grover's search algorithm. 3 | * See https://en.wikipedia.org/wiki/Grover's_algorithm 4 | */ 5 | 6 | import jsqubits from '../../lib/index.js' 7 | 8 | function amplifyTargetAmplitude(qstate, f, inputBits) { 9 | // This is the core of Grover's algorithm. It amplifies the amplitude of the state for which f(x) = 1 10 | 11 | // Phase flip the target. 12 | qstate = qstate.applyFunction(inputBits, 0, f); 13 | // Reflect about the mean 14 | qstate = qstate.hadamard(inputBits) 15 | .applyFunction(inputBits, 0, function (x) { 16 | return x === 0 ? 1 : 0; 17 | }) 18 | .hadamard(inputBits); 19 | return qstate; 20 | } 21 | 22 | function search(f, range) { 23 | var qstate, 24 | attempts, 25 | amplifications; 26 | var numBits = Math.ceil(Math.log(range) / Math.log(2)); 27 | var inputBits = { 28 | from: 1, 29 | to: numBits 30 | }; 31 | var requiredNumberOfAmplifications = Math.floor(Math.sqrt(range) * Math.PI / 4); 32 | var result = 0; 33 | 34 | for (attempts = 0; f(result) !== 1 && attempts < 6; attempts++) { 35 | qstate = new jsqubits.QState(numBits).tensorProduct(jsqubits('|1>')) 36 | .hadamard(jsqubits.ALL); 37 | for (amplifications = 0; amplifications < requiredNumberOfAmplifications; amplifications++) { 38 | qstate = amplifyTargetAmplitude(qstate, f, inputBits); 39 | console.log('Amplified ' + amplifications + ' times.'); 40 | } 41 | result = qstate.measure(inputBits).result; 42 | } 43 | 44 | if (f(result) !== 1) { 45 | console.log('Giving up after ' + attempts + ' attempts'); 46 | result = 'failed'; 47 | } 48 | 49 | return result; 50 | } 51 | 52 | // A function: f(x) = 1 for exactly one x, and f(x) = 0 otherwise 53 | var range = 128; 54 | var f = function (x) { 55 | return x === 97 ? 1 : 0; 56 | }; 57 | 58 | var startTime = new Date(); 59 | var result = search(f, range); 60 | var timeTaken = ((new Date().getTime()) - startTime.getTime()) / 1000; 61 | console.log('The desired value is ' + result); 62 | console.log('Time taken in seconds: ' + timeTaken); 63 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/specialPeriodFinding.js.example: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Special case of the "Period Finding" problem. 4 | * For any function where f(x) = f(x + r) such that r is a power of two, this algorithm will find r. 5 | */ 6 | 7 | // The number of qubits in the quantum circuit used as "input" and "output" bits to f are numInBits and numOutBits respectively. 8 | // This number determines the size of the quantum circuit. It will have 2 * numOutBits qubits. 9 | // It limits the size of r for which we can find the period to 2^numOutBits. 10 | var numOutBits = 10; 11 | // For the special case where r is a power of 2, we can use the same number of input qubits as output qubits. 12 | var numInBits = numOutBits; 13 | 14 | function findPeriod(f) { 15 | var outBits = {from: 0, to: numOutBits - 1}; 16 | var inputBits = {from: numOutBits, to: numOutBits + numInBits - 1}; 17 | // gcd is the greatest common divisor of all the frequency samples found so far. 18 | var gcd = 0; 19 | 20 | // This function contains the actual quantum computation part of the algorithm. 21 | // It returns either the frequency of the function f or some integer multiple (where "frequency" is the number of times the period of f will fit into 2^numInputBits) 22 | function determineFrequency(f) { 23 | var qstate = new jsqubits.QState(numInBits + numOutBits).hadamard(inputBits); 24 | qstate = qstate.applyFunction(inputBits, outBits, f); 25 | // We do not need to measure the outBits, but it does speed up the simulation. 26 | qstate = qstate.measure(outBits).newState; 27 | return qstate.qft(inputBits).measure(inputBits).result; 28 | } 29 | 30 | // Do this multiple times and get the GCD. 31 | for (var i = 0; i < numOutBits; i ++) { 32 | gcd = jsqubitsmath.gcd(gcd, determineFrequency(f)); 33 | } 34 | return Math.pow(2, numInBits)/gcd; 35 | } 36 | 37 | var f = promptForFunction("Enter a function where f(x) = f(x+r) for some r that is a factor of " + Math.pow(2, numOutBits), "function(x) {return x % 16;}"); 38 | 39 | log("The period of your function is " + findPeriod(f)); 40 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/quantumTeleportation.js.example: -------------------------------------------------------------------------------- 1 | /* 2 | * The quantum teleportation algorithm 3 | * If Alice and Bob share a pair of entangled qubits, then Alice can transmit the state of a third qubit to Bob 4 | * by sending just two classical bits. 5 | */ 6 | 7 | function applyTeleportation(state) { 8 | var alicesMeasurement = state.cnot(2, 1).hadamard(2).measure({from: 1, to: 2}); 9 | var resultingState = alicesMeasurement.newState; 10 | if (alicesMeasurement.result & 1) { 11 | resultingState = resultingState.x(0); 12 | } 13 | if (alicesMeasurement.result & 2) { 14 | resultingState = resultingState.z(0); 15 | } 16 | return resultingState; 17 | }; 18 | 19 | // The state of the qubit to be transmitted consists of the amplitude of |0> and the amplitude of |1> 20 | // Any two complex values can be chosen here so long as the sum of their magnitudes adds up to 1. 21 | // For this example, we choose to transmit the state: 0.5+0.5i |0> + 0.7071i |1> 22 | var stateToBeTransmitted0 = jsqubits("|0>").multiply(jsqubits.complex(0.5, 0.5)); 23 | var stateToBeTransmitted1 = jsqubits("|1>").multiply(jsqubits.complex(0, Math.sqrt(0.5))); 24 | var stateToBeTransmitted = stateToBeTransmitted0.add(stateToBeTransmitted1); 25 | 26 | log("State to be transmitted: " + stateToBeTransmitted); 27 | 28 | // The quantum computer is initialized as a three qubit system with bits 0 and 1 being the bell state qbits shared by Alice and Bob 29 | // and bit 2 being the qubit to be transmitted. 30 | 31 | var bellState = jsqubits('|00>').add(jsqubits('|11>')).normalize(); 32 | var initialState = stateToBeTransmitted.tensorProduct(bellState); 33 | 34 | // Now apply the Teleportation algorithm 35 | var finalState = applyTeleportation(initialState); 36 | 37 | // By this stage, only bit zero has not been measured and it should have the same state the original state to be transmitted. 38 | // The other two bits will have random values but will be in definite states. 39 | // ie. finalState should be 40 | // stateToBeTransmitted0 |xx0> + stateToBeTransmitted1 |xx1> 41 | // where the bit values of bits 1 and 2 (xx) are identical in the two states. 42 | // If we had some way of projecting onto bit 0, we would have 43 | // stateToBeTransmitted0 |0> + stateToBeTransmitted1 |1> 44 | 45 | log("Final state: " + finalState); -------------------------------------------------------------------------------- /examples/algorithms/quantumTeleportation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * The quantum teleportation algorithm 3 | * If Alice and Bob share a pair of entangled qubits, then Alice can transmit the state of a third qubit to Bob 4 | * by sending just two classical bits. 5 | */ 6 | 7 | import jsqubits from '../../lib/index.js' 8 | 9 | const applyTeleportation = function (state) { 10 | const alicesMeasurement = state.cnot(2, 1) 11 | .hadamard(2) 12 | .measure({ 13 | from: 1, 14 | to: 2 15 | }); 16 | let resultingState = alicesMeasurement.newState; 17 | if (alicesMeasurement.result & 1) { 18 | resultingState = resultingState.x(0); 19 | } 20 | if (alicesMeasurement.result & 2) { 21 | resultingState = resultingState.z(0); 22 | } 23 | return resultingState; 24 | }; 25 | 26 | // The state of the qubit to be transmitted consists of the amplitude of |0> and the amplitude of |1> 27 | // Any two complex values can be chosen here so long as the sum of their magnitudes adds up to 1. 28 | // For this example, we choose to transmit the state: 0.5+0.5i |0> + 0.7071i |1> 29 | const stateToBeTransmitted0 = jsqubits('|0>') 30 | .multiply(jsqubits.complex(0.5, 0.5)); 31 | const stateToBeTransmitted1 = jsqubits('|1>') 32 | .multiply(jsqubits.complex(0, Math.sqrt(0.5))); 33 | const stateToBeTransmitted = stateToBeTransmitted0.add(stateToBeTransmitted1); 34 | 35 | console.log(`State to be transmitted: ${stateToBeTransmitted}`); 36 | 37 | // The quantum computer is initialized as a three qubit system with bits 0 and 1 being the bell state qbits shared by Alice and Bob 38 | // and bit 2 being the qubit to be transmitted. 39 | 40 | const bellState = jsqubits('|00>') 41 | .add(jsqubits('|11>')) 42 | .normalize(); 43 | const initialState = stateToBeTransmitted.tensorProduct(bellState); 44 | 45 | // Now apply the Teleportation algorithm 46 | const finalState = applyTeleportation(initialState); 47 | 48 | // By this stage, only bit zero has not been measured and it should have the same state the original state to be transmitted. 49 | // The other two bits will have random values but will be in definite states. 50 | // ie. finalState should be 51 | // stateToBeTransmitted0 |xx0> + stateToBeTransmitted1 |xx1> 52 | // where the bit values of bits 1 and 2 (xx) are identical in the two states. 53 | // If we had some way of projecting onto bit 0, we would have 54 | // stateToBeTransmitted0 |0> + stateToBeTransmitted1 |1> 55 | 56 | console.log(`Final state: ${finalState}`); 57 | -------------------------------------------------------------------------------- /examples/algorithms/specialPeriodFinding.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Special case of the "Period Finding" problem. 3 | * For any function where f(x) = f(x + r) such that r is a power of two, this algorithm will find r. 4 | */ 5 | 6 | import jsqubits from '../../lib/index.js' 7 | 8 | const jsqubitsmath = jsqubits.QMath 9 | 10 | export function findPeriod(f, upperLimit) { 11 | // The number of qubits in the quantum circuit used as "input" and "output" bits to f are numInBits and numOutBits respectively. 12 | // This number determines the size of the quantum circuit. It will have 2 * numOutBits qubits. 13 | // It limits the size of r for which we can find the period to 2^numOutBits. 14 | // For the special case where r is a power of 2, we can use the same number of input qubits as output qubits. 15 | const numOutBits = Math.ceil(Math.log(upperLimit) / Math.log(2)); 16 | const numInBits = numOutBits; 17 | const outBits = { 18 | from: 0, 19 | to: numOutBits - 1 20 | }; 21 | const inputBits = { 22 | from: numOutBits, 23 | to: numOutBits + numInBits - 1 24 | }; 25 | // gcd is the greatest common divisor of all the frequency samples found so far. 26 | let gcd = 0; 27 | 28 | // This function contains the actual quantum computation part of the algorithm. 29 | // It returns either the frequency of the function f or some integer multiple (where "frequency" is the number of times the period of f will fit into 2^numInputBits) 30 | function determineFrequency(f) { 31 | let qstate = new jsqubits.QState(numInBits + numOutBits).hadamard(inputBits); 32 | qstate = qstate.applyFunction(inputBits, outBits, f); 33 | // We do not need to measure the outBits, but it does speed up the simulation. 34 | qstate = qstate.measure(outBits).newState; 35 | return qstate.qft(inputBits) 36 | .measure(inputBits).result; 37 | } 38 | 39 | // Do this multiple times and get the GCD. 40 | for (let i = 0; i < numOutBits; i++) { 41 | gcd = jsqubitsmath.gcd(gcd, determineFrequency(f)); 42 | } 43 | return Math.pow(2, numInBits) / gcd; 44 | }; 45 | 46 | // var f = promptForFunction("Enter a function where f(x) = f(x+r) for some r that is a factor of " + Math.pow(2, numOutBits), "function(x) {return x % 16;}"); 47 | const f = function (x) { 48 | return x % 16; 49 | }; 50 | // Provide a guarantee on the upper limit of the period. 51 | const upperLimit = 32; 52 | 53 | console.log(`The period of your function is ${findPeriod(f, upperLimit)}`); 54 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/simonsAlg.js.example: -------------------------------------------------------------------------------- 1 | /** 2 | * Simon's algorithm. 3 | * See https://en.wikipedia.org/wiki/Simon's_algorithm 4 | */ 5 | 6 | 7 | function singleRunOfSimonsCircuit(f, numbits) { 8 | var inputBits = {from: numbits, to: 2 * numbits - 1}; 9 | var targetBits = {from: 0, to: numbits - 1}; 10 | var qbits = new jsqubits.QState(2 * numbits) 11 | .hadamard(inputBits) 12 | .applyFunction(inputBits, targetBits, f) 13 | .hadamard(inputBits); 14 | return qbits.measure(inputBits).result; 15 | } 16 | 17 | function findPotentialSolution(f, numBits) { 18 | var nullSpace = null; 19 | var results = []; 20 | var estimatedNumberOfIndependentSolutions = 0; 21 | for(var count = 0; count < 10 * numBits; count++) { 22 | var result = singleRunOfSimonsCircuit(f, numBits); 23 | if (results.indexOf(result) < 0) { 24 | results.push(result); 25 | estimatedNumberOfIndependentSolutions++; 26 | if (estimatedNumberOfIndependentSolutions == numBits - 1) { 27 | nullSpace = jsqubitsmath.findNullSpaceMod2(results, numBits); 28 | if (nullSpace.length == 1) break; 29 | estimatedNumberOfIndependentSolutions = numBits - nullSpace.length; 30 | } 31 | } 32 | } 33 | if (nullSpace === null) throw "Could not find a solution"; 34 | return nullSpace[0]; 35 | } 36 | 37 | function simonsAlgorithm(f, numBits) { 38 | var solution = findPotentialSolution(f, numBits); 39 | return (f(0) === f(solution)) ? solution : 0; 40 | }; 41 | 42 | var testFunction000 = (function() { 43 | var mapping = [3, 1, 4, 5, 7, 2, 0, 6]; 44 | return function(x) { 45 | return mapping[x]; 46 | }; 47 | })(); 48 | 49 | var testFunction110 = function(x) { 50 | var mapping = ['101', '010', '000', '110', '000', '110', '101', '010']; 51 | return parseInt(mapping[x], 2); 52 | }; 53 | 54 | var testFunction011 = function(x) { 55 | var mapping = ['010', '110', '110', '010', '100', '110', '110', '100']; 56 | return parseInt(mapping[x], 2); 57 | }; 58 | 59 | var testFunction1010 = (function() { 60 | var key = parseInt('1010', 2); 61 | var mapping = []; 62 | var valuesUsed = []; 63 | for (var i = 0; i < 16; i ++) { 64 | if (mapping[i] === undefined) { 65 | var value; 66 | for(;;) { 67 | value = Math.floor(Math.random() * 16); 68 | if (valuesUsed.indexOf(value) < 0) break; 69 | } 70 | mapping[i] = value; 71 | mapping[i ^ key] = value; 72 | valuesUsed.push(value); 73 | } 74 | } 75 | return function(x) { 76 | return mapping[x]; 77 | }; 78 | })(); 79 | 80 | log("The special string for testFunction000 is " + simonsAlgorithm(testFunction000, 3).toString(2)); 81 | log("The special string for testFunction011 is " + simonsAlgorithm(testFunction011, 3).toString(2)); 82 | log("The special string for testFunction110 is " + simonsAlgorithm(testFunction110, 3).toString(2)); 83 | log("The special string for testFunction1010 is " + simonsAlgorithm(testFunction1010, 4).toString(2)); 84 | -------------------------------------------------------------------------------- /spec/qft.spec.js: -------------------------------------------------------------------------------- 1 | import * as chai from 'chai'; 2 | import Q from '../lib/index.js'; 3 | const {QMath} = Q; 4 | const {expect} = chai; 5 | 6 | describe('QState.qft (Quantum Fourier Transform)', function() { 7 | 8 | it('Should be a Hadamard when applied to one bit', function() { 9 | const initialState = Q('|0>').add(Q('|1>')).normalize(); 10 | const newState = initialState.qft([0]); 11 | expect(newState.toString()).to.equal('|0>'); 12 | }); 13 | 14 | it('Should be a Hadamard when applied to all zeros', function() { 15 | const initialState = Q('|00>'); 16 | const newState = initialState.qft([0, 1]); 17 | expect(newState.toString()).to.equal('(0.5)|00> + (0.5)|01> + (0.5)|10> + (0.5)|11>'); 18 | }); 19 | 20 | it('Should return state to all zeros when applied twice.', function() { 21 | const initialState = Q('|0000>'); 22 | const newState = initialState.hadamard(Q.ALL).qft(Q.ALL); 23 | expect(newState.toString()).to.equal('|0000>'); 24 | }); 25 | 26 | it('Should transform |01> correctly', function() { 27 | const initialState = Q('|01>'); 28 | const newState = initialState.qft(Q.ALL); 29 | expect(newState.toString()).to.equal('(0.5)|00> + (0.5i)|01> + (-0.5)|10> + (-0.5i)|11>'); 30 | }); 31 | 32 | it('Should transform |001> correctly', function() { 33 | const initialState = Q('|001>'); 34 | const newState = initialState.qft(Q.ALL); 35 | expect(newState.toString()).to.equal('(0.3536)|000> + (0.25+0.25i)|001> + (0.3536i)|010> + (-0.25+0.25i)|011> + (-0.3536)|100> + (-0.25-0.25i)|101> + (-0.3536i)|110> + (0.25-0.25i)|111>'); 36 | }); 37 | 38 | it('Should transform |010> correctly', function() { 39 | const initialState = Q('|010>'); 40 | const newState = initialState.qft(Q.ALL); 41 | expect(newState.toString()).to.equal('(0.3536)|000> + (0.3536i)|001> + (-0.3536)|010> + (-0.3536i)|011> + (0.3536)|100> + (0.3536i)|101> + (-0.3536)|110> + (-0.3536i)|111>'); 42 | }); 43 | 44 | it('Should find the frequency of a simple periodic state', function() { 45 | const initialState = Q('|000>').add(Q('|100>')).normalize(); 46 | const newState = initialState.qft([0, 1, 2]); 47 | expect(newState.toString()).to.equal('(0.5)|000> + (0.5)|010> + (0.5)|100> + (0.5)|110>'); 48 | }); 49 | 50 | it('Should find the frequency of a simple periodic state offset by 1', function() { 51 | const initialState = Q('|001>').add(Q('|101>')).normalize(); 52 | const newState = initialState.qft([0, 1, 2]); 53 | expect(newState.toString()).to.equal('(0.5)|000> + (0.5i)|010> + (-0.5)|100> + (-0.5i)|110>'); 54 | }); 55 | 56 | it('Should find the frequency of a simple periodic function', function() { 57 | const inputBits = {from: 2, to: 4}; 58 | const outBits = {from: 0, to: 1}; 59 | let gcd = 0; 60 | // Do this 100 times since it is random and sometimes takes a long time :-) 61 | for (let i = 0; i < 100; i++) { 62 | let qstate = Q('|00000>').hadamard(inputBits); 63 | qstate = qstate.applyFunction(inputBits, outBits, (x) => { return x % 4; }); 64 | const result = qstate.qft(inputBits).measure(inputBits).result; 65 | gcd = QMath.gcd(gcd, result); 66 | } 67 | expect(gcd).to.equal(2); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /examples/algorithms/simonsAlg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simon's algorithm. 3 | * See https://en.wikipedia.org/wiki/Simon's_algorithm 4 | */ 5 | 6 | import jsqubits from '../../lib/index.js' 7 | 8 | const jsqubitsmath = jsqubits.QMath 9 | 10 | function singleRunOfSimonsCircuit(f, numbits) { 11 | const inputBits = { 12 | from: numbits, 13 | to: 2 * numbits - 1 14 | }; 15 | const targetBits = { 16 | from: 0, 17 | to: numbits - 1 18 | }; 19 | const qbits = new jsqubits.QState(2 * numbits) 20 | .hadamard(inputBits) 21 | .applyFunction(inputBits, targetBits, f) 22 | .hadamard(inputBits); 23 | return qbits.measure(inputBits).result; 24 | } 25 | 26 | function findPotentialSolution(f, numBits) { 27 | let nullSpace = null; 28 | const results = []; 29 | let estimatedNumberOfIndependentSolutions = 0; 30 | for (let count = 0; count < 10 * numBits; count++) { 31 | const result = singleRunOfSimonsCircuit(f, numBits); 32 | if (results.indexOf(result) < 0) { 33 | results.push(result); 34 | estimatedNumberOfIndependentSolutions++; 35 | if (estimatedNumberOfIndependentSolutions === numBits - 1) { 36 | nullSpace = jsqubitsmath.findNullSpaceMod2(results, numBits); 37 | if (nullSpace.length === 1) break; 38 | estimatedNumberOfIndependentSolutions = numBits - nullSpace.length; 39 | } 40 | } 41 | } 42 | if (nullSpace === null) throw 'Could not find a solution'; 43 | return nullSpace[0]; 44 | } 45 | 46 | const simonsAlgorithm = function (f, numBits) { 47 | if (arguments.length !== 2) throw new Error('Must supply a function and number of bits'); 48 | const solution = findPotentialSolution(f, numBits); 49 | return (f(0) === f(solution)) ? solution : 0; 50 | }; 51 | 52 | const testFunction000 = (function () { 53 | const mapping = [3, 1, 4, 5, 7, 2, 0, 6]; 54 | return function (x) { 55 | return mapping[x]; 56 | }; 57 | }()); 58 | 59 | const testFunction110 = function (x) { 60 | const mapping = ['101', '010', '000', '110', '000', '110', '101', '010']; 61 | return parseInt(mapping[x], 2); 62 | }; 63 | 64 | const testFunction011 = function (x) { 65 | const mapping = ['010', '110', '110', '010', '100', '110', '110', '100']; 66 | return parseInt(mapping[x], 2); 67 | }; 68 | 69 | const testFunction1010 = (function () { 70 | const key = 0b1010; 71 | const mapping = []; 72 | const valuesUsed = []; 73 | for (let i = 0; i < 16; i++) { 74 | if (mapping[i] === undefined) { 75 | var value; 76 | for (; ;) { 77 | value = Math.floor(Math.random() * 16); 78 | if (valuesUsed.indexOf(value) < 0) break; 79 | } 80 | mapping[i] = value; 81 | mapping[i ^ key] = value; 82 | valuesUsed.push(value); 83 | } 84 | } 85 | return function (x) { 86 | return mapping[x]; 87 | }; 88 | }()); 89 | 90 | console.log(`The special string for testFunction000 is ${simonsAlgorithm(testFunction000, 3) 91 | .toString(2)}`); 92 | console.log(`The special string for testFunction011 is ${simonsAlgorithm(testFunction011, 3) 93 | .toString(2)}`); 94 | console.log(`The special string for testFunction110 is ${simonsAlgorithm(testFunction110, 3) 95 | .toString(2)}`); 96 | console.log(`The special string for testFunction1010 is ${simonsAlgorithm(testFunction1010, 4) 97 | .toString(2)}`); 98 | -------------------------------------------------------------------------------- /spec/utils/typecheck.spec.js: -------------------------------------------------------------------------------- 1 | import * as chai from 'chai'; 2 | import typecheck from '../../lib/utils/typecheck.js'; 3 | 4 | const { expect } = chai; 5 | 6 | describe('typecheck', function() { 7 | describe('isNull', function() { 8 | it('should return true if null passed', function() { 9 | expect(typecheck.isNull(null)).to.equal(true); 10 | }); 11 | }); 12 | 13 | describe('isObject', function() { 14 | it('should return true if object passed', function() { 15 | expect(typecheck.isObject({})).to.equal(true); 16 | expect(typecheck.isObject(new Object())).to.equal(true); 17 | }); 18 | 19 | it('should return false if array passed', function() { 20 | expect(typecheck.isObject([])).to.equal(false); 21 | expect(typecheck.isObject(new Array())).to.equal(false); 22 | }); 23 | 24 | it('should return false if string passed', function() { 25 | expect(typecheck.isObject('jsqubit')).to.equal(false); 26 | expect(typecheck.isObject(new String('jsqubit'))).to.equal(false); 27 | }); 28 | 29 | it('should return false if number passed', function() { 30 | expect(typecheck.isObject(3)).to.equal(false); 31 | expect(typecheck.isObject(new Number(3))).to.equal(false); 32 | }); 33 | 34 | it('should return false if boolean passed', function() { 35 | expect(typecheck.isObject(true)).to.equal(false); 36 | expect(typecheck.isObject(new Boolean(true))).to.equal(false); 37 | }); 38 | 39 | it('should return false if date passed', function() { 40 | expect(typecheck.isObject(new Date())).to.equal(false); 41 | }); 42 | 43 | it('should return false if undefined passed', function() { 44 | expect(typecheck.isObject()).to.equal(false); 45 | expect(typecheck.isObject(undefined)).to.equal(false); 46 | }); 47 | 48 | it('should return false if regex passed', function() { 49 | expect(typecheck.isObject(/hello-world/g)).to.equal(false); 50 | expect(typecheck.isObject(new RegExp(/hello-world/g))).to.equal(false); 51 | }); 52 | }); 53 | 54 | describe('isArray', function() { 55 | it('should return true if array passed', function() { 56 | expect(typecheck.isArray([])).to.equal(true); 57 | expect(typecheck.isArray(new Array())).to.equal(true); 58 | }); 59 | }); 60 | 61 | describe('isString', function() { 62 | it('should return true if string passed', function() { 63 | expect(typecheck.isString('jsqubit')).to.equal(true); 64 | expect(typecheck.isString(new String('jsqubit'))).to.equal(true); 65 | }); 66 | }); 67 | 68 | describe('isNumber', function() { 69 | it('should return true if number passed', function() { 70 | expect(typecheck.isNumber(3)).to.equal(true); 71 | expect(typecheck.isNumber(new Number(3))).to.equal(true); 72 | }); 73 | }); 74 | 75 | describe('isBoolean', function() { 76 | it('should return true if boolean passed', function() { 77 | expect(typecheck.isBoolean(true)).to.equal(true); 78 | expect(typecheck.isBoolean(new Boolean(true))).to.equal(true); 79 | }); 80 | }); 81 | 82 | describe('isDate', function() { 83 | it('should return true if date passed', function() { 84 | expect(typecheck.isDate(new Date())).to.equal(true); 85 | }); 86 | }); 87 | 88 | describe('isRegExp', function() { 89 | it('should return true if regexp passed', function() { 90 | expect(typecheck.isRegExp(/hello-world/g)).to.equal(true); 91 | expect(typecheck.isRegExp(new RegExp(/hello-world/g))).to.equal(true); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /lib/Complex.js: -------------------------------------------------------------------------------- 1 | import validateArgs from './utils/validateArgs.js'; 2 | import typecheck from './utils/typecheck.js'; 3 | 4 | export default class Complex { 5 | constructor(real, imaginary) { 6 | validateArgs(arguments, 1, 2, 'Must supply a real, and optionally an imaginary, argument to Complex()'); 7 | imaginary = imaginary || 0; 8 | this.real = real; 9 | this.imaginary = imaginary; 10 | } 11 | 12 | add(other) { 13 | validateArgs(arguments, 1, 1, 'Must supply 1 parameter to add()'); 14 | if (typecheck.isNumber(other)) { 15 | return new Complex(this.real + other, this.imaginary); 16 | } 17 | return new Complex(this.real + other.real, this.imaginary + other.imaginary); 18 | } 19 | 20 | multiply(other) { 21 | validateArgs(arguments, 1, 1, 'Must supply 1 parameter to multiply()'); 22 | if (typecheck.isNumber(other)) { 23 | return new Complex(this.real * other, this.imaginary * other); 24 | } 25 | return new Complex( 26 | this.real * other.real - this.imaginary * other.imaginary, 27 | this.real * other.imaginary + this.imaginary * other.real 28 | ); 29 | } 30 | 31 | conjugate() { 32 | return new Complex(this.real, -this.imaginary); 33 | } 34 | 35 | toString() { 36 | if (this.imaginary === 0) return `${this.real}`; 37 | let imaginaryString; 38 | if (this.imaginary === 1) { 39 | imaginaryString = 'i'; 40 | } else if (this.imaginary === -1) { 41 | imaginaryString = '-i'; 42 | } else { 43 | imaginaryString = `${this.imaginary}i`; 44 | } 45 | if (this.real === 0) return imaginaryString; 46 | const sign = (this.imaginary < 0) ? '' : '+'; 47 | return this.real + sign + imaginaryString; 48 | } 49 | 50 | inspect() { 51 | return this.toString(); 52 | } 53 | 54 | format(options) { 55 | let realValue = this.real; 56 | let imaginaryValue = this.imaginary; 57 | if (options && options.decimalPlaces != null) { 58 | const roundingMagnitude = Math.pow(10, options.decimalPlaces); 59 | realValue = Math.round(realValue * roundingMagnitude) / roundingMagnitude; 60 | imaginaryValue = Math.round(imaginaryValue * roundingMagnitude) / roundingMagnitude; 61 | } 62 | const objectToFormat = new Complex(realValue, imaginaryValue); 63 | return objectToFormat.toString(); 64 | } 65 | 66 | negate() { 67 | return new Complex(-this.real, -this.imaginary); 68 | } 69 | 70 | magnitude() { 71 | return Math.sqrt(this.real * this.real + this.imaginary * this.imaginary); 72 | } 73 | 74 | phase() { 75 | // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 76 | return Math.atan2(this.imaginary, this.real); 77 | } 78 | 79 | 80 | subtract(other) { 81 | validateArgs(arguments, 1, 1, 'Must supply 1 parameter to subtract()'); 82 | if (typecheck.isNumber(other)) { 83 | return new Complex(this.real - other, this.imaginary); 84 | } 85 | return new Complex(this.real - other.real, this.imaginary - other.imaginary); 86 | } 87 | 88 | eql(other) { 89 | if (!(other instanceof Complex)) return false; 90 | return this.real === other.real && this.imaginary === other.imaginary; 91 | } 92 | 93 | equal(other) { 94 | return this.eql(other); 95 | } 96 | 97 | equals(other) { 98 | return this.eql(other); 99 | } 100 | 101 | closeTo(other) { 102 | return Math.abs(this.real - other.real) < 0.0001 && Math.abs(this.imaginary - other.imaginary) < 0.0001; 103 | } 104 | 105 | } 106 | 107 | /* 108 | * These should all be made static constants once Safari supports them. 109 | */ 110 | Complex.ZERO = new Complex(0, 0); 111 | Complex.ONE = new Complex(1, 0); 112 | Complex.SQRT2 = new Complex(Math.SQRT2, 0); 113 | Complex.SQRT1_2 = new Complex(Math.SQRT1_2, 0); 114 | -------------------------------------------------------------------------------- /resources/css/runner.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-size: 100%; 4 | font-family: sans-serif; 5 | margin: 1em 5%; 6 | line-height: 1.2; 7 | } 8 | 9 | h1 { 10 | line-height: 1; 11 | font-size: 2em; 12 | font-weight: bold; 13 | font-family: serif; 14 | padding: 0.2em 0 0.4em 0; 15 | } 16 | 17 | h2 { 18 | line-height: 1; 19 | font-size: 1.5em; 20 | font-weight: bold; 21 | font-style: italic; 22 | font-family: serif; 23 | padding: 0.1em 0 0.4em 0; 24 | } 25 | 26 | h3 { 27 | font-size: 1.25em; 28 | font-weight: bold; 29 | padding: 0.1em 0 0.4em 0; 30 | } 31 | 32 | h4 { 33 | font-weight: bold; 34 | padding: 0.5em 0 0.4em 0; 35 | } 36 | 37 | p { 38 | padding: 0.4em 0; 39 | } 40 | 41 | #code { 42 | width: 100%; 43 | font-size: 1em; 44 | } 45 | 46 | .buttons { 47 | display: inline-block; 48 | } 49 | 50 | button { 51 | margin-top: 1em; 52 | font-size: 80%; 53 | margin-right: 1em; 54 | -webkit-border-radius: 4px; 55 | -moz-border-radius: 4px; 56 | border-radius: 4px; 57 | } 58 | 59 | #run { 60 | color: rgb(7, 26, 145); 61 | font-weight: bold; 62 | padding-left: 2em; 63 | padding-right: 2em; 64 | } 65 | 66 | #run:active { 67 | color: rgba(7, 26, 145, 0.7); 68 | } 69 | 70 | .examples { 71 | display: inline-block; 72 | border: 1px solid black; 73 | padding: 0.5em; 74 | margin-top: 1em; 75 | } 76 | 77 | .result { 78 | margin-top:1em; 79 | min-height:2em; 80 | border-style:solid; 81 | border-width:1px; 82 | text-align: left; 83 | font-size: 1em; 84 | margin-bottom: 1em; 85 | } 86 | 87 | .commandlist { 88 | border-style: solid; 89 | border-width: 1px; 90 | white-space: nowrap; 91 | font-size: 0.75em 92 | } 93 | 94 | .commandlist.first { 95 | border-bottom: none; 96 | } 97 | 98 | .commandlist.second { 99 | border-top: none; 100 | } 101 | 102 | .row:nth-child(odd) { background-color:#ddd; } 103 | 104 | .row { 105 | padding: 0.5em 0.5em; 106 | } 107 | 108 | .method { 109 | font-weight: bold; 110 | } 111 | 112 | .desc { 113 | padding-left: 1em; 114 | } 115 | 116 | ul { 117 | list-style-type: disc; 118 | margin-left: 1em; 119 | } 120 | 121 | small { 122 | font-size: 0.6em; 123 | } 124 | 125 | /** 126 | * See http://nicolasgallagher.com/micro-clearfix-hack/ 127 | * For modern browsers 128 | * 1. The space content is one way to avoid an Opera bug when the 129 | * contenteditable attribute is included anywhere else in the document. 130 | * Otherwise it causes space to appear at the top and bottom of elements 131 | * that are clearfixed. 132 | * 2. The use of `table` rather than `block` is only necessary if using 133 | * `:before` to contain the top-margins of child elements. 134 | */ 135 | .clearfix:before, 136 | .clearfix:after { 137 | content: " "; /* 1 */ 138 | display: table; /* 2 */ 139 | } 140 | 141 | .clearfix:after { 142 | clear: both; 143 | } 144 | 145 | /** 146 | * For IE 6/7 only 147 | * Include this rule to trigger hasLayout and contain floats. 148 | */ 149 | .clearfix { 150 | *zoom: 1; 151 | } 152 | 153 | @media screen and (min-width: 600px) { 154 | .commandlist { 155 | font-size: 0.875em; 156 | } 157 | } 158 | 159 | @media screen and (min-width: 768px) { 160 | .method { 161 | width: 25em; 162 | float: left; 163 | } 164 | } 165 | 166 | @media screen and (min-width: 1200px) { 167 | 168 | .commandlist { 169 | width: 49.2481203%; 170 | } 171 | 172 | .commandlist.first { 173 | float: left; 174 | border-bottom-style: solid; 175 | border-width: 1px; 176 | } 177 | 178 | .commandlist.second { 179 | float: right; 180 | border-top-style: solid; 181 | border-width: 1px; 182 | } 183 | } 184 | 185 | 186 | @media screen and (min-width: 1500px) { 187 | .commandlist { 188 | font-size: 1em; 189 | } 190 | } -------------------------------------------------------------------------------- /examples/algorithms/generalPeriodFinding.js: -------------------------------------------------------------------------------- 1 | /** 2 | * General case of the "Period Finding" problem. 3 | * For any function where f(x) = f(x + r), this algorithm will find r. 4 | * Examples are: 5 | * function(x) {return x % 17;} 6 | * function(x) {return 30 + Math.round(30 * Math.sin(Math.PI * x / 10));} 7 | * function(x) {return (x % 17) == 0 ? 1 : 0;} // NOTE: This often fails! 8 | */ 9 | 10 | import jsqubits from '../../lib/index.js' 11 | 12 | const jsqubitsmath = jsqubits.QMath 13 | 14 | export function findPeriod(f, upperLimit) { 15 | // The number of qubits in the quantum circuit used as "input" and "output" bits to f are numInBits and numOutBits respectively. 16 | // They limit the size of r for which we can find the period to 2^numOutBits. 17 | const numOutBits = Math.ceil(Math.log(upperLimit) / Math.log(2)); 18 | const numInBits = 2 * numOutBits; 19 | const inputRange = Math.pow(2, numInBits); 20 | const outputRange = Math.pow(2, numOutBits); 21 | const accuracyRequiredForContinuedFraction = 1 / (2 * outputRange * outputRange); 22 | const outBits = { 23 | from: 0, 24 | to: numOutBits - 1 25 | }; 26 | const inputBits = { 27 | from: numOutBits, 28 | to: numOutBits + numInBits - 1 29 | }; 30 | let attempts = 0; 31 | let successes = 0; 32 | let bestSoFar = 1; 33 | let bestSoFarIsAPeriod = false; 34 | const f0 = f(0); 35 | 36 | // This function contains the actual quantum computation part of the algorithm. 37 | // It returns either the frequency of the function f or some integer multiple (where "frequency" is the number of times the period of f will fit into 2^numInputBits) 38 | function determineFrequency(f) { 39 | let qstate = new jsqubits.QState(numInBits + numOutBits).hadamard(inputBits); 40 | qstate = qstate.applyFunction(inputBits, outBits, f); 41 | // We do not need to measure the outBits, but it does speed up the simulation. 42 | qstate = qstate.measure(outBits).newState; 43 | return qstate.qft(inputBits) 44 | .measure(inputBits).result; 45 | } 46 | 47 | console.log(`Using ${numOutBits} output bits and ${numInBits} input bits`); 48 | 49 | // If we have had numOutBits successful "samples" then we have probably got the right answer. 50 | // But give up and use our best value so far if we have had too many attempts. 51 | while (successes < numOutBits && attempts < 2 * numOutBits) { 52 | const sample = determineFrequency(f); 53 | 54 | // Each "sample" has a high probability of being approximately equal to some integer multiple of (inputRange/r) rounded to the nearest integer. 55 | // So we use a continued fraction function to find r (or a divisor of r). 56 | const continuedFraction = jsqubitsmath.continuedFraction(sample / inputRange, accuracyRequiredForContinuedFraction); 57 | // The denominator is a "candidate" for being r or a divisor of r (hence we need to find the least common multiple of several of these). 58 | const candidateDivisor = continuedFraction.denominator; 59 | console.log(`Candidate divisor of r: ${candidateDivisor}`); 60 | // Reduce the chances of getting the wrong answer by ignoring obviously wrong results! 61 | if (candidateDivisor <= outputRange && candidateDivisor > 1) { 62 | // The period r should be the least common multiple of all of our candidate values (each is of the form k*r for random integer k). 63 | const lcm = jsqubitsmath.lcm(candidateDivisor, bestSoFar); 64 | if (lcm <= outputRange) { 65 | console.log('This is a good candidate.'); 66 | bestSoFar = lcm; 67 | if (f(bestSoFar) === f0) bestSoFarIsAPeriod = true; 68 | successes++; 69 | } else if (!bestSoFarIsAPeriod && f(candidateDivisor) === f0) { 70 | // It can occasionally happen that our current best estimate of the period is a complete dead end, but we stumble across a much better one. 71 | console.log('This is a much better candidate'); 72 | bestSoFar = candidateDivisor; 73 | bestSoFarIsAPeriod = true; 74 | successes++; 75 | } 76 | } 77 | attempts++; 78 | console.log(`Least common multiple: ${bestSoFar}. Attempts: ${attempts}. Good candidates: ${successes}`); 79 | } 80 | 81 | return bestSoFar; 82 | }; 83 | 84 | // A function where f(x) = f(x+r) for some r (to be found). 85 | const f = function (x) { 86 | return x % 19; 87 | }; 88 | // Provide a guarantee on the upper limit of the period. 89 | const upperLimit = 20; 90 | 91 | const period = findPeriod(f, upperLimit); 92 | 93 | if (f(0) === f(period)) { 94 | console.log(`The period of your function is ${period}`); 95 | } else { 96 | console.log(`Could not find period. Best effort was: ${period}`); 97 | } 98 | -------------------------------------------------------------------------------- /spec/qmath.spec.js: -------------------------------------------------------------------------------- 1 | import * as chai from 'chai'; 2 | import Q from '../lib/index.js'; 3 | const {QMath} = Q; 4 | const {expect} = chai; 5 | 6 | describe('jsqubitsmath', function() { 7 | describe('#powerMod', function() { 8 | it('should return 1 for x^0 mod 35', function() { 9 | expect(QMath.powerMod(2, 0, 35)).to.equal(1); 10 | }); 11 | 12 | it('should give 16 for 2^4 mod 35', function() { 13 | expect(QMath.powerMod(2, 4, 35)).to.equal(16); 14 | }); 15 | 16 | it('should give 32 for 2^5 mod 35', function() { 17 | expect(QMath.powerMod(2, 5, 35)).to.equal(32); 18 | }); 19 | 20 | it('should give 11 for 3^4 mod 70', function() { 21 | expect(QMath.powerMod(3, 4, 70)).to.equal(11); 22 | }); 23 | }); 24 | 25 | describe('#primePowerFactor', function() { 26 | it('should return 0 for 35', function() { 27 | expect(QMath.powerFactor(35)).to.equal(0); 28 | }); 29 | 30 | it('should return 2 for 2^6', function() { 31 | expect(QMath.powerFactor(Math.pow(2, 6))).to.equal(2); 32 | }); 33 | 34 | it('should return 5 for 5^6', function() { 35 | expect(QMath.powerFactor(Math.pow(5, 6))).to.equal(5); 36 | }); 37 | }); 38 | 39 | describe('#nullSpace', function() { 40 | it('should solve Ax=0 (single solution)', function() { 41 | const a = [ 42 | 0b001, 43 | 0b111, 44 | 0b110, 45 | 0b000 46 | ]; 47 | const results = QMath.findNullSpaceMod2(a, 3); 48 | expect(results).to.deep.equal([0b110]); 49 | }); 50 | 51 | it('should solve Ax=0 (three bits in solution)', function() { 52 | const a = [ 53 | 0b101, 54 | 0b011, 55 | ]; 56 | const results = QMath.findNullSpaceMod2(a, 3); 57 | expect(results).to.deep.equal([0b111]); 58 | }); 59 | 60 | it('should solve Ax=0 (many solutions)', function() { 61 | // Should reduce to 62 | // 0101101 63 | // 0000011 64 | const a = [ 65 | 0b0101110, 66 | 0b0101101 67 | ]; 68 | const results = QMath.findNullSpaceMod2(a, 7); 69 | expect(results.sort()).to.deep.equal([ 70 | 0b1000000, 71 | 0b0010000, 72 | 0b0101000, 73 | 0b0100100, 74 | 0b0100011, 75 | ].sort()); 76 | }); 77 | }); 78 | 79 | describe('gcd', function() { 80 | it('should compute the greatest common divisor of 27 and 18 as 9', function() { 81 | expect(QMath.gcd(27, 18)).to.equal(9); 82 | expect(QMath.gcd(18, 27)).to.equal(9); 83 | }); 84 | 85 | it('should compute the greatest common divisor of 27 and 12 as 3', function() { 86 | expect(QMath.gcd(27, 12)).to.equal(3); 87 | expect(QMath.gcd(12, 27)).to.equal(3); 88 | }); 89 | }); 90 | 91 | describe('lcm', function() { 92 | it('should compute the least common multiple of 7 and 6 as 42', function() { 93 | expect(QMath.lcm(7, 6)).to.equal(42); 94 | expect(QMath.lcm(6, 7)).to.equal(42); 95 | }); 96 | 97 | it('should compute the least common multiple of 9 and 18 as 18', function() { 98 | expect(QMath.lcm(9, 18)).to.equal(18); 99 | expect(QMath.lcm(18, 9)).to.equal(18); 100 | }); 101 | }); 102 | 103 | describe('continuedFraction', function() { 104 | it('should compute the continued fraction of 1/3', function() { 105 | const results = QMath.continuedFraction(1 / 3, 0.0001); 106 | expect(results.numerator).to.equal(1); 107 | expect(results.denominator).to.equal(3); 108 | expect(results.quotients).to.deep.equal([0, 3]); 109 | }); 110 | 111 | it('should compute the continued fraction of 11/13', function() { 112 | const results = QMath.continuedFraction(11 / 13, 0.0001); 113 | expect(results.numerator).to.equal(11); 114 | expect(results.denominator).to.equal(13); 115 | expect(results.quotients).to.deep.equal([0, 1, 5, 2]); 116 | }); 117 | 118 | it('should stop when the desired accuracy is reached', function() { 119 | const results = QMath.continuedFraction(Math.PI, 0.000001); 120 | expect(results.numerator).to.equal(355); 121 | expect(results.denominator).to.equal(113); 122 | expect(results.quotients).to.deep.equal([3, 7, 15, 1]); 123 | }); 124 | 125 | it('should work for negative numbers', function() { 126 | const results = QMath.continuedFraction(-Math.PI, 0.000001); 127 | expect(results.numerator).to.equal(-355); 128 | expect(results.denominator).to.equal(113); 129 | expect(results.quotients).to.deep.equal([-3, -7, -15, -1]); 130 | }); 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /spec/factoring.spec.js: -------------------------------------------------------------------------------- 1 | import * as chai from 'chai'; 2 | import Q from '../lib/index.js'; 3 | const {QMath, QState} = Q; 4 | const {expect} = chai; 5 | 6 | describe("Shor's algorithm", function() { 7 | // WARNING: This takes a random amount of time, but usually less than 10 seconds. 8 | it('should factor 35', function () { 9 | this.timeout(10 * 1000); 10 | function computeOrder(a, n) { 11 | const numOutBits = Math.ceil(Math.log(n) / Math.log(2)); 12 | const numInBits = 2 * numOutBits; 13 | const inputRange = Math.pow(2, numInBits); 14 | const outputRange = Math.pow(2, numOutBits); 15 | const accuracyRequiredForContinuedFraction = 1 / (2 * outputRange * outputRange); 16 | const outBits = {from: 0, to: numOutBits - 1}; 17 | const inputBits = {from: numOutBits, to: numOutBits + numInBits - 1}; 18 | const f = function (x) { return QMath.powerMod(a, x, n); }; 19 | const f0 = f(0); 20 | 21 | // This function contains the actual quantum computation part of the algorithm. 22 | // It returns either the frequency of the function f or some integer multiple (where "frequency" is the number of times the period of f will fit into 2^numInputBits) 23 | function determineFrequency(f) { 24 | let qstate = new QState(numInBits + numOutBits).hadamard(inputBits); 25 | qstate = qstate.applyFunction(inputBits, outBits, f); 26 | // We do not need to measure the outBits, but it does speed up the simulation. 27 | qstate = qstate.measure(outBits).newState; 28 | return qstate.qft(inputBits).measure(inputBits).result; 29 | } 30 | 31 | // Determine the period of f (i.e. find r such that f(x) = f(x+r). 32 | function findPeriod() { 33 | let bestSoFar = 1; 34 | 35 | for (let attempts = 0; attempts < 2 * numOutBits; attempts++) { 36 | // NOTE: Here we take advantage of the fact that, for Shor's algorithm, we know that f(x) = f(x+i) ONLY when i is an integer multiple of r. 37 | if (f(bestSoFar) === f0) { 38 | return bestSoFar; 39 | } 40 | 41 | const sample = determineFrequency(f); 42 | 43 | // Each "sample" has a high probability of being approximately equal to some integer multiple of (inputRange/r) rounded to the nearest integer. 44 | // So we use a continued fraction function to find r (or a divisor of r). 45 | const continuedFraction = QMath.continuedFraction(sample / inputRange, accuracyRequiredForContinuedFraction); 46 | // The denominator is a "candidate" for being r or a divisor of r (hence we need to find the least common multiple of several of these). 47 | const candidate = continuedFraction.denominator; 48 | // Reduce the chances of getting the wrong answer by ignoring obviously wrong results! 49 | if (candidate > 1 && candidate <= outputRange) { 50 | if (f(candidate) === f0) { 51 | bestSoFar = candidate; 52 | } else { 53 | const lcm = QMath.lcm(candidate, bestSoFar); 54 | if (lcm <= outputRange) { 55 | bestSoFar = lcm; 56 | } 57 | } 58 | } 59 | } 60 | return 'failed'; 61 | } 62 | 63 | // Step 2: compute the period of a^x mod n 64 | return findPeriod(); 65 | } 66 | 67 | function factor(n) { 68 | if (n % 2 === 0) { 69 | // Is even. No need for any quantum computing! 70 | return 2; 71 | } 72 | 73 | const powerFactor = QMath.powerFactor(n); 74 | if (powerFactor > 1) { 75 | // Is a power factor. No need for anything quantum! 76 | return powerFactor; 77 | } 78 | 79 | for (let attempts = 0; attempts < 8; attempts++) { 80 | // Step 1: chose random number between 2 and n 81 | const randomChoice = 2 + Math.floor(Math.random() * (n - 2)); 82 | const gcd = QMath.gcd(randomChoice, n); 83 | if (gcd > 1) { 84 | // Lucky guess. n and randomly chosen randomChoice have a common factor = gcd 85 | return gcd; 86 | } 87 | 88 | const r = computeOrder(randomChoice, n); 89 | if (r !== 'failed' && r % 2 === 0) { 90 | const powerMod = QMath.powerMod(randomChoice, r / 2, n); 91 | const candidateFactor = QMath.gcd(powerMod - 1, n); 92 | if (candidateFactor > 1 && n % candidateFactor === 0) { 93 | return candidateFactor; 94 | } 95 | } 96 | } 97 | return 'failed'; 98 | } 99 | 100 | expect(35 % factor(35)).to.equal(0); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/generalPeriodFinding.js.example: -------------------------------------------------------------------------------- 1 | /** 2 | * General case of the "Period Finding" problem. 3 | * For any function where f(x) = f(x + r), this algorithm will find r. 4 | * Examples are: 5 | * function(x) {return x % 17;} 6 | * function(x) {return 30 + Math.round(30 * Math.sin(Math.PI * x / 10));} 7 | * function(x) {return (x % 17) == 0 ? 1 : 0;} // NOTE: This often fails! 8 | */ 9 | 10 | // The number of qubits in the quantum circuit used as "input" and "output" bits to f are numInBits and numOutBits respectively. 11 | // They limit the size of r for which we can find the period to 2^numOutBits. 12 | var numOutBits = 6; 13 | var numInBits = 2 * numOutBits; 14 | var inputRange = Math.pow(2, numInBits); 15 | var outputRange = Math.pow(2, numOutBits); 16 | 17 | function findPeriod(f, callback) { 18 | var accuracyRequiredForContinuedFraction = 1/(2 * outputRange * outputRange); 19 | var outBits = {from: 0, to: numOutBits - 1}; 20 | var inputBits = {from: numOutBits, to: numOutBits + numInBits - 1}; 21 | var attempts = 0; 22 | var successes = 0; 23 | var bestSoFar = 1; 24 | var bestSoFarIsAPeriod = false; 25 | var f0 = f(0); 26 | 27 | // This function contains the actual quantum computation part of the algorithm. 28 | // It returns either the frequency of the function f or some integer multiple (where "frequency" is the number of times the period of f will fit into 2^numInputBits) 29 | function determineFrequency(f) { 30 | var qstate = new jsqubits.QState(numInBits + numOutBits).hadamard(inputBits); 31 | qstate = qstate.applyFunction(inputBits, outBits, f); 32 | // We do not need to measure the outBits, but it does speed up the simulation. 33 | qstate = qstate.measure(outBits).newState; 34 | return qstate.qft(inputBits).measure(inputBits).result; 35 | } 36 | 37 | function continueFindingPeriod() { 38 | // If we have had numOutBits successful "samples" then we have probably got the right answer. 39 | // But give up and use our best value so far if we have had too many attempts. 40 | if (successes === numOutBits || attempts === 2 * numOutBits) { 41 | callback(bestSoFar); 42 | return; 43 | } 44 | 45 | var sample = determineFrequency(f); 46 | 47 | // Each "sample" has a high probability of being approximately equal to some integer multiple of (inputRange/r) rounded to the nearest integer. 48 | // So we use a continued fraction function to find r (or a divisor of r). 49 | var continuedFraction = jsqubitsmath.continuedFraction(sample/inputRange, accuracyRequiredForContinuedFraction); 50 | // The denominator is a "candidate" for being r or a divisor of r (hence we need to find the least common multiple of several of these). 51 | var candidateDivisor = continuedFraction.denominator; 52 | log("Candidate divisor of r: " + candidateDivisor); 53 | // Reduce the chances of getting the wrong answer by ignoring obviously wrong results! 54 | if (candidateDivisor <= outputRange && candidateDivisor > 1) { 55 | // The period r should be the least common multiple of all of our candidate values (each is of the form k*r for random integer k). 56 | var lcm = jsqubitsmath.lcm(candidateDivisor, bestSoFar) 57 | if (lcm <= outputRange) { 58 | log("This is a good candidate."); 59 | bestSoFar = lcm; 60 | if (f(bestSoFar) === f0) bestSoFarIsAPeriod = true; 61 | successes++; 62 | } else if(!bestSoFarIsAPeriod && f(candidateDivisor) === f0) { 63 | // It can occasionally happen that our current best estimate of the period is a complete dead end, but we stumble across a much better one. 64 | log("This is a much better candidate"); 65 | bestSoFar = candidateDivisor; 66 | bestSoFarIsAPeriod = true; 67 | successes++; 68 | } 69 | } 70 | attempts++; 71 | log("Least common multiple: " + bestSoFar + ". Attempts: " + attempts + ". Good candidates: " + successes); 72 | // Yield control for a millisecond to give the browser a chance to log to the console. 73 | setTimeout(continueFindingPeriod, 50); 74 | } 75 | 76 | continueFindingPeriod(); 77 | } 78 | 79 | var f = promptForFunction("Enter a function where f(x) = f(x+r) for some r less than " + outputRange, "function(x) {return x % 16;}"); 80 | 81 | findPeriod(f, function(period) { 82 | if (f(0) === f(period)) { 83 | log("The period of your function is " + period); 84 | } else { 85 | log("Could not find period. Best effort was: " + period); 86 | } 87 | } 88 | ); 89 | 90 | 91 | -------------------------------------------------------------------------------- /spec/complex.spec.js: -------------------------------------------------------------------------------- 1 | import * as chai from 'chai'; 2 | import Q from '../lib/index.js'; 3 | const {expect} = chai; 4 | const QState = Q.QState; 5 | const complex = Q.complex; 6 | 7 | describe('Complex', function() { 8 | let w; 9 | let x; 10 | let y; 11 | 12 | beforeEach(function() { 13 | w = complex(-4, 3); 14 | x = complex(1, 3); 15 | y = complex(10, 30); 16 | }); 17 | 18 | describe('construction', function() { 19 | it('should use a default imaginary value of zero', function() { 20 | const z = complex(3); 21 | expect(z.real).to.equal(3); 22 | expect(z.imaginary).to.equal(0); 23 | }); 24 | }); 25 | 26 | describe('#add', function() { 27 | it('adds complex numbers', function() { 28 | const z = x.add(y); 29 | expect(z.real).to.equal(11); 30 | expect(z.imaginary).to.equal(33); 31 | }); 32 | 33 | it('adds real numbers', function() { 34 | const z = x.add(5); 35 | expect(z.real).to.equal(6); 36 | expect(z.imaginary).to.equal(x.imaginary); 37 | }); 38 | }); 39 | 40 | describe('#multiply', function() { 41 | it('multiplies complex numbers', function() { 42 | const z = x.multiply(y); 43 | expect(z.real).to.equal(10 - 90); 44 | expect(z.imaginary).to.equal(60); 45 | }); 46 | 47 | it('multiplies real numbers', function() { 48 | const z = y.multiply(5); 49 | expect(z.real).to.equal(50); 50 | expect(z.imaginary).to.equal(150); 51 | }); 52 | }); 53 | 54 | describe('#negate', function() { 55 | it('negates complex numbers', function() { 56 | const z = x.negate(); 57 | expect(z.real).to.equal(-1); 58 | expect(z.imaginary).to.equal(-3); 59 | }); 60 | }); 61 | 62 | describe('#magnitude', function() { 63 | it('returns the magnitude', function() { 64 | expect(w.magnitude()).to.equal(5); 65 | }); 66 | }); 67 | 68 | describe('#phase', function() { 69 | it('returns the correct phase for 1', function() { 70 | const p = complex(1, 0).phase(); 71 | expect(p).to.be.closeTo(0, QState.roundToZero); 72 | }); 73 | 74 | it('returns the correct phase for i', function() { 75 | expect(complex(0, 1).phase()).to.be.closeTo(Math.PI / 2, QState.roundToZero); 76 | }); 77 | 78 | it('returns the correct phase for -1', function() { 79 | expect(complex(-1, 0).phase()).to.be.closeTo(Math.PI, QState.roundToZero); 80 | }); 81 | 82 | it('returns the correct phase for -i', function() { 83 | expect(complex(0, -1).phase()).to.be.closeTo(-Math.PI / 2, QState.roundToZero); 84 | }); 85 | 86 | it('returns the correct phase for 0', function() { 87 | expect(complex(0, 0).phase()).to.be.closeTo(0, QState.roundToZero); 88 | }); 89 | 90 | it('returns the correct phase for 1+i', function() { 91 | expect(complex(1, 1).phase()).to.be.closeTo(Math.PI / 4, QState.roundToZero); 92 | }); 93 | 94 | it('returns the correct phase for -1+i', function() { 95 | expect(complex(-1, 1).phase()).to.be.closeTo(3 * Math.PI / 4, QState.roundToZero); 96 | }); 97 | 98 | it('returns the correct phase for -1-i', function() { 99 | expect(complex(-1, -1).phase()).to.be.closeTo(-3 * Math.PI / 4, QState.roundToZero); 100 | }); 101 | 102 | it('returns the correct phase for 1-i', function() { 103 | expect(complex(1, -1).phase()).to.be.closeTo(-Math.PI / 4, QState.roundToZero); 104 | }); 105 | }); 106 | 107 | describe('#subtract', function() { 108 | it('subtracts real numbers', function() { 109 | expect(y.subtract(2).equal(complex(8, 30))).to.be.true; 110 | }); 111 | 112 | it('subtracts complex numbers', function() { 113 | expect(y.subtract(w).equal(complex(14, 27))).to.be.true; 114 | }); 115 | }); 116 | 117 | describe('#conjugate', function() { 118 | it('returns the complex conjugate', function() { 119 | expect(x.conjugate().equal(complex(1, -3))).to.be.true; 120 | }); 121 | }); 122 | 123 | describe('#real', function() { 124 | it('should create a complex number', function() { 125 | expect(Q.real(3).closeTo(complex(3, 0))).to.be.true; 126 | }); 127 | }); 128 | 129 | describe('#toString', function() { 130 | it('should format the complex number', function() { 131 | expect(complex(-1.23, 3.4).toString()).to.equal('-1.23+3.4i'); 132 | }); 133 | }); 134 | 135 | describe('#format', function() { 136 | it('should use toString when no options', function() { 137 | expect(complex(-1.23, 3.4).format()).to.equal('-1.23+3.4i'); 138 | }); 139 | 140 | it('should drop the 1 if imaginary value is 1', function() { 141 | expect(complex(-1.23, 1).format()).to.equal('-1.23+i'); 142 | }); 143 | 144 | it('should drop the 1 if imaginary value is -1', function() { 145 | expect(complex(-1.23, -1).format()).to.equal('-1.23-i'); 146 | }); 147 | 148 | it('should round off decimal places when requested', function() { 149 | expect(complex(-1.235959, 3.423523).format({decimalPlaces: 3})).to.equal('-1.236+3.424i'); 150 | }); 151 | }); 152 | }); 153 | -------------------------------------------------------------------------------- /examples/algorithms/factoring.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Shor's factoring algorithm. 3 | * See https://cs.uwaterloo.ca/~watrous/QC-notes/QC-notes.11.pdf 4 | */ 5 | 6 | import jsqubits from '../../lib/index.js' 7 | 8 | const jsqubitsmath = jsqubits.QMath 9 | 10 | function computeOrder(a, n) { 11 | var numOutBits = Math.ceil(Math.log(n) / Math.log(2)); 12 | var numInBits = 2 * numOutBits; 13 | var inputRange = Math.pow(2, numInBits); 14 | var outputRange = Math.pow(2, numOutBits); 15 | var accuracyRequiredForContinuedFraction = 1 / (2 * outputRange * outputRange); 16 | var outBits = { 17 | from: 0, 18 | to: numOutBits - 1 19 | }; 20 | var inputBits = { 21 | from: numOutBits, 22 | to: numOutBits + numInBits - 1 23 | }; 24 | var f = function (x) { 25 | return jsqubitsmath.powerMod(a, x, n); 26 | }; 27 | var f0 = f(0); 28 | 29 | // This function contains the actual quantum computation part of the algorithm. 30 | // It returns either the frequency of the function f or some integer multiple (where "frequency" is the number of times the period of f will fit into 2^numInputBits) 31 | function determineFrequency(f) { 32 | var qstate = new jsqubits.QState(numInBits + numOutBits).hadamard(inputBits); 33 | qstate = qstate.applyFunction(inputBits, outBits, f); 34 | // We do not need to measure the outBits, but it does speed up the simulation. 35 | qstate = qstate.measure(outBits).newState; 36 | return qstate.qft(inputBits) 37 | .measure(inputBits).result; 38 | } 39 | 40 | // Determine the period of f (i.e. find r such that f(x) = f(x+r). 41 | function findPeriod() { 42 | var bestSoFar = 1; 43 | 44 | for (var attempts = 0; attempts < 2 * numOutBits; attempts++) { 45 | // NOTE: Here we take advantage of the fact that, for Shor's algorithm, we know that f(x) = f(x+i) ONLY when i is an integer multiple of the rank r. 46 | if (f(bestSoFar) === f0) { 47 | console.log('The period of ' + a + '^x mod ' + n + ' is ' + bestSoFar); 48 | return bestSoFar; 49 | } 50 | 51 | var sample = determineFrequency(f); 52 | 53 | // Each "sample" has a high probability of being approximately equal to some integer multiple of (inputRange/r) rounded to the nearest integer. 54 | // So we use a continued fraction function to find r (or a divisor of r). 55 | var continuedFraction = jsqubitsmath.continuedFraction(sample / inputRange, accuracyRequiredForContinuedFraction); 56 | // The denominator is a "candidate" for being r or a divisor of r (hence we need to find the least common multiple of several of these). 57 | var candidateDivisor = continuedFraction.denominator; 58 | console.log('Candidate divisor of r: ' + candidateDivisor); 59 | // Reduce the chances of getting the wrong answer by ignoring obviously wrong results! 60 | if (candidateDivisor > 1 && candidateDivisor <= outputRange) { 61 | if (f(candidateDivisor) === f0) { 62 | console.log('This is a multiple of the rank.'); 63 | bestSoFar = candidateDivisor; 64 | } else { 65 | var lcm = jsqubitsmath.lcm(candidateDivisor, bestSoFar); 66 | if (lcm <= outputRange) { 67 | console.log('This is a good candidate.'); 68 | bestSoFar = lcm; 69 | } 70 | } 71 | } 72 | console.log('Least common multiple so far: ' + bestSoFar + '. Attempts: ' + attempts); 73 | } 74 | console.log('Giving up trying to find rank of ' + a + ' after ' + attempts + ' attempts.'); 75 | return 'failed'; 76 | } 77 | 78 | // Step 2: compute the period of a^x mod n 79 | return findPeriod(); 80 | } 81 | 82 | const factor = function (n) { 83 | 84 | if (n % 2 === 0) { 85 | // Is even. No need for any quantum computing! 86 | return 2; 87 | } 88 | 89 | var powerFactor = jsqubitsmath.powerFactor(n); 90 | if (powerFactor > 1) { 91 | // Is a power factor. No need for anything quantum! 92 | return powerFactor; 93 | } 94 | 95 | for (var attempts = 0; attempts < 8; attempts++) { 96 | // Step 1: chose random number between 2 and n 97 | var randomChoice = 2 + Math.floor(Math.random() * (n - 2)); 98 | console.log('Step 1: chose random number between 2 and ' + n + '. Chosen: ' + randomChoice); 99 | var gcd = jsqubitsmath.gcd(randomChoice, n); 100 | if (gcd > 1) { 101 | // Lucky guess. n and randomly chosen randomChoice have a common factor = gcd 102 | console.log('Lucky guess. ' + n + ' and randomly chosen ' + randomChoice + ' have a common factor = ' + gcd); 103 | return gcd; 104 | } 105 | 106 | var r = computeOrder(randomChoice, n); 107 | if (r !== 'failed' && r % 2 !== 0) { 108 | console.log('Need a period with an even number. Sadly, ' + r + ' is not even.'); 109 | } else if (r !== 'failed' && r % 2 === 0) { 110 | var powerMod = jsqubitsmath.powerMod(randomChoice, r / 2, n); 111 | var candidateFactor = jsqubitsmath.gcd(powerMod - 1, n); 112 | console.log('Candidate Factor computed from period = ' + candidateFactor); 113 | if (candidateFactor > 1 && n % candidateFactor === 0) { 114 | return candidateFactor; 115 | } 116 | } 117 | console.log('Try again.'); 118 | } 119 | return 'failed'; 120 | }; 121 | 122 | 123 | var startTime = new Date(); 124 | var n = 35; 125 | var result = factor(n); 126 | console.log('One of the factors of ' + n + ' is ' + result); 127 | console.log('Time taken in seconds: ' + ((new Date().getTime()) - startTime.getTime()) / 1000); 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsqubits 2 | Quantum computation simulation JavaScript library 3 | 4 | [![Build Status](https://github.com/davidbkemp/jsqubits/actions/workflows/node.js.yml/badge.svg)](https://github.com/davidbkemp/jsqubits/actions/workflows/node.js.yml) 5 | 6 | Website: 7 | https://davidbkemp.github.io/jsqubits/ 8 | 9 | The user manual: 10 | https://davidbkemp.github.io/jsqubits/jsqubitsManual.html 11 | 12 | Try it out online using the jsqubits runner: 13 | https://davidbkemp.github.io/jsqubits/jsqubitsRunner.html 14 | 15 | Wiki (with examples): 16 | https://github.com/davidbkemp/jsqubits/wiki 17 | 18 | GitHub: 19 | https://github.com/davidbkemp/jsqubits 20 | 21 | Node npm module: 22 | https://npmjs.org/package/jsqubits 23 | 24 | You can use it to implement quantum algorithms using JavaScript like this: 25 | 26 | jsqubits('|01>') 27 | .hadamard(jsqubits.ALL) 28 | .cnot(1, 0) 29 | .hadamard(jsqubits.ALL) 30 | .measure(1) 31 | .result 32 | 33 | 34 | If you are new to quantum programming, then it is highly recommended that you try reading 35 | [John Watrous' Quantum Information and Computation Lecture Notes](https://cs.uwaterloo.ca/~watrous/QC-notes/). 36 | You may also wish to try reading the (work in progress) [Introduction to Quantum Programming using jsqubits](https://davidbkemp.github.io/jsqubits/jsqubitsTutorial.html). 37 | 38 | Usage 39 | ----- 40 | 41 | ## Online 42 | Try it out online using the jsqubits runner: 43 | https://davidbkemp.github.io/jsqubits/jsqubitsRunner.html 44 | 45 | ## Install using npm 46 | 47 | You can use jsqubits in a Node application by installing jsqubits using npm. 48 | 49 | **First, make sure you have at least version 16 of Node installed.** 50 | 51 | Place the following code in `myprogram.mjs`. 52 | Note the `.mjs` extension is a way of informing Node that the program uses ES modules. 53 | 54 | ```javascript 55 | import {jsqubits} from 'jsqubits' 56 | const result = jsqubits('|0101>').hadamard(jsqubits.ALL); 57 | console.log(result.toString()); 58 | ``` 59 | 60 | Run the following: 61 | ```shell 62 | $ npm install jsqubits@2 63 | $ node myprogram.mjs 64 | ``` 65 | 66 | ## Downloading from github 67 | 68 | You could use jsqubits by cloning the github repository, or downloading a release from github. 69 | 70 | **First, make sure you have at least version 22 of Node installed.** 71 | 72 | Clone jsqubits 73 | 74 | ```shell 75 | $ git clone https://github.com/davidbkemp/jsqubits.git 76 | ``` 77 | 78 | Place the following code in `myprogram.mjs`. 79 | Note the `.mjs` extension is a way of informing Node that the program uses ES modules. 80 | 81 | ```javascript 82 | import {jsqubits} from './jsqubits/lib/index.js' 83 | const result = jsqubits('|0101>').hadamard(jsqubits.ALL); 84 | console.log(result.toString()); 85 | ``` 86 | 87 | Run your program using Node: 88 | 89 | ```shell 90 | $ node myprogram.mjs 91 | ``` 92 | 93 | ## On your own website 94 | 95 | We are using [native ES modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). 96 | Sadly, this complicates things when using jsqubits from within a web page: 97 | 98 | - The page will need to be served by a web server, not just loaded from your local file system. 99 | Note: placing your web page on github pages is an option for this. 100 | - You will need a copy of the contents of the `lib` directory on your web server. 101 | - Your page will need to specify `type="module"` in the `script` tag used to load the jsqubits library. 102 | - The page will not work on old versions of web browsers. It definitely won't work on Internet Explorer. 103 | I have successfully had jsqubits running on Chrome 89, Firefox 87, Safari 14, and Edge 89. 104 | 105 | e.g. assuming you have placed the contents of `lib` in a directory called `jsqubits` that sits next to your webpage, 106 | then you could create a simple web page like this: 107 | 108 | ```html 109 | 110 |

111 | 116 | 117 | ``` 118 | 119 | See other examples in the `examples` directory. 120 | 121 | TypeScript type definitions 122 | --------------------------- 123 | TypeScript type definitions for jsqubits are available: 124 | 125 | - Node npm module: https://www.npmjs.com/package/@types/jsqubits 126 | - Github: https://github.com/qramana/jsqubits.d.ts 127 | 128 | 129 | Development 130 | ----------- 131 | To run the tests, you will need to install version 16 or later of Node.js (https://nodejs.org). 132 | Then use `npm install` to install the testing dependencies and `npm test` to run the specs. 133 | NOTE: The tests include an example of factoring using Shor's faction algorithm. This is non-deterministic and can take a fraction of a second or several seconds to complete. 134 | 135 | License 136 | ------- 137 | 138 | (The MIT License) 139 | 140 | Copyright (c) 2012 David Kemp <davidbkemp@gmail.com> 141 | 142 | Permission is hereby granted, free of charge, to any person obtaining 143 | a copy of this software and associated documentation files (the 144 | 'Software'), to deal in the Software without restriction, including 145 | without limitation the rights to use, copy, modify, merge, publish, 146 | distribute, sublicense, and/or sell copies of the Software, and to 147 | permit persons to whom the Software is furnished to do so, subject to 148 | the following conditions: 149 | 150 | The above copyright notice and this permission notice shall be 151 | included in all copies or substantial portions of the Software. 152 | 153 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 154 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 155 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 156 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 157 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 158 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 159 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 160 | -------------------------------------------------------------------------------- /jsqubitsRunner/examples/factoring.js.example: -------------------------------------------------------------------------------- 1 | /* 2 | * Shor's factoring algorithm. 3 | * See https://cs.uwaterloo.ca/~watrous/QC-notes/QC-notes.11.pdf 4 | */ 5 | 6 | function computeOrder(a, n, numOutBits, callback) { 7 | var numInBits = 2 * numOutBits; 8 | var inputRange = Math.pow(2,numInBits); 9 | var outputRange = Math.pow(2,numOutBits); 10 | var accuracyRequiredForContinuedFraction = 1/(2 * outputRange * outputRange); 11 | var outBits = {from: 0, to: numOutBits - 1}; 12 | var inputBits = {from: numOutBits, to: numOutBits + numInBits - 1}; 13 | var attempts = 0; 14 | var bestSoFar = 1; 15 | var f = function(x) { return jsqubitsmath.powerMod(a, x, n); } 16 | var f0 = f(0); 17 | 18 | // This function contains the actual quantum computation part of the algorithm. 19 | // It returns either the frequency of the function f or some integer multiple (where "frequency" is the number of times the period of f will fit into 2^numInputBits) 20 | function determineFrequency(f) { 21 | var qstate = new jsqubits.QState(numInBits + numOutBits).hadamard(inputBits); 22 | qstate = qstate.applyFunction(inputBits, outBits, f); 23 | // We do not need to measure the outBits, but it does speed up the simulation. 24 | qstate = qstate.measure(outBits).newState; 25 | return qstate.qft(inputBits).measure(inputBits).result; 26 | } 27 | 28 | // Determine the period of f (i.e. find r such that f(x) = f(x+r). 29 | function findPeriod() { 30 | 31 | // NOTE: Here we take advantage of the fact that, for Shor's algorithm, we know that f(x) = f(x+i) ONLY when i is an integer multiple of the rank r. 32 | if (f(bestSoFar) === f0) { 33 | log("The period of " + a + "^x mod " + n + " is " + bestSoFar); 34 | callback(bestSoFar); 35 | return; 36 | } 37 | 38 | if (attempts === 2 * numOutBits) { 39 | log("Giving up trying to find rank of " + a); 40 | callback("failed"); 41 | return; 42 | } 43 | 44 | var sample = determineFrequency(f); 45 | 46 | // Each "sample" has a high probability of being approximately equal to some integer multiple of (inputRange/r) rounded to the nearest integer. 47 | // So we use a continued fraction function to find r (or a divisor of r). 48 | var continuedFraction = jsqubitsmath.continuedFraction(sample/inputRange, accuracyRequiredForContinuedFraction); 49 | // The denominator is a "candidate" for being r or a divisor of r (hence we need to find the least common multiple of several of these). 50 | var candidate = continuedFraction.denominator; 51 | log("Candidate period from quantum fourier transform: " + candidate); 52 | // Reduce the chances of getting the wrong answer by ignoring obviously wrong results! 53 | if (candidate <= 1 || candidate > outputRange) { 54 | log("Ignoring as candidate is out of range."); 55 | } else if (f(candidate) === f0) { 56 | bestSoFar = candidate; 57 | } else { 58 | var lcm = jsqubitsmath.lcm(candidate, bestSoFar); 59 | log("Least common multiple of new candidate and best LCM so far: " + lcm); 60 | if (lcm > outputRange) { 61 | log("Ignoring this candidate as the LCM is too large!") 62 | } else { 63 | bestSoFar = lcm; 64 | } 65 | } 66 | attempts++; 67 | log("Least common multiple so far: " + bestSoFar + ". Attempts: " + attempts); 68 | // Yield control to give the browser a chance to log to the console. 69 | setTimeout(findPeriod, 50); 70 | } 71 | 72 | log("Step 2: compute the period of " + a + "^x mod " + n); 73 | findPeriod(); 74 | } 75 | 76 | function factor(n, callback) { 77 | 78 | var attempt = 0; 79 | var numOutBits = Math.ceil(Math.log(n)/Math.log(2)); 80 | 81 | function attemptFactor() { 82 | if (attempt++ === 8) { 83 | callback("failed"); 84 | return; 85 | } 86 | var randomChoice = 2 + Math.floor(Math.random() * (n - 2)); 87 | log("Step 1: chose random number between 2 and " + n + ". Chosen: " + randomChoice); 88 | var gcd = jsqubitsmath.gcd(randomChoice, n); 89 | if(gcd > 1) { 90 | log("Lucky guess. " + n + " and randomly chosen " + randomChoice + " have a common factor = " + gcd); 91 | callback(gcd); 92 | return; 93 | } 94 | 95 | computeOrder(randomChoice, n, numOutBits, function(r) { 96 | if (r !== "failed" && r % 2 !== 0) { 97 | log('Need a period with an even number. Sadly, ' + r + ' is not even.'); 98 | } else if (r !== "failed" && r % 2 === 0) { 99 | var powerMod = jsqubitsmath.powerMod(randomChoice, r/2, n); 100 | var candidateFactor = jsqubitsmath.gcd(powerMod - 1, n); 101 | log("Candidate Factor computed from period = " + candidateFactor); 102 | if(candidateFactor > 1 && n % candidateFactor === 0) { 103 | callback(candidateFactor); 104 | return; 105 | } 106 | log(candidateFactor + " is not really a factor."); 107 | } 108 | log("Try again. (Attempts so far: " + attempt + ")"); 109 | // Yield control to give the browser a chance to log to the console. 110 | setTimeout(function(){attemptFactor();}, 30) 111 | }); 112 | } 113 | 114 | if (n % 2 === 0) { 115 | log("Is even. No need for any quantum computing!") 116 | callback(2); 117 | return; 118 | } 119 | 120 | var powerFactor = jsqubitsmath.powerFactor(n); 121 | if (powerFactor > 1) { 122 | log("Is a power factor. No need for anything quantum!") 123 | callback(powerFactor); 124 | return; 125 | } 126 | 127 | attemptFactor(); 128 | } 129 | 130 | var n = prompt("Enter a product of two distinct primes: ", 35); 131 | 132 | var startTime = new Date(); 133 | factor(n, function(result) { 134 | log("One of the factors of " + n + " is " + result); 135 | log("Time taken in seconds: " + ((new Date().getTime()) - startTime.getTime()) / 1000); 136 | }); 137 | -------------------------------------------------------------------------------- /lib/QMath.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Return x^y mod m 3 | */ 4 | export function powerMod(x, y, m) { 5 | if (y === 0) return 1; 6 | if (y === 1) return x; 7 | const halfY = Math.floor(y / 2); 8 | const powerHalfY = powerMod(x, halfY, m); 9 | let result = (powerHalfY * powerHalfY) % m; 10 | if (y % 2 === 1) result = (x * result) % m; 11 | return result; 12 | } 13 | 14 | function approximatelyInteger(x) { 15 | return Math.abs(x - Math.round(x)) < 0.0000001; 16 | } 17 | 18 | /** 19 | * Return x such that n = x^y for some prime number x, or otherwise return 0. 20 | */ 21 | export function powerFactor(n) { 22 | const log2n = Math.log(n) / Math.log(2); 23 | // Try values of root_y(n) starting at log2n and working your way down to 2. 24 | let y = Math.floor(log2n); 25 | if (log2n === y) { 26 | return 2; 27 | } 28 | y--; 29 | for (; y > 1; y--) { 30 | const x = Math.pow(n, 1 / y); 31 | if (approximatelyInteger(x)) { 32 | return Math.round(x); 33 | } 34 | } 35 | return 0; 36 | } 37 | 38 | /** 39 | * Greatest common divisor 40 | */ 41 | export function gcd(a, b) { 42 | while (b !== 0) { 43 | const c = a % b; 44 | a = b; 45 | b = c; 46 | } 47 | return a; 48 | } 49 | 50 | /** 51 | * Least common multiple 52 | */ 53 | export function lcm(a, b) { 54 | return a * b / gcd(a, b); 55 | } 56 | 57 | function roundTowardsZero(value) { 58 | return value >= 0 ? Math.floor(value) : Math.ceil(value); 59 | } 60 | 61 | /** 62 | * Find the continued fraction representation of a number. 63 | * @param the value to be converted to a continued faction. 64 | * @param the precision with which to compute (eg. 0.01 will compute values until the fraction is at least as precise as 0.01). 65 | * @return An object {quotients: quotients, numerator: numerator, denominator: denominator} where quotients is 66 | * an array of the quotients making up the continued fraction whose value is within the specified precision of the targetValue, 67 | * and where numerator and denominator are the integer values to which the continued fraction evaluates. 68 | */ 69 | export function continuedFraction(targetValue, precision) { 70 | let firstValue; 71 | let remainder; 72 | if (Math.abs(targetValue) >= 1) { 73 | firstValue = roundTowardsZero(targetValue); 74 | remainder = targetValue - firstValue; 75 | } else { 76 | firstValue = 0; 77 | remainder = targetValue; 78 | } 79 | let twoAgo = {numerator: 1, denominator: 0}; 80 | let oneAgo = {numerator: firstValue, denominator: 1}; 81 | const quotients = [firstValue]; 82 | 83 | while (Math.abs(targetValue - (oneAgo.numerator / oneAgo.denominator)) > precision) { 84 | const reciprocal = 1 / remainder; 85 | const quotient = roundTowardsZero(reciprocal); 86 | remainder = reciprocal - quotient; 87 | quotients.push(quotient); 88 | const current = {numerator: quotient * oneAgo.numerator + twoAgo.numerator, denominator: quotient * oneAgo.denominator + twoAgo.denominator}; 89 | twoAgo = oneAgo; 90 | oneAgo = current; 91 | } 92 | 93 | let {numerator, denominator} = oneAgo; 94 | if (oneAgo.denominator < 0) { 95 | numerator *= -1; 96 | denominator *= -1; 97 | } 98 | return {quotients, numerator, denominator}; 99 | } 100 | 101 | //-------------- 102 | 103 | function cloneArray(a) { 104 | const result = []; 105 | for (let i = 0; i < a.length; i++) { 106 | result[i] = a[i]; 107 | } 108 | return result; 109 | } 110 | 111 | /** 112 | * Find the null space in modulus 2 arithmetic of a matrix of binary values 113 | * @param matrix matrix of binary values represented using an array of numbers 114 | * whose bit values are the entries of a matrix rowIndex. 115 | * @param width the width of the matrix. 116 | */ 117 | export function findNullSpaceMod2(matrix, width) { 118 | const transformedMatrix = cloneArray(matrix); 119 | /** 120 | * Try to make row pivotRowIndex / column colIndex a pivot 121 | * swapping rows if necessary. 122 | */ 123 | function attemptToMakePivot(colIndex, pivotRowIndex) { 124 | const colBitMask = 1 << colIndex; 125 | if (colBitMask & transformedMatrix[pivotRowIndex]) return; 126 | for (let rowIndex = pivotRowIndex + 1; rowIndex < transformedMatrix.length; rowIndex++) { 127 | if (colBitMask & transformedMatrix[rowIndex]) { 128 | const tmp = transformedMatrix[pivotRowIndex]; 129 | transformedMatrix[pivotRowIndex] = transformedMatrix[rowIndex]; 130 | transformedMatrix[rowIndex] = tmp; 131 | return; 132 | } 133 | } 134 | } 135 | 136 | /** 137 | * Zero out the values above and below the pivot (using mod 2 arithmetic). 138 | */ 139 | function zeroOutAboveAndBelow(pivotColIndex, pivotRowIndex) { 140 | const pivotRow = transformedMatrix[pivotRowIndex]; 141 | const colBitMask = 1 << pivotColIndex; 142 | for (let rowIndex = 0; rowIndex < transformedMatrix.length; rowIndex++) { 143 | if (rowIndex !== pivotRowIndex && (colBitMask & transformedMatrix[rowIndex])) { 144 | transformedMatrix[rowIndex] ^= pivotRow; 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Reduce 'a' to reduced row echelon form, 151 | * and keep track of which columns are pivot columns in pivotColumnIndexes. 152 | */ 153 | function makeReducedRowEchelonForm(pivotColumnIndexes) { 154 | let pivotRowIndex = 0; 155 | for (let pivotColIndex = width - 1; pivotColIndex >= 0; pivotColIndex--) { 156 | attemptToMakePivot(pivotColIndex, pivotRowIndex); 157 | const colBitMask = 1 << pivotColIndex; 158 | if (colBitMask & transformedMatrix[pivotRowIndex]) { 159 | pivotColumnIndexes[pivotRowIndex] = pivotColIndex; 160 | zeroOutAboveAndBelow(pivotColIndex, pivotRowIndex); 161 | pivotRowIndex++; 162 | } 163 | } 164 | } 165 | 166 | /** 167 | * Add to results, special solutions corresponding to the specified non-pivot column colIndex. 168 | */ 169 | function specialSolutionForColumn(pivotColumnIndexes, colIndex, pivotNumber) { 170 | const columnMask = 1 << colIndex; 171 | let specialSolution = columnMask; 172 | for (let rowIndex = 0; rowIndex < pivotNumber; rowIndex++) { 173 | if (transformedMatrix[rowIndex] & columnMask) { 174 | specialSolution += 1 << pivotColumnIndexes[rowIndex]; 175 | } 176 | } 177 | return specialSolution; 178 | } 179 | 180 | /** 181 | * Find the special solutions to the mod-2 equation Ax=0 for matrix a. 182 | */ 183 | function specialSolutions(pivotColumnIndexes) { 184 | const results = []; 185 | let pivotNumber = 0; 186 | let nextPivotColumnIndex = pivotColumnIndexes[pivotNumber]; 187 | for (let colIndex = width - 1; colIndex >= 0; colIndex--) { 188 | if (colIndex === nextPivotColumnIndex) { 189 | pivotNumber++; 190 | nextPivotColumnIndex = pivotColumnIndexes[pivotNumber]; 191 | } else { 192 | results.push(specialSolutionForColumn(pivotColumnIndexes, colIndex, pivotNumber)); 193 | } 194 | } 195 | return results; 196 | } 197 | 198 | const pivotColumnIndexes = []; 199 | makeReducedRowEchelonForm(pivotColumnIndexes); 200 | return specialSolutions(pivotColumnIndexes); 201 | } 202 | 203 | export default { 204 | powerMod, 205 | powerFactor, 206 | gcd, 207 | lcm, 208 | continuedFraction, 209 | findNullSpaceMod2 210 | }; 211 | -------------------------------------------------------------------------------- /jsqubitsRunner/jsqubitsRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jsqubits runner 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 |

jsqubits runner

20 |

An online quantum computer simulator

21 |

22 | This is a JavaScript runner preloaded with 23 | the jsqubits library. 24 | Enter arbitrary JavaScript in the text area below and press the "Run" button. 25 | For more information, see the 26 | jsqubits user manual. 27 |

28 | 29 | 30 | 31 |
32 |
33 | 34 | 35 |
36 |
37 | Load example: 38 | 52 |
53 |
54 |
55 |
56 |
57 |
58 | 59 |

jsqubits methods

60 |
61 |
62 |
jsqubits(bitstring)
Create a QState object
63 |
new QState(numbits, amplitudes)
Create a QState object
64 |
add(otherState)
Add another state
65 |
subtract(otherState)
Subtract another state
66 |
multiply(value)
Multiply by a global phase
67 |
normalize()
Normalize the amplitudes
68 |
x(targetBits)
Pauli X
69 |
y(targetBits)
Pauli Y
70 |
z(targetBits)
Pauli Z
71 |
r(targetBits, angle)
Phase shift gate
72 |
s(targetBits)
Phase gate: r(π/2)
73 |
t(targetBits)
T gate: r(π/4)
74 |
hadamard(targetBits)
Hadamard
75 |
not(targetBits)
Not
76 |
swap(bit1, bit2)
Swap two bits
77 |
78 |
79 |
rotateX(targetBits, angle)
Rotate about X
80 |
rotateY(targetBits, angle)
Rotate about Y
81 |
rotateZ(targetBits, angle)
Rotate about Z
82 |
controlledHadamard(controlBits, targetBits))
Controlled Hadamard
83 |
cnot(controlBits, targetBits)
Controlled Not
84 |
controlledX(controlBits, targetBits)
Controlled Pauli X
85 |
controlledXRotation(ctrlBits, trgtBits, angle)
Controlled rotation about X
86 |
toffoli(controlBit, controlBit, ..., targetBit)
Toffoli
87 |
applyFunction(inputBits, targetBits, function)
Apply a function
88 |
tensorProduct(otherQState)
Tensor product
89 |
qft(targetBits)
Quantum Fourier Transform
90 |
amplitude(basisState)
Get an amplitude
91 |
measure(targetBits)
Make a measurement
92 |
jsqubits.complex(real, imaginary)
Create a complex number
93 |
jsqubits.Complex.add,subtract,multiply,negate
Some Complex methods
94 |
95 |
96 | 97 |

Note:

98 | 115 |

116 | For more details, see the 117 | jsqubits user manual. 118 |

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 |

33 | 34 | 35 | jsqubits('|01>') 36 | .hadamard(jsqubits.ALL) 37 | .cnot(1, 0) 38 | .hadamard(jsqubits.ALL) 39 | .measure(1) 40 | .result 41 | 42 |

43 | If you are new to quantum programming, then it is highly recommended that you try reading 44 | John Watrous' Quantum Information and Computation Lecture Notes. 45 | You may also wish to try reading the partially finished 46 | Introduction to Quantum Programming using jsqubits. 47 |

48 | 49 |
50 |

jsqubits.QState

51 |
    52 |
  • jsqubits(bitString)
  • 53 |
  • QState(numBits, amplitudes)
  • 54 |
  • add(otherState)
  • 55 |
  • subtract(otherState)
  • 56 |
  • multiply(globalPhase)
  • 57 |
  • normalize()
  • 58 |
59 |

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 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
jsqubits("0101")creates a 4 qubit QState in the simple basis state of|0101>
jsqubits("|0101>")also creates a 4 qubit QState of|0101>
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 |

93 | 94 | jsqubits('|00>') 95 | .multiply(jsqubits.complex(1,1)) 96 | .add(jsqubits('|01>')) 97 | .subtract(jsqubits('|10>')) 98 | .normalize() 99 | 100 | 101 |

102 | creates the QState (0.5+0.5i)|00> + (0.5)|01> + (-0.5)|10> 103 |

104 | 105 |

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 |

creates the following 4 qubit QState:

115 | 116 | (0.5)|0000> + (0.5)|0001> + (0.5)|0100> + (0.5)|0101> 117 | 118 | 119 |

120 | WARNINGS: 121 |

    122 |
  • 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 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 |
jsqubits("|0000>").x(0)gives|0001>
jsqubits("|0000>").x({from: 0, to: 2})gives|0111>
jsqubits("|0000>").x(jsqubits.ALL)gives|1111>
jsqubits("|0000>").x([0,2])gives|0101>
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 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 |
jsqubits("|00>").x(0)gives|01>
jsqubits("|01>").x(0)gives|00>
jsqubits("|00>").y(0)gives(i)|01>
jsqubits("|01>").y(0)gives(-i)|00>
jsqubits("|00>").z(0)gives|00>
jsqubits("|01>").z(0)gives(-1)|01>
jsqubits("|00>").not(0)gives|01>
jsqubits("|01>").not(0)gives|00>
jsqubits("|00>").hadamard(0)gives(0.7071)|00> + (0.7071)|01>
jsqubits("|01>").hadamard(0)gives(0.7071)|00> + (-0.7071)|01>
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 e, 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 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 |
jsqubits("|00>").r(0, Math.PI/3)gives|00>
jsqubits("|01>").r(0, Math.PI/3)gives(0.5+0.866i)|01>
jsqubits("|00>").s(0)gives|00>
jsqubits("|01>").s(0)gives(i)|01>
jsqubits("|00>").t(0)gives|00>
jsqubits("|01>").t(0)gives(0.7071+0.7071i)|01>
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 |

310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 |
jsqubits("|00>").rotateX(0, Math.PI/2)gives(0.7071)|00> + (-0.7071i)|01>
jsqubits("|01>").rotateX(0, Math.PI/2)gives(-0.7071i)|00> + (0.7071)|01>
jsqubits("|00>").rotateY(0, Math.PI/2)gives(0.7071)|00> + (0.7071)|01>
jsqubits("|01>").rotateY(0, Math.PI/2)gives(-0.7071)|00> + (0.7071)|01>
jsqubits("|00>").rotateZ(0, Math.PI/2)gives(0.7071-0.7071i)|00>
jsqubits("|01>").rotateZ(0, Math.PI/2)gives(0.7071+0.7071i)|01>
345 |
346 | 347 |
348 |

Controlled operations

349 |
    350 |
  • controlledX(controlBits, targetBits)
  • 351 |
  • controlledY(controlBits, targetBits)
  • 352 |
  • controlledZ(controlBits, targetBits)
  • 353 |
  • controlledR(controlBits, targetBits, angle)
  • 354 |
  • controlledS(controlBits, targetBits)
  • 355 |
  • controlledT(controlBits, targetBits)
  • 356 |
  • controlledHadamard(controlBits, targetBits)
  • 357 |
  • cnot(controlBits, targetBits)
  • 358 |
  • controlledXRotation(controlBits, targetBits, angle)
  • 359 |
  • controlledYRotation(controlBits, targetBits, angle)
  • 360 |
  • controlledZRotation(controlBits, targetBits, angle)
  • 361 |
362 |

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 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 |
jsqubits("|0000>").controlledX(2, 0)leaves the state unchanged:|0000>
jsqubits("|0100>").controlledX(2, 0)flips bit zero to give:|0101>
jsqubits("|0010>").controlledX([1,3], 0)leaves the state unchanged:|0010>
jsqubits("|1010>").controlledX([1,3], 0)flips bit zero to give:|1011>
jsqubits("|1010>").controlledX({from:1, to: 3}, 0)leaves the state unchanged:|1010>
jsqubits("|1110>").controlledX({from:1, to: 3}, 0)flips bit zero to give:|1111>
jsqubits("|1010>").controlledX([2,3], [0,1])leaves the state unchanged:|1010>
jsqubits("|1110>").controlledX([2,3], [0,1])flips bit zero and one give:|1101>
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 |

427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 |
jsqubits("|010>").controlledXRotation(1, 0, Math.PI/2)gives(0.7071)|010> + (-0.7071i)|011>
436 |
437 | 438 |
439 |

Swap

440 |
    441 |
  • swap(bit1, bit2)
  • 442 |
  • controlledSwap(controlBits, bit1, bit2)
  • 443 |
444 |

445 | The swap and controlledSwap operators allow you to swap two qubits: 446 |

447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 |
jsqubits("|1110>").swap(1, 0)gives|1101>
jsqubits("|1110>").controlledSwap([2,3], 0, 1)gives|1101>
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 |

480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 |
jsqubits("|0110>").measure(2)gives{result: 1, newState: |0110>}
jsqubits("|0110>").measure({from:1, to: 3})gives{result: 3, newState: |0110>}
jsqubits("|0110>").measure(jsqubits.ALL)gives{result: 6, newState: |0110>}
jsqubits("|00>").hadamard(0).measure(1)gives{result: 0, newState: (0.7071)|00> + (0.7071)|01>}
jsqubits("|0110>").measure([1,3])gives{result: 1, newState: |0110>}
509 | 510 |

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 |

523 | 524 | jsqubits.QState.prototype.random = function() {return 0;}; 525 | jsqubits("|100>").hadamard([0,1]).measure(0) 526 | 527 |

528 | always results in 529 | {result: 0, newState: (0.7071)|100> + (0.7071)|110>} 530 |

531 |
532 | 533 |
534 |

Toffoli

535 |
    536 |
  • toffoli(controlBit, controlBit, ..., targetBit)
  • 537 |
538 |

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 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 |
jsqubits("|0001>").toffoli(0, 2, 3)leaves the state unchanged:|0001>
jsqubits("|0101>").toffoli(0, 2, 3)flips bit three to give:|1101>
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 |

575 |
576 | 577 |
578 |

applyFunction

579 |
    580 |
  • applyFunction(inputBits, targetBits, functionToApply)
  • 581 |
582 |

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 |

595 | 596 | jsqubits("|10110>") 597 | .applyFunction( 598 | {from:3, to:4}, 599 | {from:0, to:2}, 600 | function(x){return x+1;}) 601 | 602 |

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 |

655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 |
jsqubits.complex(3, 4)=3+4i
jsqubits.real(3)=3
jsqubits.complex(3, 4).add(jsqubits.complex(10, 20))=13+24i
jsqubits.complex(3, 4).add(7)=10+4i
jsqubits.complex(3, 4).subtract(jsqubits.complex(10, 20))=-7-16i
jsqubits.complex(3, 4).subtract(7)=-4+4i
jsqubits.complex(3, 4).multiply(jsqubits.complex(10, 20))=-50+100i
jsqubits.complex(3, 4).multiply(10)=30+40i
jsqubits.complex(3, 4).negate()=-3-4i
jsqubits.complex(3, 4).magnitude()=5
jsqubits.complex(1, 1).phase()=Math.PI/4
jsqubits.complex(1, -1).phase()=-Math.PI/4
jsqubits.complex(3, 4).conjugate()=3-4i
jsqubits.complex(-1.235959, 3.423523).format({decimalPlaces: 3})=-1.236+3.424i
jsqubits.ZERO=0
jsqubits.ONE=1
739 |
740 |

741 | 742 | 745 | 748 | 751 |

752 |

753 | Creative Commons License
jsqubits User Manual by David Kemp is licensed under a Creative Commons Attribution 3.0 Unported License. 754 |

755 | 756 | 757 | -------------------------------------------------------------------------------- /spec/qstate.spec.js: -------------------------------------------------------------------------------- 1 | import * as chai from 'chai'; 2 | import sinon from 'sinon'; 3 | import Q from '../lib/index.js'; 4 | 5 | const {QState, Complex} = Q; 6 | const {expect} = chai; 7 | 8 | describe('QState', function() { 9 | 10 | // Shifting these into a before block to keen eslint happy. 11 | let complex, real, hadamardMatrix; 12 | 13 | before(function() { 14 | complex = Q.complex; 15 | real = Q.real; 16 | hadamardMatrix = [ 17 | [Complex.SQRT1_2, Complex.SQRT1_2], 18 | [Complex.SQRT1_2, Complex.SQRT1_2.multiply(-1)] 19 | ]; 20 | }); 21 | 22 | describe('new', function() { 23 | it('will default amplitudes to the zero state |00...0>', function() { 24 | const x = new QState(3); 25 | expect(x.toString()).to.equal('|000>'); 26 | }); 27 | }); 28 | 29 | describe('#toString', function() { 30 | it('will use parenthesis to avoid ambiguity', function() { 31 | const a = []; a[0] = a[1] = new Complex(-0.5, -0.5); 32 | const x = new QState(1, a); 33 | expect(x.toString()).to.equal('(-0.5-0.5i)|0> + (-0.5-0.5i)|1>'); 34 | }); 35 | 36 | it('will round off long decimals', function() { 37 | const a = []; a[0] = a[1] = new Complex(Math.SQRT1_2, 0); 38 | const x = new QState(1, a); 39 | expect(x.toString()).to.equal('(0.7071)|0> + (0.7071)|1>'); 40 | }); 41 | 42 | it('will omit the amplitude when it is close to one', function() { 43 | const x = Q('|000>').rotateX(1, 0.1).rotateX(1, -0.1); 44 | expect(x.toString()).to.equal('|000>'); 45 | }); 46 | }); 47 | 48 | describe('#eql', function() { 49 | it('will be true for equal states', function() { 50 | const state1 = Q('|010>').hadamard(1); 51 | const state2 = Q('|010>').hadamard(1); 52 | expect(state1.equal(state2)).to.be.true; 53 | }); 54 | 55 | it('will be false when the amplitudes differ', function() { 56 | const state1 = Q('|01>').hadamard(0); 57 | const state2 = Q('|00>').hadamard(0); 58 | expect(state1.equal(state2)).to.be.false; 59 | }); 60 | 61 | it('will be false when the states differ', function() { 62 | expect(Q('|01>').equal(Q('|10>'))).to.be.false; 63 | }); 64 | 65 | it('will be false when the number of qubits differ', function() { 66 | const state1 = Q('|0>').hadamard(0); 67 | const state2 = Q('|00>').hadamard(0); 68 | expect(state1.equal(state2)).to.be.false; 69 | }); 70 | 71 | it('will be false when one state has more non-zero amplitudes than the other', function() { 72 | // Note: these obscure cases occur when the states are not normalized. 73 | expect(Q('|00>').add(Q('|01>')).equal(Q('|00>'))).to.be.false; 74 | expect(Q('|00>').equal(Q('|00>').add(Q('|01>')))).to.be.false; 75 | }); 76 | }); 77 | 78 | describe('#applyOperatorMatrix', function() { 79 | const matrix = [ 80 | [new Complex(2), new Complex(3)], 81 | [new Complex(4), new Complex(5)] 82 | ]; 83 | const amplitude = new Complex(6); 84 | 85 | it('applies the matrix to a 0 bit', function() { 86 | const result = QState.applyOperatorMatrix(matrix, 0, amplitude); 87 | expect(result[0].toString()).to.equal('12'); 88 | expect(result[1].toString()).to.equal('24'); 89 | }); 90 | 91 | it('applies the matrix to a 1 bit', function() { 92 | const result = QState.applyOperatorMatrix(matrix, 1, amplitude); 93 | expect(result[0].toString()).to.equal('18'); 94 | expect(result[1].toString()).to.equal('30'); 95 | }); 96 | }); 97 | 98 | describe('#applyToOneBit', function() { 99 | 100 | it('does nothing when control bits are zero', function() { 101 | const result = QState.applyToOneBit([1, 2], 0, hadamardMatrix, Q('|001>')); 102 | expect(result.toString()).to.equal('|001>'); 103 | }); 104 | 105 | it('applies an operator to a qubit (when control bits always match)', function() { 106 | // Initial state: (0.712)|101> - (0.712)|111> 107 | const initialAmplitudes = []; 108 | initialAmplitudes[5] = Complex.SQRT1_2; 109 | initialAmplitudes[7] = Complex.SQRT1_2.multiply(-1); 110 | const initialState = new QState(3, initialAmplitudes); 111 | 112 | const result = QState.applyToOneBit([0, 2], 1, hadamardMatrix, initialState); 113 | expect(result.toString()).to.equal('|111>'); 114 | }); 115 | 116 | it('applies an operator to a qubit (when control bits match only for some states)', function() { 117 | // Initial state: (0.712)|101> - (0.712)|111> 118 | const initialAmplitudes = []; 119 | initialAmplitudes[5] = Complex.SQRT1_2; 120 | initialAmplitudes[7] = Complex.SQRT1_2.multiply(-1); 121 | const initialState = new QState(3, initialAmplitudes); 122 | 123 | const result = QState.applyToOneBit([1, 2], 0, hadamardMatrix, initialState); 124 | expect(result.toString()).to.equal('(0.7071)|101> + (-0.5)|110> + (0.5)|111>'); 125 | }); 126 | }); 127 | 128 | describe('#controlledApplicationOfqBitOperator', function() { 129 | it('does nothing when the control bit is zero (one target)', function() { 130 | const x = Q('|001>').controlledApplicationOfqBitOperator(2, 0, hadamardMatrix); 131 | expect(x.toString()).to.equal('|001>'); 132 | }); 133 | 134 | it('does nothing when the control bit is zero (target range)', function() { 135 | const targetBits = {from: 0, to: 1}; 136 | const x = Q('|001>').controlledApplicationOfqBitOperator(2, targetBits, hadamardMatrix); 137 | expect(x.toString()).to.equal('|001>'); 138 | }); 139 | 140 | it('does nothing when the control bit is zero (target array)', function() { 141 | const x = Q('|0001>').controlledApplicationOfqBitOperator(3, [0, 2], hadamardMatrix); 142 | expect(x.toString()).to.equal('|0001>'); 143 | }); 144 | 145 | it('invokes the qbitFunction when the control bit is one', function() { 146 | const x = Q('|100>').controlledApplicationOfqBitOperator(2, 0, hadamardMatrix); 147 | expect(x.toString()).to.equal('(0.7071)|100> + (0.7071)|101>'); 148 | }); 149 | 150 | it('invokes the qbitFunction when the control bit specifier is null', function() { 151 | const x = Q('|000>').controlledApplicationOfqBitOperator(null, 0, hadamardMatrix); 152 | expect(x.toString()).to.equal('(0.7071)|000> + (0.7071)|001>'); 153 | }); 154 | 155 | it('invokes the qbitFunction on a range of target bits', function() { 156 | const targetBits = {from: 0, to: 1}; 157 | const x = Q('|101>').controlledApplicationOfqBitOperator(2, targetBits, hadamardMatrix); 158 | expect(x.toString()).to.equal('(0.5)|100> + (-0.5)|101> + (0.5)|110> + (-0.5)|111>'); 159 | }); 160 | 161 | it('does nothing when any of the control bits are zero (control bit range)', function() { 162 | const controlBits = {from: 1, to: 2}; 163 | const x = Q('|101>').controlledApplicationOfqBitOperator(controlBits, 0, hadamardMatrix); 164 | expect(x.toString()).to.equal('|101>'); 165 | 166 | }); 167 | 168 | it('does nothing when any of the control bits are zero (control bit array)', function() { 169 | const controlBits = [1, 2]; 170 | const x = Q('|101>').controlledApplicationOfqBitOperator(controlBits, 0, hadamardMatrix); 171 | expect(x.toString()).to.equal('|101>'); 172 | }); 173 | 174 | it('invokes the qbitFunction when the control bits are all one (control bit range)', function() { 175 | const controlBits = {from: 1, to: 2}; 176 | const x = Q('|110>').controlledApplicationOfqBitOperator(controlBits, 0, hadamardMatrix); 177 | expect(x.toString()).to.equal('(0.7071)|110> + (0.7071)|111>'); 178 | }); 179 | 180 | it('invokes the qbitFunction when the control bits are all one (control bit array)', function() { 181 | const controlBits = [1, 3]; 182 | const x = Q('|1010>').controlledApplicationOfqBitOperator(controlBits, 0, hadamardMatrix); 183 | expect(x.toString()).to.equal('(0.7071)|1010> + (0.7071)|1011>'); 184 | }); 185 | 186 | it('throws an error when the control and target bits overlap', function() { 187 | const badFunctionInvocation = function () { 188 | Q('0000').controlledApplicationOfqBitOperator({from: 0, to: 2}, {from: 2, to: 3}, hadamardMatrix); 189 | }; 190 | expect(badFunctionInvocation).to.throw('control and target bits must not be the same nor overlap'); 191 | }); 192 | }); 193 | 194 | describe('#x', function() { 195 | it('applies the Pauli x operator to (|0>)', function() { 196 | const x = Q('|0>').x(0); 197 | expect(x.amplitude('|0>').equal(Q.ZERO)).to.be.true; 198 | expect(x.amplitude('|1>').equal(Q.ONE)).to.be.true; 199 | }); 200 | 201 | it('applies the Pauli x operator to (|1>)', function() { 202 | const x = Q('|1>').x(0); 203 | expect(x.amplitude('|0>').equal(Q.ONE)).to.be.true; 204 | expect(x.amplitude('|1>').equal(Q.ZERO)).to.be.true; 205 | }); 206 | }); 207 | 208 | describe('#controlledX', function() { 209 | it('does nothing when the control bit is zero', function() { 210 | const x = Q('|000>').controlledX(2, 0); 211 | expect(x.amplitude('|000>').closeTo(Q.ONE)).to.be.true; 212 | }); 213 | 214 | it('flips the target bit when the control bit is one', function() { 215 | const x = Q('|100>').controlledX(2, 0); 216 | expect(x.amplitude('|101>').closeTo(Q.ONE)).to.be.true; 217 | }); 218 | }); 219 | 220 | describe('#toffoli', function() { 221 | it('does nothing if any of the control bits are zero', function() { 222 | const x = Q('|0010>').toffoli(3, 1, 0); 223 | expect(x.amplitude('|0010>').closeTo(Q.ONE)).to.be.true; 224 | }); 225 | 226 | it('flips the target bit when all of the control bits are one', function() { 227 | const x = Q('|1010>').toffoli(3, 1, 0); 228 | expect(x.amplitude('|1011>').closeTo(Q.ONE)).to.be.true; 229 | }); 230 | }); 231 | 232 | describe('#z', function() { 233 | it('applies the Pauli z operator to (|0>)', function() { 234 | const x = Q('|0>').z(0); 235 | expect(x.amplitude('|0>').equal(Q.ONE)).to.be.true; 236 | expect(x.amplitude('|1>').equal(Q.ZERO)).to.be.true; 237 | }); 238 | 239 | it('applies the Pauli z operator to (|1>)', function() { 240 | const x = Q('|1>').z(0); 241 | expect(x.amplitude('|0>').equal(Q.ZERO)).to.be.true; 242 | expect(x.amplitude('|1>').equal(real(-1))).to.be.true; 243 | }); 244 | }); 245 | 246 | describe('#controlledZ', function() { 247 | it('does nothing when the control bit is zero', function() { 248 | const x = Q('|001>').controlledZ(2, 0); 249 | expect(x.equal(Q('|001>'))).to.be.true; 250 | }); 251 | 252 | it('flips the phase when both the control and target bits are one', function() { 253 | const x = Q('|101>').controlledZ(2, 0); 254 | expect(x.amplitude('|101>').closeTo(real(-1))).to.be.true; 255 | }); 256 | 257 | it('does nothing when an even number of the target bits are 1 when the control bit is one', function() { 258 | const targetBits = {from: 0, to: 1}; 259 | const x = Q('|111>').controlledZ(2, targetBits); 260 | expect(x.amplitude('|111>').closeTo(Q.ONE)).to.be.true; 261 | }); 262 | }); 263 | 264 | describe('#y', function() { 265 | it('applies the Pauli y operator to (|0>)', function() { 266 | const x = Q('|0>').y(0); 267 | expect(x.amplitude('|0>').equal(Q.ZERO)).to.be.true; 268 | expect(x.amplitude('|1>').equal(complex(0, 1))).to.be.true; 269 | }); 270 | 271 | it('applies the Pauli y operator to (|1>)', function() { 272 | const x = Q('|1>').y(0); 273 | expect(x.amplitude('|0>').equal(complex(0, -1))).to.be.true; 274 | expect(x.amplitude('|1>').equal(Q.ZERO)).to.be.true; 275 | }); 276 | }); 277 | 278 | describe('#controlledY', function() { 279 | it('does nothing when the control bit is zero', function() { 280 | const x = Q('|001>').controlledY(2, 0); 281 | expect(x.equal(Q('|001>'))).to.be.true; 282 | }); 283 | 284 | it('applies the Pauli y operator when the control bit is one', function() { 285 | const x = Q('|101>').controlledY(0, 2); 286 | expect(x.amplitude('|001>').equal(complex(0, -1))).to.be.true; 287 | expect(x.amplitude('|101>').equal(Q.ZERO)).to.be.true; 288 | }); 289 | }); 290 | 291 | describe('#s', function() { 292 | it('leaves |0> untouched', function() { 293 | const x = Q('|0>').s(0); 294 | expect(x.equal(Q('|0>'))).to.be.true; 295 | }); 296 | 297 | it('multiplies the amplitude of |1> by i', function() { 298 | const x = Q('|1>').s(0); 299 | expect(x.amplitude('|0>').equal(Q.ZERO)).to.be.true; 300 | expect(x.amplitude('|1>').equal(complex(0, 1))).to.be.true; 301 | }); 302 | }); 303 | 304 | describe('#controlledS', function() { 305 | it('does nothing when the control bit is zero', function() { 306 | const x = Q('|100>').controlledS(0, 2); 307 | expect(x.equal(Q('|100>'))).to.be.true; 308 | }); 309 | 310 | it('applies the S operator when the control bit is one (target is 1)', function() { 311 | const x = Q('|101>').controlledS(0, 2); 312 | expect(x.amplitude('|101>').equal(complex(0, 1))).to.be.true; 313 | }); 314 | 315 | it('applies the S operator when the control bit is one (target is 0)', function() { 316 | const x = Q('|001>').controlledS(0, 2); 317 | expect(x.equal(Q('|001>'))).to.be.true; 318 | }); 319 | }); 320 | 321 | describe('#t', function() { 322 | it('leaves |0> untouched', function() { 323 | const x = Q('|0>').t(0); 324 | expect(x.equal(Q('|0>'))).to.be.true; 325 | }); 326 | 327 | it('multiplies the amplitude of |1> by e^(i pi/4)', function() { 328 | const x = Q('|1>').t(0); 329 | expect(x.amplitude('|0>').equal(Q.ZERO)).to.be.true; 330 | expect(x.amplitude('|1>').closeTo(complex(Math.cos(Math.PI / 4), Math.sin(Math.PI / 4)))).to.be.true; 331 | }); 332 | }); 333 | 334 | describe('#controlledT', function() { 335 | it('does nothing when the control bit is zero', function() { 336 | const x = Q('|100>').controlledT(0, 2); 337 | expect(x.equal(Q('|100>'))).to.be.true; 338 | }); 339 | 340 | it('applies the T operator when the control bit is one (target is 1)', function() { 341 | const x = Q('|101>').controlledT(0, 2); 342 | expect(x.amplitude('|101>').closeTo(complex(Math.cos(Math.PI / 4), Math.sin(Math.PI / 4)))).to.be.true; 343 | }); 344 | 345 | it('applies the T operator when the control bit is one (target is 0)', function() { 346 | const x = Q('|001>').controlledT(0, 2); 347 | expect(x.equal(Q('|001>'))).to.be.true; 348 | }); 349 | }); 350 | 351 | describe('#hadamard', function() { 352 | it('applies the hadamard operation', function() { 353 | const x = Q('|000>').hadamard(2); 354 | expect(x.amplitude('|000>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 355 | expect(x.amplitude('|001>').equal(Q.ZERO)).to.be.true; 356 | expect(x.amplitude('|010>').equal(Q.ZERO)).to.be.true; 357 | expect(x.amplitude('|011>').equal(Q.ZERO)).to.be.true; 358 | expect(x.amplitude('|100>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 359 | expect(x.amplitude('|101>').equal(Q.ZERO)).to.be.true; 360 | expect(x.amplitude('|110>').equal(Q.ZERO)).to.be.true; 361 | expect(x.amplitude('|111>').equal(Q.ZERO)).to.be.true; 362 | }); 363 | 364 | it("is it's own inverse", function() { 365 | const x = Q('|000>').hadamard(2).hadamard(2); 366 | expect(x.amplitude('|000>').closeTo(Q.ONE)).to.be.true; 367 | expect(x.amplitude('|001>').equal(Q.ZERO)).to.be.true; 368 | expect(x.amplitude('|010>').equal(Q.ZERO)).to.be.true; 369 | expect(x.amplitude('|011>').equal(Q.ZERO)).to.be.true; 370 | expect(x.amplitude('|100>').equal(Q.ZERO)).to.be.true; 371 | expect(x.amplitude('|101>').equal(Q.ZERO)).to.be.true; 372 | expect(x.amplitude('|110>').equal(Q.ZERO)).to.be.true; 373 | expect(x.amplitude('|111>').equal(Q.ZERO)).to.be.true; 374 | }); 375 | 376 | it('accepts an ALL parameter', function() { 377 | const x = Q('|00>').hadamard(Q.ALL); 378 | expect(x.amplitude('|00>').closeTo(real(0.5))).to.be.true; 379 | expect(x.amplitude('|01>').closeTo(real(0.5))).to.be.true; 380 | expect(x.amplitude('|10>').closeTo(real(0.5))).to.be.true; 381 | expect(x.amplitude('|11>').closeTo(real(0.5))).to.be.true; 382 | }); 383 | 384 | it('accepts a bit range', function() { 385 | const x = Q('|000>').hadamard({from: 1, to: 2}); 386 | expect(x.amplitude('|000>').closeTo(real(0.5))).to.be.true; 387 | expect(x.amplitude('|001>').equal(Q.ZERO)).to.be.true; 388 | expect(x.amplitude('|010>').closeTo(real(0.5))).to.be.true; 389 | expect(x.amplitude('|011>').equal(Q.ZERO)).to.be.true; 390 | expect(x.amplitude('|100>').closeTo(real(0.5))).to.be.true; 391 | expect(x.amplitude('|101>').equal(Q.ZERO)).to.be.true; 392 | expect(x.amplitude('|110>').closeTo(real(0.5))).to.be.true; 393 | expect(x.amplitude('|111>').equal(Q.ZERO)).to.be.true; 394 | }); 395 | }); 396 | 397 | describe('#controlledHadamard', function() { 398 | it('does nothing when the control bit is zero', function() { 399 | const x = Q('|001>').controlledHadamard(2, 0); 400 | expect(x.toString()).to.equal('|001>'); 401 | }); 402 | 403 | it('applies the Hadamard operator when the control bits are one', function() { 404 | const x = Q('|111>').controlledHadamard({from: 1, to: 2}, 0); 405 | expect(x.toString()).to.equal('(0.7071)|110> + (-0.7071)|111>'); 406 | }); 407 | }); 408 | 409 | describe('#rotateX', function() { 410 | it('rotates about the X axis', function() { 411 | const x = Q('|00>').rotateX(1, Math.PI / 4); 412 | 413 | expect(x.amplitude('|00>').closeTo(real(Math.cos(Math.PI / 8)))).to.be.true; 414 | expect(x.amplitude('|01>').equal(Q.ZERO)).to.be.true; 415 | expect(x.amplitude('|10>').closeTo(complex(0, -Math.sin(Math.PI / 8)))).to.be.true; 416 | expect(x.amplitude('|11>').equal(Q.ZERO)).to.be.true; 417 | }); 418 | 419 | it('can be applied multiple times', function() { 420 | const x = Q('|00>').rotateX(1, Math.PI / 4).rotateX(1, Math.PI / 4).rotateX(1, Math.PI / 4); 421 | 422 | expect(x.amplitude('|00>').closeTo(real(Math.cos(3 * Math.PI / 8)))).to.be.true; 423 | expect(x.amplitude('|01>')).to.equal(Q.ZERO); 424 | expect(x.amplitude('|10>').closeTo(complex(0, -Math.sin(3 * Math.PI / 8)))).to.be.true; 425 | expect(x.amplitude('|11>')).to.equal(Q.ZERO); 426 | }); 427 | 428 | it('is accepts an ALL parameter', function() { 429 | const x = Q('|00>').rotateX(Q.ALL, Math.PI / 4); 430 | 431 | expect(x.amplitude('|00>').closeTo(real(Math.cos(Math.PI / 8) * Math.cos(Math.PI / 8)))).to.be.true; 432 | expect(x.amplitude('|01>').closeTo(real(Math.cos(Math.PI / 8)).multiply(complex(0, -Math.sin(Math.PI / 8))))).to.be.true; 433 | expect(x.amplitude('|10>').closeTo(real(Math.cos(Math.PI / 8)).multiply(complex(0, -Math.sin(Math.PI / 8))))).to.be.true; 434 | expect(x.amplitude('|11>').closeTo(real(-Math.sin(Math.PI / 8) * Math.sin(Math.PI / 8)))).to.be.true; 435 | }); 436 | }); 437 | 438 | describe('#controlledXRotation', function() { 439 | it('does nothing when the control bit is zero', function() { 440 | const x = Q('|001>').controlledXRotation(2, 0, Math.PI / 4); 441 | expect(x.equal(Q('|001>'))).to.be.true; 442 | }); 443 | 444 | it('rotates around the x axis when the control bit is one', function() { 445 | const x = Q('|100>').controlledXRotation(2, 0, Math.PI / 4); 446 | expect(x.amplitude('|100>').closeTo(real(Math.cos(Math.PI / 8)))).to.be.true; 447 | expect(x.amplitude('|101>').closeTo(complex(0, -Math.sin(Math.PI / 8)))).to.be.true; 448 | }); 449 | }); 450 | 451 | describe('#rotateY', function() { 452 | it('rotates about the Y axis', function() { 453 | const x = Q('|00>').rotateY(1, Math.PI / 4); 454 | expect(x.amplitude('|00>').closeTo(real(Math.cos(Math.PI / 8)))).to.be.true; 455 | expect(x.amplitude('|01>').equal(Q.ZERO)).to.be.true; 456 | expect(x.amplitude('|10>').closeTo(real(Math.sin(Math.PI / 8)))).to.be.true; 457 | expect(x.amplitude('|11>').equal(Q.ZERO)).to.be.true; 458 | }); 459 | 460 | it('can be applied multiple times', function() { 461 | const x = Q('|00>').rotateY(1, Math.PI / 4).rotateY(1, Math.PI / 4).rotateY(1, Math.PI / 4); 462 | expect(x.amplitude('|00>').closeTo(real(Math.cos(3 * Math.PI / 8)))).to.be.true; 463 | expect(x.amplitude('|01>').equal(Q.ZERO)).to.be.true; 464 | expect(x.amplitude('|10>').closeTo(real(Math.sin(3 * Math.PI / 8)))).to.be.true; 465 | expect(x.amplitude('|11>').equal(Q.ZERO)).to.be.true; 466 | }); 467 | }); 468 | 469 | describe('#controlledYRotation', function() { 470 | it('does nothing when the control bit is zero', function() { 471 | const x = Q('|001>').controlledYRotation(2, 0, Math.PI / 4); 472 | expect(x.toString()).to.equal('|001>'); 473 | }); 474 | 475 | it('rotates around the y axis when the control bit is one', function() { 476 | const x = Q('|100>').controlledYRotation(2, 0, Math.PI / 4); 477 | expect(x.toString()).to.equal('(0.9239)|100> + (0.3827)|101>'); 478 | }); 479 | }); 480 | 481 | describe('#rotateZ', function() { 482 | it('rotates about the Z axis (|0>)', function() { 483 | const x = Q('|0>').rotateZ(0, Math.PI / 4); 484 | expect(x.amplitude('|0>').closeTo(complex(Math.cos(Math.PI / 8), -Math.sin(Math.PI / 8)))).to.be.true; 485 | expect(x.amplitude('|1>').equal(Q.ZERO)).to.be.true; 486 | }); 487 | 488 | it('rotates about the Z axis (|1>)', function() { 489 | const x = Q('|1>').rotateZ(0, Math.PI / 4); 490 | expect(x.amplitude('|0>').equal(Q.ZERO)).to.be.true; 491 | expect(x.amplitude('|1>').closeTo(complex(Math.cos(Math.PI / 8), Math.sin(Math.PI / 8)))).to.be.true; 492 | }); 493 | 494 | it('can be applied multiple times', function() { 495 | const x = Q('|0>').rotateZ(0, Math.PI / 4).rotateZ(0, Math.PI / 4).rotateZ(0, Math.PI / 4); 496 | expect(x.amplitude('|0>').closeTo(complex(Math.cos(3 * Math.PI / 8), -Math.sin(3 * Math.PI / 8)))).to.be.true; 497 | expect(x.amplitude('|1>').equal(Q.ZERO)).to.be.true; 498 | }); 499 | }); 500 | 501 | describe('#controlledZRotation', function() { 502 | it('does nothing when the control bit is zero', function() { 503 | const x = Q('|001>').controlledZRotation(2, 0, Math.PI / 4); 504 | expect(x.equal(Q('|001>'))).to.be.true; 505 | }); 506 | 507 | it('rotates around the z axis when the control bit is one', function() { 508 | const x = Q('|100>').controlledZRotation(2, 0, Math.PI / 4); 509 | expect(x.amplitude('|100>').closeTo(complex(Math.cos(Math.PI / 8), -Math.sin(Math.PI / 8)))).to.be.true; 510 | expect(x.amplitude('|101>').equal(Q.ZERO)).to.be.true; 511 | }); 512 | }); 513 | 514 | describe('#controlledR', function() { 515 | it('does nothing when the control bit is zero', function() { 516 | const originalState = Q('|000>').hadamard(0); 517 | const x = originalState.controlledR(2, 0, Math.PI / 4); 518 | expect(x.toString()).to.equal(originalState.toString()); 519 | }); 520 | 521 | it('shifts the phase by e^(i angle) when the control bit is one', function() { 522 | const originalState = Q('|100>').hadamard(0); 523 | const x = originalState.controlledR(2, 0, Math.PI / 4); 524 | expect(x.toString()).to.equal('(0.7071)|100> + (0.5+0.5i)|101>'); 525 | }); 526 | }); 527 | 528 | 529 | describe('#r', function() { 530 | it('shifts the phase by the specified angle e^(i angle)', function() { 531 | const originalState = Q('|0>').hadamard(0); 532 | const x = originalState.r(0, Math.PI / 4); 533 | expect(x.toString()).to.equal('(0.7071)|0> + (0.5+0.5i)|1>'); 534 | }); 535 | }); 536 | 537 | describe('#controlledSwap', function() { 538 | it('does nothing when the control bit is zero', function() { 539 | expect(Q('|010>').controlledSwap(2, 1, 0).toString()).to.equal('|010>'); 540 | }); 541 | 542 | it('swaps the target bits when the control bit is one', function() { 543 | expect(Q('|110>').controlledSwap(2, 1, 0).toString()).to.equal('|101>'); 544 | }); 545 | }); 546 | 547 | describe('#swap', function() { 548 | it('swaps the target bits', function() { 549 | expect(Q('|10>').swap(1, 0).toString()).to.equal('|01>'); 550 | }); 551 | }); 552 | 553 | describe('#cnot', function() { 554 | it('does nothing when the control bit is zero', function() { 555 | const x = Q('|000>').cnot(2, 0); 556 | expect(x.equal(Q('|000>'))).to.be.true; 557 | }); 558 | 559 | it('flips the target bit from zero to one when the control bit is one', function() { 560 | const x = Q('|100>').cnot(2, 0); 561 | expect(x.equal(Q('|101>'))).to.be.true; 562 | }); 563 | 564 | it('flips the target bit from one to zero when the control bit is one', function() { 565 | const x = Q('|101>').cnot(2, 0); 566 | expect(x.equal(Q('|100>'))).to.be.true; 567 | }); 568 | }); 569 | 570 | describe('Simple combination of hadamard and cnot', function() { 571 | it('results in a phase kick back', function() { 572 | const x = Q('|01>').hadamard(0).hadamard(1).cnot(1, 0) 573 | .hadamard(0) 574 | .hadamard(1); 575 | expect(x.amplitude('|00>').equal(Q.ZERO)).to.be.true; 576 | expect(x.amplitude('|01>').equal(Q.ZERO)).to.be.true; 577 | expect(x.amplitude('|10>').equal(Q.ZERO)).to.be.true; 578 | expect(x.amplitude('|11>').closeTo(Q.ONE)).to.be.true; 579 | }); 580 | }); 581 | 582 | describe('#applyFunction', function() { 583 | it('invokes function with states (bit range)', function() { 584 | const f = sinon.spy(() => 1); 585 | const x = Q('|1000>').hadamard(2); 586 | x.applyFunction({from: 1, to: 2}, 0, f); 587 | expect(f.called).to.be.true; 588 | expect(f.args).to.deep.include([0]); 589 | expect(f.args).to.deep.include([2]); 590 | }); 591 | 592 | it('invokes function with states (single bit)', function() { 593 | const f = sinon.spy(() => 1); 594 | const x = Q('|1000>').hadamard(2); 595 | x.applyFunction(2, 0, f); 596 | expect(f.args).to.deep.include([0]); 597 | expect(f.args).to.deep.include([1]); 598 | }); 599 | 600 | it('does nothing when the funciton returns zero', function() { 601 | const f = sinon.spy(() => 0); 602 | const x = Q('|00>').applyFunction(1, 0, f); 603 | expect(f.called).to.be.true; 604 | expect(x.equal(Q('|00>'))).to.be.true; 605 | }); 606 | 607 | it('flips the target bit from zero to one when the function returns one', function() { 608 | const f = function (x) { return 1; }; 609 | const x = Q('|00>').applyFunction(1, 0, f); 610 | expect(x.equal(Q('|01>'))).to.be.true; 611 | }); 612 | 613 | it('flips the target bit from one to zero when the function returns one', function() { 614 | const f = function (x) { return 1; }; 615 | const x = Q('|01>').applyFunction(1, 0, f); 616 | expect(x.equal(Q('|00>'))).to.be.true; 617 | }); 618 | 619 | it('can flip multiple target bits', function() { 620 | const f = function (x) { return 0b101; }; 621 | const x = Q('|1011>').applyFunction(3, {from: 0, to: 2}, f); 622 | expect(x.equal(Q('|1110>'))).to.be.true; 623 | }); 624 | 625 | it('restricts flipping of target bits to those specified', function() { 626 | const f = function (x) { return 0b1101; }; 627 | const x = Q('|1011>').applyFunction(3, {from: 0, to: 2}, f); 628 | expect(x.equal(Q('|1110>'))).to.be.true; 629 | }); 630 | 631 | it('throws exception when target and control bits overlap', function() { 632 | const badFunctionInvocation = function () { 633 | Q('0000').applyFunction({from: 0, to: 2}, {from: 2, to: 3}, (x) => { return x; }); 634 | }; 635 | expect(badFunctionInvocation).to.throw('control and target bits must not be the same nor overlap'); 636 | }); 637 | }); 638 | 639 | describe('#each', function() { 640 | it('should invoke a callback with a QStateComponent', function() { 641 | const callBack = sinon.spy(); 642 | Q('|10>').hadamard(1).each(callBack); 643 | expect(callBack.called).to.be.true; 644 | expect(callBack.args.length).to.be.equal(2); 645 | let stateWithAmplitude0 = callBack.getCall(0).args[0]; 646 | let stateWithAmplitude2 = callBack.getCall(1).args[0]; 647 | const index0 = stateWithAmplitude0.index; 648 | expect(index0 === '0' || index0 === '2').to.be.true; 649 | if (index0 === '2') { 650 | const tmp = stateWithAmplitude0; 651 | stateWithAmplitude0 = stateWithAmplitude2; 652 | stateWithAmplitude2 = tmp; 653 | } 654 | expect(stateWithAmplitude0.index).to.equal('0'); 655 | expect(stateWithAmplitude2.index).to.equal('2'); 656 | expect(stateWithAmplitude0.amplitude.closeTo(real(Math.sqrt(0.5)))).to.be.true; 657 | expect(stateWithAmplitude2.amplitude.closeTo(real(-Math.sqrt(0.5)))).to.be.true; 658 | expect(stateWithAmplitude0.asNumber()).to.equal(0); 659 | expect(stateWithAmplitude2.asNumber()).to.equal(2); 660 | expect(stateWithAmplitude0.asBitString()).to.equal('00'); 661 | expect(stateWithAmplitude2.asBitString()).to.equal('10'); 662 | }); 663 | 664 | it('should break early when returned false', function() { 665 | let callCount = 0; 666 | Q('|10>').hadamard(Q.ALL).each((stateWithAmplitude) => { 667 | callCount++; 668 | if (callCount === 1) return false; 669 | }); 670 | expect(callCount).to.equal(1); 671 | }); 672 | }); 673 | 674 | describe('#measure', function() { 675 | const bitRange = {from: 1, to: 2}; 676 | let stateToMeasure; 677 | 678 | beforeEach(function() { 679 | // 0.5 |1000> + 0.5 |1001> + 0.5 |1100> + 0.5 |1101> 680 | stateToMeasure = Q('|1000>').hadamard(2).hadamard(0); 681 | }); 682 | 683 | it('should return the new states for outcome of 00 (random returns 0)', function() { 684 | stateToMeasure.random = function () { return 0; }; 685 | const measurement = stateToMeasure.measure(bitRange); 686 | const newState = measurement.newState; 687 | expect(newState.numBits()).to.equal(4); 688 | expect(measurement.result).to.equal(0); 689 | expect(newState.amplitude('|1000>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 690 | expect(newState.amplitude('|1001>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 691 | expect(newState.amplitude('|1100>').equal(Q.ZERO)).to.be.true; 692 | expect(newState.amplitude('|1101>').equal(Q.ZERO)).to.be.true; 693 | }); 694 | 695 | it('should return the new states for outcome of 00 (random returns 0.49)', function() { 696 | stateToMeasure.random = function () { return 0.49; }; 697 | const measurement = stateToMeasure.measure(bitRange); 698 | const newState = measurement.newState; 699 | expect(newState.numBits()).to.equal(4); 700 | expect(measurement.result).to.equal(0); 701 | expect(newState.amplitude('|1000>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 702 | expect(newState.amplitude('|1001>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 703 | expect(newState.amplitude('|1100>').equal(Q.ZERO)).to.be.true; 704 | expect(newState.amplitude('|1101>').equal(Q.ZERO)).to.be.true; 705 | }); 706 | 707 | it('should return the new states for outcome of 10 (random returns 0.51)', function() { 708 | stateToMeasure.random = function () { return 0.51; }; 709 | const measurement = stateToMeasure.measure(bitRange); 710 | const newState = measurement.newState; 711 | expect(newState.numBits()).to.equal(4); 712 | expect(measurement.result).to.equal(2); 713 | expect(newState.amplitude('|1000>').equal(Q.ZERO)).to.be.true; 714 | expect(newState.amplitude('|1001>').equal(Q.ZERO)).to.be.true; 715 | expect(newState.amplitude('|1100>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 716 | expect(newState.amplitude('|1101>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 717 | }); 718 | 719 | it('should return the new states for outcome of 10 (random returns 1.0)', function() { 720 | stateToMeasure.random = function () { return 1.0; }; 721 | const measurement = stateToMeasure.measure(bitRange); 722 | const newState = measurement.newState; 723 | expect(newState.numBits()).to.equal(4); 724 | expect(measurement.result).to.equal(2); 725 | expect(newState.amplitude('|1000>')).to.equal(Q.ZERO); 726 | expect(newState.amplitude('|1001>')).to.equal(Q.ZERO); 727 | expect(newState.amplitude('|1100>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 728 | expect(newState.amplitude('|1101>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 729 | }); 730 | 731 | it('Can measure bit zero', function() { 732 | stateToMeasure.random = function () { return 1.0; }; 733 | const measurement = stateToMeasure.measure(0); 734 | const newState = measurement.newState; 735 | expect(newState.numBits()).to.equal(4); 736 | expect(measurement.result).to.equal(1); 737 | expect(newState.amplitude('|1000>')).to.equal(Q.ZERO); 738 | expect(newState.amplitude('|1001>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 739 | expect(newState.amplitude('|1100>')).to.equal(Q.ZERO); 740 | expect(newState.amplitude('|1101>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 741 | }); 742 | 743 | it('Can measure all bits', function() { 744 | stateToMeasure.random = function () { return 0.49; }; 745 | const measurement = stateToMeasure.measure(Q.ALL); 746 | const newState = measurement.newState; 747 | expect(newState.numBits()).to.equal(4); 748 | expect(measurement.result).to.equal(0b1001); 749 | expect(newState.amplitude('|1000>')).to.equal(Q.ZERO); 750 | expect(newState.amplitude('|1001>').closeTo(Q.ONE)).to.be.true; 751 | expect(newState.amplitude('|1100>')).to.equal(Q.ZERO); 752 | expect(newState.amplitude('|1101>')).to.equal(Q.ZERO); 753 | }); 754 | 755 | it('Can measure selected bits', function() { 756 | stateToMeasure.random = function () { return 0.51; }; 757 | const measurement = stateToMeasure.measure([0, 3]); 758 | const newState = measurement.newState; 759 | expect(newState.numBits()).to.equal(4); 760 | expect(measurement.result).to.equal(2); 761 | expect(newState.amplitude('|1000>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 762 | expect(newState.amplitude('|1001>')).to.equal(Q.ZERO); 763 | expect(newState.amplitude('|1100>').closeTo(real(1 / Math.sqrt(2)))).to.be.true; 764 | expect(newState.amplitude('|1101>')).to.equal(Q.ZERO); 765 | }); 766 | 767 | it('actually calls Math.random', function() { 768 | const measurement = stateToMeasure.measure(3); 769 | const newState = measurement.newState; 770 | expect(newState.numBits()).to.equal(4); 771 | expect(measurement.result).to.equal(1); 772 | expect(newState.amplitude('|1000>').closeTo(real(0.5))).to.be.true; 773 | expect(newState.amplitude('|1001>').closeTo(real(0.5))).to.be.true; 774 | expect(newState.amplitude('|1100>').closeTo(real(0.5))).to.be.true; 775 | expect(newState.amplitude('|1101>').closeTo(real(0.5))).to.be.true; 776 | }); 777 | }); 778 | 779 | describe('Measurement', function() { 780 | it('should have a asBitString() function', function() { 781 | expect(Q('|0101>').measure({from: 1, to: 3}).asBitString()).to.equal('010'); 782 | }); 783 | }); 784 | 785 | describe('#kron', function() { 786 | it('should return the tensor product of two states', function() { 787 | const q1 = Q('|01>').hadamard(0); /* sqrt(1/2)(|00> - |01> */ 788 | const q2 = Q('|100>').hadamard(2); /* sqrt(1/2) |000> - |100> */ 789 | const newState = q1.kron(q2); /* Should be 0.5 (|00000> - |00100> - |01000> + |01100> */ 790 | expect(newState.amplitude('|00000>').closeTo(real(0.5))).to.be.true; 791 | expect(newState.amplitude('|00100>').closeTo(real(-0.5))).to.be.true; 792 | expect(newState.amplitude('|01000>').closeTo(real(-0.5))).to.be.true; 793 | expect(newState.amplitude('|01100>').closeTo(real(0.5))).to.be.true; 794 | }); 795 | }); 796 | 797 | describe('#tensorProduct', function() { 798 | it('should be an alias for #kron', function() { 799 | const q1 = Q('|01>').hadamard(0); /* sqrt(1/2)(|00> - |01> */ 800 | const q2 = Q('|100>').hadamard(2); /* sqrt(1/2) |000> - |100> */ 801 | const newState = q1.tensorProduct(q2); /* Should be 0.5 (|00000> - |00100> - |01000> + |01100> */ 802 | expect(newState.amplitude('|00000>').closeTo(real(0.5))).to.be.true; 803 | expect(newState.amplitude('|00100>').closeTo(real(-0.5))).to.be.true; 804 | expect(newState.amplitude('|01000>').closeTo(real(-0.5))).to.be.true; 805 | expect(newState.amplitude('|01100>').closeTo(real(0.5))).to.be.true; 806 | }); 807 | }); 808 | 809 | describe('#multiply', function() { 810 | it('should modify the global phase', function() { 811 | const q = Q('|1>').hadamard(0).multiply(complex(3, -4)); 812 | expect(q.amplitude('|0>').closeTo(real(Math.sqrt(0.5)).multiply(complex(3, -4)))).to.be.true; 813 | expect(q.amplitude('|1>').closeTo(real(-Math.sqrt(0.5)).multiply(complex(3, -4)))).to.be.true; 814 | }); 815 | 816 | it('should accept non-complex numbers', function() { 817 | const q = Q('|1>').hadamard(0).multiply(2); 818 | expect(q.amplitude('|0>').closeTo(real(Math.sqrt(2)))).to.be.true; 819 | expect(q.amplitude('|1>').closeTo(real(-Math.sqrt(2)))).to.be.true; 820 | }); 821 | }); 822 | 823 | describe('#add', function() { 824 | it('should add two quantum states together', function() { 825 | // NOTE: You will need to normalize afterwards to get a sensible answer! 826 | const q = Q('|01>').hadamard(0).add(Q('|00>')).add(Q('|11>')); 827 | expect(q.amplitude('|00>').closeTo(complex(1 + Math.sqrt(0.5)))).to.be.true; 828 | expect(q.amplitude('|01>').closeTo(complex(-Math.sqrt(0.5)))).to.be.true; 829 | expect(q.amplitude('|11>').closeTo(complex(1))).to.be.true; 830 | }); 831 | }); 832 | 833 | describe('#subtract', function() { 834 | it('should subtract two quantum states together', function() { 835 | // NOTE: You will need to normalize afterwards to get a sensible answer! 836 | const q = Q('|01>').hadamard(0).add(Q('|00>')).subtract(Q('|01>')); 837 | expect(q.toString()).to.equal('(1.7071)|00> + (-1.7071)|01>'); 838 | }); 839 | }); 840 | 841 | describe('#normalize', function() { 842 | it('should normalize the amplitudes', function() { 843 | let q = Q('|0>') 844 | .multiply(complex(3, 4)) 845 | .add(Q('|1>').multiply(complex(0, 1))); 846 | q = q.normalize(); 847 | const factor = 1 / Math.sqrt(25 + 1); 848 | expect(q.amplitude('|1>').closeTo(complex(0, factor))).to.be.true; 849 | expect(q.amplitude('|0>').closeTo(complex(3, 4).multiply(complex(factor)))).to.be.true; 850 | }); 851 | }); 852 | }); 853 | --------------------------------------------------------------------------------