From 40ae95945e818f667209353e724ab9c2f92fd6fd Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 5 Aug 2021 21:53:46 +0000 Subject: [PATCH 01/22] Create quantum-js-cli package --- packages/quantum-js-cli/index.js | 3 + packages/quantum-js-cli/package.json | 15 ++ packages/quantum-js-cli/runner.js | 222 +++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 packages/quantum-js-cli/index.js create mode 100644 packages/quantum-js-cli/package.json create mode 100644 packages/quantum-js-cli/runner.js diff --git a/packages/quantum-js-cli/index.js b/packages/quantum-js-cli/index.js new file mode 100644 index 0000000..a28abe7 --- /dev/null +++ b/packages/quantum-js-cli/index.js @@ -0,0 +1,3 @@ +const {run} = require('./runner'); + +run(); diff --git a/packages/quantum-js-cli/package.json b/packages/quantum-js-cli/package.json new file mode 100644 index 0000000..f30a8c7 --- /dev/null +++ b/packages/quantum-js-cli/package.json @@ -0,0 +1,15 @@ +{ + "name": "quantum-js-cli", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "jest" + }, + "dependencies": { + "prompt-sync": "^4.2.0", + "readline-sync": "^1.4.10" + }, + "author": "", + "license": "ISC" +} diff --git a/packages/quantum-js-cli/runner.js b/packages/quantum-js-cli/runner.js new file mode 100644 index 0000000..4d7f16e --- /dev/null +++ b/packages/quantum-js-cli/runner.js @@ -0,0 +1,222 @@ +const {Q, Circuit, Gate, logger} = require('quantum-js-util'); +const prompt = require('prompt-sync')({sigint: true}); +var readlineSync = require('readline-sync'); +const mark = "> "; + + +function prepareCircuit() { + let selection = NaN; + console.clear(); + let circuit; + while(!selection) { + console.log("Please select a method to begin circuit creation: "); + console.log("1. From Scratch\n" + + "2. From Text Diagram\n" + /*+ "3. From Table Transposed\n"*/); + selection = +prompt(mark); + switch(selection) { + case 1: + let num_registers = NaN; + while(!num_registers) { + console.log("Enter the number of qubits you would like to start out with.\n"); + num_registers = +prompt(mark); + } + circuit = Q(num_registers, 8); + break; + case 2: + circuit = prepareCircuitFromTable(); + break; + default: + selection = NaN + } + } + if(!(circuit instanceof Circuit)) { + logger.error("Failed to create circuit"); + process.exit(); + } + console.log(circuit.toDiagram()); + return circuit; +} + +function prepareCircuitFromTable() { + let resultingCircuit; + let tableString; + let lines = []; + console.log('Input (or paste) your table below and press [Enter] key twice to submit.'); + readlineSync.promptLoop(line => { + lines.push(line); + return !line; + }, {prompt: ''}); + tableString = lines.join('\n'); + try { + resultingCircuit = Q(tableString); + } + catch(e) { + return logger.error("Failed to create circuit from table."); + } + return resultingCircuit; +} + + + +function printMenu() { + let menu = +`-h, help Print Q.js command line options (currently set) + toDiagram Print the current circuit as a text diagram + toAmazonBraket Export the current circuit as Python code using the Amazon Braket SDK. + toLaTeX Print the current circuit as a LaTeX diagram + toText Print as a table using only common characters (can be used to import later). + report Evaluate and current circuit's probability report + clear Clear the console + newCircuit Discard the current circuit and create a new circuit. +q, quit Exit the command line + `; + console.log(menu); + return menu; +} + +function evaluateOperation(input, circuit) { + let functionName = (/[^\s,\[\]()]+/g).exec(input)[0]; + let gate = Gate.findBySymbol(functionName); + if(!gate) gate = Gate.findByNameCss(functionName) + //checks that the function call is gate set$ operation and not another circuit operation. + //Syntax: GateSymbol(momentIndex, [registerIndex0, registerIndex1,...]) + //Regex explanation: looks for the first INTEGER of the from "(digit0digit1digit2..." and removes the "(" at the beginning. + let momentIndex = +(/\(\s*\d+/).exec(input)[0].substring(1); + if(momentIndex === undefined) { + return logger.error("Invalid gate set operation syntax"); + } + // + let registerIndices; + let re = /\[(\s*\d+\s*,{0,1}\s*)+\]/g; + try { + registerIndices = (re) + .exec(input)[0] + .slice(1, -1) + .split(',') + .map(index => +index); + } + catch(e) { + return logger.error("Invalid gate set operation syntax"); + } + let newParameters = {}; + input = input.substring(re.lastIndex); + re = /\d+\.{0,1}\d*/g + let input_params = []; + while(value = re.exec(input)) { + input_params.push(+value[0]); + } + input_params.reverse(); + if(gate.has_parameters) { + if(input_params.length > Object.keys(gate.parameters).length) return logger.error("b Invalid gate set operation syntax"); + Object.keys(gate.parameters).forEach(key => { + newParameters[key] = input_params.pop(); + if(!newParameters[key]) { + newParameters[key] = gate.parameters[key]; + } + }); + } + else if(input_params.length !== 0) return logger.error("c Invalid gate set operation syntax"); + return circuit[functionName](momentIndex, registerIndices, newParameters); +} + + +function removeOperation(input, circuit) { + let momentIndex = +(/\(\s*\d+/).exec(input)[0].substring(1); + if(momentIndex === undefined) { + return logger.error("Invalid gate set operation syntax"); + } + // + let registerIndices; + let re = /\[(\s*\d+\s*,{0,1}\s*)+\]/g; + try { + registerIndices = (re) + .exec(input)[0] + .slice(1, -1) + .split(',') + .map(index => +index); + } + catch(e) { + return logger.error("Invalid gate set operation syntax"); + } + if(input.substring(re.lastIndex).trim() != ")") { + return logger.error("Invalid gate set operation syntax"); + } + let operationToRemove = circuit.get(momentIndex, registerIndices[0]); + if(!operationToRemove) { + return logger.log("No operation to remove"); + } + if(registerIndices.every(index => { + return operationToRemove.registerIndices.includes(index); + })) return circuit.clear$(momentIndex, operationToRemove.registerIndices); +} + +function evaluateInput(input, circuit) { + switch(input) { + case "-h": + case "help": + printMenu(); + break; + case "toDiagram": + console.log(circuit.toDiagram()); + break; + case "toAmazonBraket": + console.log(circuit.toAmazonBraket()); + break; + case "toLaTeX": + console.log(circuit.toLatex()); + break; + case "report": + circuit.evaluate$(); + console.log(circuit.report$()); + break; + case "clear": + console.clear(); + break; + case "toText": + console.log(circuit.toText(true)); + break; + case "newCircuit": + let response = prompt("Creating a new circuit will discard the current circuit. Enter yes to continue: ").toLowerCase(); + if(response !== "yes") console.log("Did not create new circuit."); + else circuit = prepareCircuit(); + break; + default: + let circuitBackup = circuit.clone(); + let re = /((\w+-*)+\(\s*\d+\s*,\s*\[(\s*\d+\s*,{0,1}\s*)+\]\s*(,\s*\d+\.{0,1}\d*\s*)*\))/g; + while(functionCall = re.exec(input)) { + functionCall = functionCall[0]; + let functionName = (/[^\s,\[\]()]+/g).exec(functionCall)[0]; + //checks that the function call is gate set$ operation and not another circuit operation. + //Syntax: GateSymbol(momentIndex, [registerIndex0, registerIndex1,...]) + if(circuit[functionName] instanceof Function && (Gate.findBySymbol(functionName) instanceof Gate || Gate.findByNameCss(functionName) instanceof Gate)) { + if(evaluateOperation(functionCall, circuit) === "(error)") { + circuit = circuitBackup; + break; + } + } + //Syntax: clear(momentIndex, registerIndex) + //If the registerIndex is the index of a multiqubit operation, we clear all indices associated with the operation under registerIndex + else if(functionName == "remove") { + if(removeOperation(functionCall, circuit) === "(error)") { + circuit = circuitBackup; + break; + } + } + } + } + return circuit; +} + + +function run() { + let circuit = prepareCircuit(); + let input = prompt(mark); + while(input !== "quit" && input !== "q") { + evaluateInput(input, circuit); + input = prompt(mark) + } + +} + +module.exports = {run, evaluateInput, removeOperation, evaluateOperation, printMenu}; \ No newline at end of file From e66857c1b1a7fbf6b692aa901e0ea93139f78da5 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 5 Aug 2021 21:54:45 +0000 Subject: [PATCH 02/22] Create tests for quantum-js-cli runner.js --- .../quantum-js-cli/__test__/runner.test.js | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 packages/quantum-js-cli/__test__/runner.test.js diff --git a/packages/quantum-js-cli/__test__/runner.test.js b/packages/quantum-js-cli/__test__/runner.test.js new file mode 100644 index 0000000..1be8866 --- /dev/null +++ b/packages/quantum-js-cli/__test__/runner.test.js @@ -0,0 +1,228 @@ +const runner = require('../runner'); +const quantumjs = require('quantum-js-util'); + + +describe("Checking evaluateInput calls the correct functions and/or logs the correct information given a certain input", () => { + //create empty circuit. + let circuit = quantumjs.Q(); + console.log = jest.fn(); + test("Testing evaluateInput() with input 'help' and an empty circuit.", () => { + let expectedText = runner.printMenu(); + runner.evaluateInput("help", circuit); + expect(console.log).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalledWith(expectedText); + }) + test("Testing evaluateInput() with input '-h' and an empty circuit.", () => { + let expectedText = runner.printMenu(); + runner.evaluateInput("-h", circuit); + expect(console.log).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalledWith(expectedText); + }) + test("Testing evaluateInput() with input 'toAmazonBraket' and an empty circuit", () => { + runner.evaluateInput("toAmazonBraket", circuit); + expectedText = circuit.toAmazonBraket(); + expect(console.log).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalledWith(expectedText); + }) + test("Testing evaluateInput() with input 'toDiagram' and an empty circuit", () => { + runner.evaluateInput("toDiagram", circuit); + expectedText = circuit.toDiagram(); + expect(console.log).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalledWith(expectedText); + }) + test("Testing evaluateInput() with input 'toLaTeX' and an empty circuit", () => { + runner.evaluateInput("toLaTeX", circuit); + expectedText = circuit.toLatex(); + expect(console.log).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalledWith(expectedText); + }) + test("Testing evaluateInput() with input 'report' and an empty circuit", () => { + runner.evaluateInput("report", circuit); + expectedText = circuit.report$(); + expect(console.log).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalledWith(expectedText); + }) + test("Testing evaluateInput() with input 'toText' and an empty circuit", () => { + runner.evaluateInput("toText", circuit); + expectedText = circuit.toAmazonBraket(); + expect(console.log).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalledWith(expectedText); + }) + test("Testing evaluateInput() with input 'clear' and an empty circuit", () => { + console.clear = jest.fn(); + runner.evaluateInput("clear", circuit); + expectedText = circuit.toAmazonBraket(); + expect(console.clear).toHaveBeenCalled(); + }) +}) + +//Gate operation syntax is of the regex form /(\w+\(\s*\d+\s*,\s*\[(\s*\d+\s*,{0,1}\s*)+\](,\s*\d+\.{0,1}\d+\s*)*\))/g +//or more easily described: +//'gate-symbol(moment-index, [registerIndex0, registerIndex1,...], parameter0, parameter1,...)' with white space allowed liberally +describe("Testing various forms of gate-operation call expression and see that evaluateOperation is called", ()=> { + //create empty circuit. + test("Check that evaluateOperation is called once for input 'h(1, [1])'", () => { + let circuit = quantumjs.Q(); + runner.evaluateInput("h(1, [1])", circuit); + expect(circuit.get(1, 1).gate.symbol).toBe('H'); + }) + //messing with the white space + test("Check that evaluateOperation is called once for input 'h( 1 , [ 1 ])'", () => { + let circuit = quantumjs.Q(); + runner.evaluateInput("h( 1 , [ 1 ])", circuit) + expect(circuit.get(1, 1).gate.symbol).toBe('H'); + }) + //while this doesn't update the circuit, it should still call evaluateOperation which detects the flaw later. + test("Check that no operation is added for 'h( 1 , [ 1 ], 3)' as the Hadamard operation takes no parameters", () => { + let circuit = quantumjs.Q(); + runner.evaluateInput("h( 1 , [ 1 ], 3)", circuit) + expect(circuit.get(1, 1)).toBeUndefined(); + }) + test("Check that evaluateOperation is called for input 'x(1, [1, 2])'", () => { + let circuit = quantumjs.Q(); + runner.evaluateInput("x(1, [1, 2])", circuit) + expect(circuit.get(1, 1).gate.symbol).toBe("X"); + expect(circuit.get(1, 2).gate.symbol).toBe("X"); + }) + //messing with the white space + test("Check that evaluateOperation is called for input 'x( 1, [ 1, 2 ] )'", () => { + let circuit = quantumjs.Q(); + runner.evaluateInput("x(1, [1, 2])", circuit) + expect(circuit.get(1, 1).gate.symbol).toBe("X"); + expect(circuit.get(1, 2).gate.symbol).toBe("X"); + }) + describe("Check that gateSymbol(...,[...]) is valid for all gate constants in the Gate module using their nameCss value", ()=> { + Object.entries(quantumjs.Gate.constants).forEach(function(entry) { + let gate = entry[1]; + let input = gate.nameCss + (gate.is_multi_qubit ? "(1, [1, 2])" : "(1, [1])"); + console.log(input); + test("Checking that evaluate operation is called once for the input '" + input + "'", () => { + let circuit = quantumjs.Q(); + runner.evaluateInput(input, circuit); + expect(circuit.get(1, 1).gate.nameCss).toBe(gate.nameCss); + if(gate.is_multi_qubit) { + expect(circuit.get(1, 2).gate.nameCss).toBe(gate.nameCss); + } + }) + }) + }) + + +}) + + +describe("Testing removal operations (of the same regex form as above) and that removeOperation() is called", () => { + test("Check that removeOperation is called once for input remove(1, [1])", () => { + let circuit = quantumjs.Q(); + //Set a hadamard operation on the circuit. + circuit.set$('H', 1, [1]); + runner.evaluateInput('remove(1, [1])', circuit); + expect(circuit.get(1, 1)).toBeUndefined(); + }) + //messing with the whitepsace + test("Check that removeOperation is called once for input remove( 1 , [ 1 ] )", () => { + let circuit = quantumjs.Q(); + //Set a hadamard operation on the circuit. + circuit.set$('H', 1, [1]); + runner.evaluateInput('remove( 1 , [ 1 ] )', circuit); + expect(circuit.get(1, 1)).toBeUndefined(); + }) + test("Check that removeOperation removes all sibling indices of the operation x(1, [1, 2]) when remove(1, [1]) is called", ()=> { + let circuit = quantumjs.Q(); + circuit.set$('X', 1, [1, 2]); + runner.evaluateInput('remove(1, [1])', circuit); + expect(circuit.get(1, 1)).toBeUndefined(); + expect(circuit.get(1, 2)).toBeUndefined(); + }) + test("Check that the removeOperation does NOT remove any operation given a set of indices that are not siblings", ()=> { + let circuit = quantumjs.Q(); + //Set a hadamard operation on the circuit. + circuit.set$('H', 1, [1]); + runner.evaluateInput('remove(1, [1, 2])', circuit); + expect(circuit.get(1, 1)).toBeDefined(); + expect(circuit.get(1, 1).gate.symbol).toBe('H'); + }) +}) + +describe("Check that parameters parameterized gates can be input and set properly", ()=> { + test("Check that the input 'u(1, [1], 3, 2, 5)' results in the creation of a unitary gate with phi=3,theta=2,lambda=5", ()=>{ + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("u(1, [1], 3, 2, 5)", circuit); + let result = circuit.get(1, 1).gate; + expect(result.symbol).toBe('U'); + expect(result.parameters['phi']).toBe(3); + expect(result.parameters['theta']).toBe(2); + expect(result.parameters['lambda']).toBe(5); + }) + //messing with whitespace + test("Check that the input 'u(1, [1], 3 , 2 , 5 )' results in the creation of a unitary gate with phi=3,theta=2,lambda=5", ()=>{ + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("u(1, [1], 3 , 2 , 5 )", circuit); + let result = circuit.get(1, 1).gate; + expect(result.symbol).toBe('U'); + expect(result.parameters['phi']).toBe(3); + expect(result.parameters['theta']).toBe(2); + expect(result.parameters['lambda']).toBe(5); + }) + //checking regex accepts decimal values + test("Check that the input 'u(1, [1], 3.93, 2.24, 5.12)' results in the creation of a unitary gate with phi=3,theta=2,lambda=5", ()=>{ + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("u(1, [1], 3.93, 2.24, 5.12)", circuit); + let result = circuit.get(1, 1).gate; + expect(result.symbol).toBe('U'); + expect(result.parameters['phi']).toBe(3.93); + expect(result.parameters['theta']).toBe(2.24); + expect(result.parameters['lambda']).toBe(5.12); + }) + //check that too many parameters results in a failed set operation + test("Check that the input 'u(1, [1], 1, 2, 3, 4)' does NOT result creation of a unitary gate", ()=>{ + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("u(1, [1], 1, 2, 3, 4)", circuit); + expect(circuit.get(1, [1])).toBeUndefined(); + }) + test("Check that the input 'u(1, [1], 1, 2)' does result creation of a unitary gate with phi=1,theta=2,lambda=[default]", ()=>{ + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("u(1, [1], 1, 2)", circuit); + let result = circuit.get(1, 1).gate; + let defaultUnitary = Gate.findBySymbol('U'); + expect(result.symbol).toBe('U'); + expect(result.parameters['phi']).toBe(1); + expect(result.parameters['theta']).toBe(2); + expect(result.parameters['lambda']).toBe(defaultUnitary.parameters['lambda']); + }) +}) + + +describe("Test various combinations of set and remove operations chained together", ()=> { + test("Check that the input 'h(1, [1]).x(2, [1])' results in valid gate set operations", ()=> { + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("h(1, [1]).x(2, [1])", circuit); + expect(circuit.get(1, 1).gate.symbol).toBe('H'); + expect(circuit.get(2, 1).gate.symbol).toBe('X'); + }) + //Messing with the whitespace + test("Check that the input 'h(1, [ 1 ]).x( 2 , [ 1 ] )' results in valid gate set operations", ()=> { + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("h(1, [ 1 ]).x( 2 , [ 1 ] )", circuit); + expect(circuit.get(1, 1).gate.symbol).toBe('H'); + expect(circuit.get(2, 1).gate.symbol).toBe('X'); + }) + test("Check that the input 'h(1, [1]).x(2, [1, 2]).p(1, [3], 3.14159)' results in valid gate set operations", ()=> { + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("h(1, [1]).x(2, [1, 2]).p(1, [3], 3.14159)", circuit); + expect(circuit.get(1, 1).gate.symbol).toBe('H'); + expect(circuit.get(2, 1).gate.symbol).toBe('X'); + expect(circuit.get(2, 1).registerIndices).toEqual([1, 2]); + expect(circuit.get(1, 3).gate.symbol).toBe('P'); + expect(circuit.get(1, 3).gate.parameters['phi']).toBe(3.14159); + }) + test("Check that the input 'h(1, [1]).x(2, [1, 2]).remove(1, [1]).p(1, [3], 3.14159)' results in valid gate set operations", ()=> { + let circuit = quantumjs.Q(4, 4); + runner.evaluateInput("h(1, [1]).x(2, [1, 2]).remove(1, [1]).p(1, [3], 3.14159)", circuit); + expect(circuit.get(1, 1)).toBeUndefined(); + expect(circuit.get(2, 1).gate.symbol).toBe('X'); + expect(circuit.get(2, 1).registerIndices).toEqual([1, 2]); + expect(circuit.get(1, 3).gate.symbol).toBe('P'); + expect(circuit.get(1, 3).gate.parameters['phi']).toBe(3.14159); + }) +}) \ No newline at end of file From 9fa52c3d30737d7106a80606be228901fd6dcf56 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 17:44:27 +0000 Subject: [PATCH 03/22] Update HTML files to reference bundled Qjs --- Q-Circuit.html | 13 +++--------- Q-ComplexNumber.html | 27 ++++++++++--------------- Q-Gate.html | 19 ++++++------------ Q-Matrix.html | 47 +++++++++++++++++++------------------------- Q-Qubit.html | 22 +++++++-------------- Q.html | 13 +++--------- contributing.html | 13 +++--------- index.html | 23 ++++++++-------------- playground.html | 23 ++++++++-------------- resources.html | 13 +++--------- tutorials.html | 13 +++--------- 11 files changed, 74 insertions(+), 152 deletions(-) diff --git a/Q-Circuit.html b/Q-Circuit.html index a64b2b2..c5c5e77 100644 --- a/Q-Circuit.html +++ b/Q-Circuit.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + diff --git a/Q-ComplexNumber.html b/Q-ComplexNumber.html index 011efaf..144e3a1 100644 --- a/Q-ComplexNumber.html +++ b/Q-ComplexNumber.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + @@ -1096,17 +1089,17 @@

Maths (destructive)

-// console.log( '\n\nQ.ComplexNumber\n\n', Q.ComplexNumber.help(), '\n\n' ) +// console.log( '\n\nComplexNumber\n\n', ComplexNumber.help(), '\n\n' ) var -cat = new Q.ComplexNumber( 1, 2 ), -dog = new Q.ComplexNumber( 3, -4 ), -i = new Q.ComplexNumber( 0, 1 ) +cat = new ComplexNumber( 1, 2 ), +dog = new ComplexNumber( 3, -4 ), +i = new ComplexNumber( 0, 1 ) var -ape = new Q.ComplexNumber(), -bee = new Q.ComplexNumber( 1 ), -elk = new Q.ComplexNumber( 1, 2 ), +ape = new ComplexNumber(), +bee = new ComplexNumber( 1 ), +elk = new ComplexNumber( 1, 2 ), fox = elk.clone()// We’re avoiding a warning here: new Q.ComplexNumber( cat ), diff --git a/Q-Gate.html b/Q-Gate.html index 172620b..0e799c8 100644 --- a/Q-Gate.html +++ b/Q-Gate.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + @@ -1265,18 +1258,18 @@

Prototype properties

-var gup = new Q.Gate({ +var gup = new Gate({ symbol: 'G', name: 'Gup', nameCss: 'gup', - matrix: Q.Gate.PAULI_X.matrix + matrix: Gate.PAULI_X.matrix }) -var fox = Q.Gate.PHASE.clone({ symbol: 'F', phi: Math.PI }) +var fox = Gate.PHASE.clone({ symbol: 'F', phi: Math.PI }) diff --git a/Q-Matrix.html b/Q-Matrix.html index 5d80b14..946d9ce 100644 --- a/Q-Matrix.html +++ b/Q-Matrix.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + @@ -1250,7 +1243,7 @@

Maths operations (destructive)

// First, what do the docs have to say? -// console.log( '\n\nQ.Matrix\n\n', Q.Matrix.help(), '\n\n' ) +// console.log( '\n\nMatrix\n\n', Matrix.help(), '\n\n' ) // We’re going to use `var` here instead of `let` or `const` @@ -1258,7 +1251,7 @@

Maths operations (destructive)

-var a = new Q.Matrix( +var a = new Matrix( [ 1, 2, 3 ], [ 4, 5, 6 ] @@ -1283,22 +1276,22 @@

Maths operations (destructive)

/* -console.log( '\n\nQ.Matrix\n', Q.extractDocumentation( Q.Matrix ), '\n\n' ) -console.log( '\n\nQ.Matrix.prototype.multiply\n', Q.extractDocumentation( Q.Matrix.prototype.multiply ), '\n\n' ) +console.log( '\n\nMatrix\n', Q.extractDocumentation( Matrix ), '\n\n' ) +console.log( '\n\nMatrix.prototype.multiply\n', Q.extractDocumentation( Matrix.prototype.multiply ), '\n\n' ) -console.log( 'Q.Matrix.IDENTITY_2X2', Q.Matrix.IDENTITY_2X2 ) -console.log( 'Q.Matrix.IDENTITY_3X3', Q.Matrix.IDENTITY_3X3 ) -console.log( 'Q.Matrix.IDENTITY_4X4', Q.Matrix.IDENTITY_4X4 ) +console.log( 'Matrix.IDENTITY_2X2', Matrix.IDENTITY_2X2 ) +console.log( 'Matrix.IDENTITY_3X3', Matrix.IDENTITY_3X3 ) +console.log( 'Matrix.IDENTITY_4X4', Matrix.IDENTITY_4X4 ) -console.log( 'Q.Matrix.CNOT', Q.Matrix.CNOT ) -console.log( '\nQ.Matrix.CNOT.toHtml()', Q.Matrix.CNOT.toHTML(), '\n\n' ) -console.log( '\nQ.Matrix.TEST_MAP_9X9.toCSV()', Q.Matrix.TEST_MAP_9X9.toCSV(), '\n\n' ) -console.log( '\nQ.Matrix.TEST_MAP_9X9.toTsv()', Q.Matrix.TEST_MAP_9X9.toTsv(), '\n\n' ) +console.log( 'Matrix.CNOT', Matrix.CNOT ) +console.log( '\nMatrix.CNOT.toHtml()', Matrix.CNOT.toHTML(), '\n\n' ) +console.log( '\nMatrix.TEST_MAP_9X9.toCSV()', Matrix.TEST_MAP_9X9.toCSV(), '\n\n' ) +console.log( '\nMatrix.TEST_MAP_9X9.toTsv()', Matrix.TEST_MAP_9X9.toTsv(), '\n\n' ) -console.log( 'How many matrices have we created?', Q.Matrix.index ) +console.log( 'How many matrices have we created?', Matrix.index ) */ @@ -1357,8 +1350,8 @@

Maths operations (destructive)

[ 3, 3, 3 ]) -console.log( 'Q.Matrix.IDENTITY_2X2.multiply( e ).toTsv()', Q.Matrix.IDENTITY_2X2.multiply( e )) -console.log( 'Q.Matrix.IDENTITY_3X3.multiply( c ).toTsv()', Q.Matrix.IDENTITY_3X3.multiply( c )) +console.log( 'Matrix.IDENTITY_2X2.multiply( e ).toTsv()', Matrix.IDENTITY_2X2.multiply( e )) +console.log( 'Matrix.IDENTITY_3X3.multiply( c ).toTsv()', Matrix.IDENTITY_3X3.multiply( c )) */ @@ -1383,14 +1376,14 @@

Maths operations (destructive)

// Import and export formats. -var csv = Q.Matrix.fromCsv(` +var csv = Matrix.fromCsv(` 1, 2, 3 4, 5, 6 7, 8, 9`) // console.log( 'Matrix from CSV', csv.toTsv(), '\n\n' ) -var tsv = Q.Matrix.fromTsv(`1 2 3 +var tsv = Matrix.fromTsv(`1 2 3 4 5 6 7 8 9`) // console.log( 'Matrix from TSV', tsv.toTsv(), '\n\n' ) @@ -1415,7 +1408,7 @@

Maths operations (destructive)

` -// console.log( 'Matrix from HTML', Q.Matrix.fromHtml( html ).toTsv(), '\n\n' ) +// console.log( 'Matrix from HTML', Matrix.fromHtml( html ).toTsv(), '\n\n' ) diff --git a/Q-Qubit.html b/Q-Qubit.html index 01f6e18..5df02cd 100644 --- a/Q-Qubit.html +++ b/Q-Qubit.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + @@ -61,7 +54,6 @@ - @@ -1885,7 +1877,7 @@

Destructive methods

// Examples created in the docs: -var fox = new Q.Qubit( 1, 0 ) +var fox = new Qubit( 1, 0 ) @@ -1958,7 +1950,7 @@

Destructive methods

camera.add( light ) scene.add( new THREE.AmbientLight( 0xFFFFFF, 0.7 )) -const blochSphere = new Q.BlochSphere( function(){ +const blochSphere = new BlochSphere( function(){ document .getElementById( 'bloch-theta' ) @@ -2033,7 +2025,7 @@

Destructive methods

'HDLARV'.split( '' ).forEach( function( symbol ){ const - qubit = Q.Qubit.findBySymbol( symbol ), + qubit = Qubit.findBySymbol( symbol ), qubitElement = document.createElement( 'div' ), qubitGutsElement = document.createElement( 'div' ) @@ -2069,7 +2061,7 @@

Destructive methods

blochSphere.group.rotation.x = Math.PI * 0.3 blochSphere.group.rotation.y = Math.PI / -4 -selectBlochQubit( Q.Qubit.HORIZONTAL ) +selectBlochQubit( Qubit.HORIZONTAL ) render() diff --git a/Q.html b/Q.html index 3dc569f..b00ba31 100644 --- a/Q.html +++ b/Q.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + diff --git a/contributing.html b/contributing.html index c8a3316..a516c2c 100644 --- a/contributing.html +++ b/contributing.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + diff --git a/index.html b/index.html index 1aa2898..c513938 100644 --- a/index.html +++ b/index.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + @@ -585,7 +578,7 @@

Import and export

// console.log( 'state width', state.getWidth(), 'state height', state.getHeight() ) // console.log( 'state', state.toTsv() ) }) -window.addEventListener( 'Q.Circuit.evaluate completed', function( event ){ +window.addEventListener( 'Circuit.evaluate completed', function( event ){ const circuit = event.detail.circuit console.log( @@ -624,7 +617,7 @@

Import and export

// Demonstrate Q’s random naming feature. -const circuitNameRandom = Q.getRandomName$() +const circuitNameRandom = misc.getRandomName$() Array .from( document.querySelectorAll( '.circuit-name-random' )) @@ -639,8 +632,8 @@

Import and export

// so wee can begin playing with them straight away. var -cat = new Q.ComplexNumber( 1, 2 ), -dog = new Q.ComplexNumber( 3, -4 ) +cat = new ComplexNumber( 1, 2 ), +dog = new ComplexNumber( 3, -4 ) @@ -661,7 +654,7 @@

Import and export

.from( document.querySelectorAll( '.Q-circuit-palette' )) .forEach( function( el ){ - Q.Circuit.Editor.createPalette( el ) + Editor.createPalette( el ) }) diff --git a/playground.html b/playground.html index 10543ca..47aeead 100644 --- a/playground.html +++ b/playground.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + @@ -246,7 +239,7 @@

Whiplash

.from( document.querySelectorAll( '.Q-circuit-palette' )) .forEach( function( el ){ - Q.Circuit.Editor.createPalette( el ) + Editor.createPalette( el ) }) @@ -291,7 +284,7 @@

Whiplash

document.getElementById( 'playground-apply-button' ).setAttribute( 'disabled', 'disabled' ) const circuit = Q( text ) - if( circuit instanceof Q.Circuit ){//+++++ This validation appears broken! + if( circuit instanceof Circuit ){//+++++ This validation appears broken! circuit.name = 'playground' const domEl = document.getElementById( 'playground' ) @@ -371,7 +364,7 @@

Whiplash

// EVALUATION. -window.addEventListener( 'Q.Circuit.evaluate began', function( event ){ +window.addEventListener( 'Circuit.evaluate began', function( event ){ console.log( @@ -379,7 +372,7 @@

Whiplash

event.detail.circuit.toDiagram() +'\n\n' ) }) -window.addEventListener( 'Q.Circuit.evaluate progressed', function( event ){ +window.addEventListener( 'Circuit.evaluate progressed', function( event ){ const length = 20, @@ -408,7 +401,7 @@

Whiplash

// console.log( 'state width', state.getWidth(), 'state height', state.getHeight() ) // console.log( 'state', state.toTsv() ) }) -window.addEventListener( 'Q.Circuit.evaluate completed', function( event ){ +window.addEventListener( 'Circuit.evaluate completed', function( event ){ console.log( diff --git a/resources.html b/resources.html index 21da767..bfb88dc 100644 --- a/resources.html +++ b/resources.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + diff --git a/tutorials.html b/tutorials.html index 9d6a2e5..7218945 100644 --- a/tutorials.html +++ b/tutorials.html @@ -40,20 +40,13 @@ - - + + - - - - - - - - + From 788727e1dfdd427a080a249a8a68c8c76bbd0d77 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 19:08:14 +0000 Subject: [PATCH 04/22] Set up build script for bundling css files --- build/q-old.css | 1376 --------- build/q-old.js | 7874 ----------------------------------------------- build/q.css | 78 +- package.json | 12 +- 4 files changed, 70 insertions(+), 9270 deletions(-) delete mode 100644 build/q-old.css delete mode 100644 build/q-old.js diff --git a/build/q-old.css b/build/q-old.css deleted file mode 100644 index e4a6620..0000000 --- a/build/q-old.css +++ /dev/null @@ -1,1376 +0,0 @@ -/* - - Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - -*/ -@charset "utf-8"; - - - - -/* - - This file is in the process of being separated - in to “essential global Q.js styles” which will - remain here in Q.css, and “documentation-specific” - styles which will be removed from here and placed - within the /other/documentation.css file instead. - - The goal is for a developer to be able to place - this Q.css file into their own web app with little - to no interference with their app. All variables - and styles here will ultimately be prefaced with - a capital Q, eg. --Q-color-base, or .Q-text-input - and so on. - - -*/ - - - - -svg, :root { - - - - /**************/ - /* */ - /* Colors */ - /* */ - /**************/ - - - /* Base color (blue) */ - - --Q-color-base-hue: 210; - --Q-color-base-saturation: 85%; - --Q-color-base-lightness: 40%; - - - /* Red */ - - --Q-color-red-hue: calc( var( --Q-color-base-hue ) + 180 - 30 ); - --Q-color-red-saturation: 85%; - --Q-color-red-lightness: 45%; - --Q-color-red: hsl( - - var( --Q-color-red-hue ), - var( --Q-color-red-saturation ), - var( --Q-color-red-lightness ) - ); - - - /* Orange */ - - --Q-color-orange-hue: calc( var( --Q-color-base-hue ) + 180 - 15 ); - --Q-color-orange-saturation: 85%; - --Q-color-orange-lightness: 50%; - --Q-color-orange: hsl( - - var( --Q-color-orange-hue ), - var( --Q-color-orange-saturation ), - var( --Q-color-orange-lightness ) - ); - - - /* Yellow */ - - --Q-color-yellow-hue: calc( var( --Q-color-base-hue ) + 180 + 15 ); - --Q-color-yellow-saturation: 90%; - --Q-color-yellow-lightness: 50%; - --Q-color-yellow: hsl( - - var( --Q-color-yellow-hue ), - var( --Q-color-yellow-saturation ), - var( --Q-color-yellow-lightness ) - ); - - - /* Green */ - - --Q-color-green-hue: calc( var( --Q-color-base-hue ) + 180 + 60 ); - --Q-color-green-saturation: 80%; - --Q-color-green-lightness: 35%; - --Q-color-green: hsl( - - var( --Q-color-green-hue ), - var( --Q-color-green-saturation ), - var( --Q-color-green-lightness ) - ); - - - /* Blue */ - - --Q-color-blue-hue: var( --Q-color-base-hue ); - --Q-color-blue-saturation: var( --Q-color-base-saturation ); - --Q-color-blue-lightness: var( --Q-color-base-lightness ); - --Q-color-blue: hsl( - - var( --Q-color-blue-hue ), - var( --Q-color-blue-saturation ), - var( --Q-color-blue-lightness ) - ); - - - /* Grayscale */ - - --Q-color-white: #FFFFFF; - --Q-color-chalk: #F9F9F9; - --Q-color-newsprint: #F3F3F3; - --Q-color-titanium: #CCCCCC; - --Q-color-slate: #777777; - --Q-color-charcoal: #333333; - --Q-color-black: #000000; - - - /* Background */ - - --Q-color-background-hue: var( --Q-color-base-hue ); - --Q-color-background-saturation: 15%; - --Q-color-background-lightness: 98%; - --Q-color-background: hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - var( --Q-color-background-lightness ) - ); - /*--Q-color-background: white;*/ - - - /* Misc */ - - --Q-text-color: hsl( - - var( --Q-color-base-hue ), - 5%, - 35% - ); - --Q-text-code-comment-color: rgba( 0, 0, 0, 0.4 ); - --Q-text-code-output-color: rgba( 0, 0, 0, 1 ); - - --Q-selection-color: var( --Q-color-black ); - --Q-selection-background-color: var( --Q-color-yellow ); - - --Q-hyperlink-internal-color: var( --Q-color-blue ); - --Q-hyperlink-external-color: var( --Q-text-color ); - - --Q-background-callout-color: var( --Q-color-white ); - - --Q-svg-fill-color: var( --Q-text-color ); - - - - - /* Fonts */ - - --Q-font-family-serif: 'Source Serif Pro', 'Roboto Slab', 'Georgia', serif; - --Q-font-family-sans: 'SF Pro Text', system-ui, -apple-system, 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; - --Q-font-family-mono: 'Roboto Mono', 'Source Code Pro', 'Menlo', 'Courier New', monospace; - --Q-font-family-symbols: 'Georgia', serif; -} - - - - - /*******************/ - /* */ - /* Interactive */ - /* */ -/*******************/ - - -.Q-input, -.Q-circuit-text-input { - - margin: 1.5rem 0 0 0 !important; - outline: none !important; - border: none !important; - border-radius: 1.2rem !important; - box-shadow: - 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.15 ) inset, - -0.2rem -0.2rem 0.2rem rgba( 255, 255, 255, 1 ) inset; - - background: linear-gradient( - - 0.375turn, - rgba( 255, 255, 255, 1.0 ), - rgba( 255, 255, 255, 0.2 ) - ) !important; -} -.Q-input, -.Q-circuit-text-input { - - padding: 1.5rem !important; - color: #555 !important; - font-size: 0.9rem !important; - line-height: 1.2rem !important; -} - - - -.Q-circuit-text-input { - - /*min-width: 18rem;*/ - width: 100%; - min-height: 8rem; - /*margin: 1rem 0 2rem 0;*/ - margin: 1rem 0 0 0; - border: 1px solid var( --Q-color-blue ); - border-radius: 0.5rem; - background-color: var( --Q-color-chalk ); - padding: 1rem 0 0 2rem; - color: var( --Q-color-blue ); - font-family: var( --Q-font-family-mono ); - font-size: 1.0rem; - line-height: 1.2rem; - white-space: pre; - word-wrap: normal;/* OMFG, iOS you make me sad. */ -} - - - - - - -.Q-button { - - position: relative; - text-align: right; - margin: 0.5rem 1rem 0 0; - border-radius: 3rem; - box-shadow: - -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ), - 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ); - height: 3rem; - background: - var( --Q-color-blue ) - linear-gradient( - - 0.4turn, - rgba( 255, 255, 255, 0.2 ), - rgba( 0, 0, 0, 0.08 ) - ); - padding: 0.8rem 1.8rem; - color: var( --Q-color-white ); - font-family: var( --Q-font-family-sans ); - font-size: 1rem; - line-height: 1rem; - font-weight: 500; - letter-spacing: 0; - text-shadow: -1px -1px 0 rgba( 0, 0, 0, 0.1 ); - cursor: pointer; -} -.Q-button:hover { - - background: - hsl( - - var( --Q-color-blue-hue ), - var( --Q-color-blue-saturation ), - calc( var( --Q-color-blue-lightness ) * 1.2 ) - ) - linear-gradient( - - 0.4turn, - rgba( 255, 255, 255, 0.2 ), - rgba( 0, 0, 0, 0.08 ) - ); -} -.Q-button:focus { - - margin-top: 0.7rem; - margin-bottom: -0.2rem; - margin-right: 0.9rem; - outline: none; - box-shadow: - -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ) inset, - 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ) inset; - background: - var( --Q-color-blue ) - linear-gradient( - - 0.4turn, - rgba( 0, 0, 0, 0.08 ), - rgba( 255, 255, 255, 0.2 ) - ); -} -.Q-button[disabled] { - - box-shadow: - -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ), - 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ); - background: - var( --Q-color-background ) - linear-gradient( - - 0.45turn, - rgba( 255, 255, 255, 0.1 ), - rgba( 0, 0, 0, 0.05 ) - ); - color: rgba( 0, 0, 0, 0.3 ); - text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 ); - cursor: default; -} - - - - - - -/* - - The below still need to be prefaced with “Q-” - and for the HTML pages to be updated accordingly. - -*/ - - - - - - - /*************/ - /* */ - /* Maths */ - /* */ -/*************/ - - -.maths { - - max-width: 100%; - overflow-x: auto; - font-family: var( --Q-font-family-sans ); - /*letter-spacing: 0.03em;*/ - word-spacing: 0.2em; -} -dd .maths { - - margin-top: 0; - margin-left: 0; -} - - - - -.symbol { - - font-size: 1.1em; - padding: 0 0.1em; - font-family: var( --Q-font-family-symbols ); - font-style: italic; - font-weight: 900; - letter-spacing: 0.05em; -} - - - - -.division { - - display: inline-block; - vertical-align: middle; - margin: 10px; -} -.division td { - - padding: 5px; -} -.dividend { - - border-bottom: 1px solid #CCC; - text-align: center; -} -.divisor { - - text-align: center; -} - - - - -.matrix { - - display: inline-block; - vertical-align: middle; - position: relative; - align: middle; - margin: 1em 0.5em; - padding: 1em; - /*font-family: var( --Q-font-family-mono );*/ - font-weight: 300; - line-height: 1em; - /*text-align: right;*/ - text-align: center; -} -.matrix td { - - padding: 0.25em 0.5em; -} -.matrix-bracket-left, .matrix-bracket-right { - - position: absolute; - top: 0; - width: 0.5em; - height: 100%; - border: 1px solid #CCC; -} -.matrix-bracket-left { - - left: 0; - border-right: none; -} -.matrix-bracket-right { - - right: 0; - border-left: none; -} -/*.matrix.qubit tr:first-child td { - - color: #BBB; -}*/ - - - -.Q-state-vector, -.complex-vector { - - font-family: var( --Q-font-family-mono ); -} -.Q-state-vector.bra::before, -.complex-vector.bra::before { - - content: '⟨'; - color: #BBB; -} -.Q-state-vector.bra::after, -.complex-vector.bra::after { - - content: '|'; - color: #BBB; -} -.Q-state-vector.ket::before, -.complex-vector.ket::before { - - content: '|'; - color: #BBB; -} -.Q-state-vector.ket::after, -.complex-vector.ket::after { - - content: '⟩'; - color: #BBB; -} -.Q-state-vector.bra + .Q-state-vector.ket::before, -.complex-vector.bra + .complex-vector.ket::before { - - content: ''; -} - - - - - - - -/* - - Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - -*/ -@charset "utf-8"; - - - - - - - - -/* - - Z indices: - - Clipboard =100 - Selected op 10 - Operation 0 - Shadow -10 - Background -20 - - - - - - Circuit - - Menu Moments - ╭───────┬───┬───┬───┬───╮ - │ ≡ ↘ │ 1 │ 2 │ 3 │ + │ Add moment - ├───┬───┼───┼───┼───┼───╯ - R │ 0 │|0⟩│ H │ C0│ X │ - - e ├───┼───┼───┼───┼───┤ - g │ 1 │|0⟩│ I │ C1│ X │ - - s ├───┼───┴───┴───┴───┘ - │ + │ - - - - - ╰───╯ - Add - register - - - Circuit Palette - - ╭───────────────────┬───╮ - │ H X Y Z S T π M … │ @ │ - ╰───────────────────┴───╯ - - - Circuit clipboard - - ┌───────────────┐ - ▟│ ┌───┬───────┐ │ - █│ │ H │ X#0.0 │ │ - █│ ├───┼───────┤ │ - █│ │ I │ X#0.1 │ │ - █│ └───┴───────┘ │ - █└───────────────┘ - ███████████████▛ - - - - ◢◣ - ◢■■■■◣ -◢■■■■■■■■◣ -◥■■■■■■■■◤ - ◥■■■■◤ - ◥◤ - - - ◢■■■■■■◤ - ◢◤ ◢◤ -◢■■■■■■◤ - - - ─────────── - ╲ ╱ ╱ ╱ - ╳ ╱ ╱ - ╱ ╲╱ ╱ - ─────── - - - ─────⦢ - ╱ ╱ -⦣───── - - -*/ - - - - - - - -.Q-circuit, -.Q-circuit-palette { - - position: relative; - width: 50%; -} -.Q-circuit-palette { - - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - line-height: 0; -} -.Q-circuit-palette > div { - - display: inline-block; - position: relative; - width: 4rem; - height: 4rem; -} - - -.Q-circuit { - - margin: 1rem 0 2rem 0; - /*border-top: 2px solid hsl( 0, 0%, 50% );*/ -} -.Q-parameters-box, -.Q-circuit-board-foreground { - - line-height: 3.85rem; - width: auto; -} - - - - - - - /***************/ - /* */ - /* Toolbar */ - /* */ -/***************/ - - -.Q-circuit-toolbar { - - display: block; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - margin-bottom: 0.5rem; - - box-sizing: border-box; - display: grid; - grid-auto-columns: 3.6rem; - grid-auto-rows: 3.0rem; - grid-auto-flow: column; - -} -.Q-circuit-button { - - position: relative; - display: inline-block; - /*margin: 0 0.5rem 0.5rem 0;*/ - width: 3.6rem; - height: 3rem; -/* box-shadow: - -0.1rem -0.1rem 0 rgba( 255, 255, 255, 0.8 ), - 0.1rem 0.1rem 0.1rem rgba( 0, 0, 0, 0.35 );*/ - - border-top: 1px solid hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 100% - ); - border-right: 1px solid hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 90% - ); - border-bottom: 1px solid hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 85% - ); - border-left: 1px solid hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 97% - ); - background: var( --Q-color-background ); -/* background: - var( --Q-color-background ) - linear-gradient( - - 0.4turn, - - rgba( 0, 0, 0, 0.02 ), - rgba( 255, 255, 255, 0.1 ) - );*/ - color: hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 30% - ); - text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 ); - /*border-radius: 0.5rem;*/ - /*border-radius: 100%;*/ - line-height: 2.9rem; - text-align: center; - cursor: pointer; - overflow: hidden; - font-weight: 900; -} -.Q-circuit-toolbar .Q-circuit-button:first-child { - - border-top-left-radius: 0.5rem; - border-bottom-left-radius: 0.5rem; -} -.Q-circuit-toolbar .Q-circuit-button:last-child { - - border-top-right-radius: 0.5rem; - border-bottom-right-radius: 0.5rem; -} -.Q-circuit-locked .Q-circuit-button, -.Q-circuit-button[Q-disabled] { - - color: hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 85% - ); - cursor: not-allowed; -} -.Q-circuit-locked .Q-circuit-toggle-lock { - - color: inherit; - cursor: pointer; -} - - - - -.Q-circuit-board-container { - - position: relative; - margin: 0 0 2rem 0; - margin: 0; - width: 100%; - max-height: 60vh; - overflow: scroll; -} -.Q-circuit-board { - - position: relative; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; -} -/*.Q-circuit-palette,*/ -.Q-circuit-board-foreground, -.Q-circuit-board-background, -.Q-circuit-clipboard { - - box-sizing: border-box; - display: grid; - grid-auto-rows: 4rem; - grid-auto-columns: 4rem; - grid-auto-flow: column; -} - -.Q-parameters-box { - - position: absolute; - display: none; - z-index: 100; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: whitesmoke; -} - -/*.Q-circuit-palette,*/ -.Q-circuit-board-foreground, -.Q-circuit-board-background { - - position: relative; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -.Q-circuit-clipboard { - - position: absolute; - z-index: 100; - min-width: 4rem; - min-height: 4rem; - transform: scale( 1.05 ); -} -.Q-circuit-clipboard, .Q-circuit-clipboard > div { - - cursor: grabbing; -} -.Q-circuit-clipboard-danger .Q-circuit-operation { - - background-color: var( --Q-color-yellow ); -} -.Q-circuit-clipboard-destroy { - - animation-name: Q-circuit-clipboard-poof; - animation-fill-mode: forwards; - animation-duration: 0.3s; - animation-iteration-count: 1; -} -@keyframes Q-circuit-clipboard-poof { - - 100% { - - transform: scale( 1.5 ); - opacity: 0; - } -} -.Q-circuit-board-background { - - /* - - Clipboard: 100 - Operation: 0 - Shadow: -10 - Background: -20 - - */ - position: absolute; - z-index: -20; - color: rgba( 0, 0, 0, 0.2 ); -} -.Q-circuit-board-background > div { - -/* transition: - background-color 0.2s, - color 0.2s;*/ -} -.Q-circuit-board-background .Q-circuit-cell-highlighted { - - background-color: hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 95% - ); - /*transition: none;*/ -} - - - - -.Q-circuit-register-wire { - - position: absolute; - top: calc( 50% - 0.5px ); - width: 100%; - height: 1px; - background-color: hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 50% - ); -} - -.Q-parameter-box-exit { - position: relative; - right: 0; - left: 0; - width: 5rem; - height: 2.5rem; - background-color: whitesmoke; - border-radius: 0; -} - -.Q-parameters-box > div, -.Q-circuit-palette > div, -.Q-circuit-clipboard > div, -.Q-circuit-board-foreground > div { - - text-align: center; -} - - - - - - - /***************/ - /* */ - /* Headers */ - /* */ -/***************/ - - -.Q-circuit-header { - - position: sticky; - z-index: 2; - margin: 0; - /*background-color: var( --Q-color-background );*/ - background-color: white; - color: hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 75% - ); - font-family: var( --Q-font-family-mono ); -} -.Q-circuit-input.Q-circuit-cell-highlighted, -.Q-circuit-header.Q-circuit-cell-highlighted { - - background-color: hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 95% - ); - color: black; -} -.Q-circuit-selectall { - - z-index: 3; - margin: 0; - top: 0; - /*left: 4rem;*/ - /*grid-column: 2;*/ - left: 0; - grid-column-start: 1; - grid-column-end: 3; - grid-row: 1; - cursor: se-resize; -} -.Q-circuit-moment-label, -.Q-circuit-moment-add { - - grid-row: 1; - top: 0; - cursor: s-resize; -} -.Q-circuit-register-label, -.Q-circuit-register-add { - - grid-column: 2; - left: 4rem; - cursor: e-resize; -} -.Q-circuit-moment-add, -.Q-circuit-register-add { - - cursor: pointer; -} -.Q-circuit-moment-add, -.Q-circuit-register-add { - - display: none; -} -.Q-circuit-selectall, -.Q-circuit-moment-label, -.Q-circuit-moment-add { - - border-bottom: 1px solid hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 95% - ); -} -.Q-circuit-selectall, -.Q-circuit-register-label, -.Q-circuit-register-add { - - border-right: 1px solid hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 95% - ); -} -.Q-circuit-input { - - position: sticky; - z-index: 2; - grid-column: 1; - left: 0; - /*background-color: var( --Q-color-background );*/ - background-color: white; - font-size: 1.5rem; - font-weight: 900; - font-family: var( --Q-font-family-mono ); -} - - - - - - -.Q-circuit-operation-link-container { - - --Q-link-stroke: 3px; - --Q-link-radius: 100%; - - display: block; - position: relative; - left: calc( 50% - ( var( --Q-link-stroke ) / 2 )); - width: 50%; - height: 100%; - overflow: hidden; -} -.Q-circuit-operation-link-container.Q-circuit-cell-highlighted { - - background-color: transparent; -} -.Q-circuit-operation-link { - - display: block; - position: absolute; - width: calc( var( --Q-link-stroke ) * 2 ); - height: calc( 100% - 4rem + var( --Q-link-stroke )); - /*border: var( --Q-link-stroke ) solid hsl( 0, 0%, 50% );*/ - border: var( --Q-link-stroke ) solid hsl( - - var( --Q-color-background-hue ), - 10%, - 30% - ); - - /*border: var( --Q-link-stroke ) solid var( --Q-color-orange );*/ - - transform: translate( -50%, calc( 2rem - ( var( --Q-link-stroke ) / 2 ))); - transform-origin: center; -} -.Q-circuit-operation-link.Q-circuit-operation-link-curved { - - width: calc( var( --Q-link-radius ) - var( --Q-link-stroke )); - width: 200%; - border-radius: 100%; -} - - - - - - - /******************/ - /* */ - /* Operations */ - /* */ -/******************/ - - -.Q-circuit-operation { - - position: relative; - /*--Q-operation-color-hue: var( --Q-color-green-hue ); - --Q-operation-color-main: var( --Q-color-green );*/ - - --Q-operation-color-hue: var( --Q-color-blue-hue ); - --Q-operation-color-main: hsl( - - var( --Q-operation-color-hue ), - 10%, - 35% - ); - - --Q-operation-color-light: hsl( - - var( --Q-operation-color-hue ), - 10%, - 50% - ); - --Q-operation-color-dark: hsl( - - var( --Q-operation-color-hue ), - 10%, - 25% - ); - color: white; - text-shadow: -0.05rem -0.05rem 0 rgba( 0, 0, 0, 0.1 ); - font-size: 1.5rem; - line-height: 2.9rem; - font-weight: 900; - cursor: grab; -} -.Q-circuit-locked .Q-circuit-operation { - - cursor: not-allowed; -} -.Q-circuit-operation-tile { - - position: absolute; - top: 0.5rem; - left: 0.5rem; - right: 0.5rem; - bottom: 0.5rem; - - /*margin: 0.5rem;*/ - /*padding: 0.5rem;*/ - - /*box-shadow: 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.2 );*/ - border-radius: 0.2rem; - /* - border-top: 0.1rem solid var( --Q-operation-color-light ); - border-left: 0.1rem solid var( --Q-operation-color-light ); - border-right: 0.1rem solid var( --Q-operation-color-dark ); - border-bottom: 0.1rem solid var( --Q-operation-color-dark ); - */ - background: - var( --Q-operation-color-main ) - /*linear-gradient( - - 0.45turn, - rgba( 255, 255, 255, 0.1 ), - rgba( 0, 0, 0, 0.05 ) - )*/; -} -.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover { - - /*background-color: rgba( 255, 255, 255, 0.6 );*/ - background-color: white; -} -.Q-circuit-palette .Q-circuit-operation-tile { - - --Q-before-rotation: 12deg; - --Q-before-x: 1px; - --Q-before-y: -2px; - - --Q-after-rotation: -7deg; - --Q-after-x: -2px; - --Q-after-y: 3px; - - box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); -} -.Q-circuit-palette .Q-circuit-operation-tile:before, -.Q-circuit-palette .Q-circuit-operation-tile:after { - - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - border-radius: 0.2rem; - /*background-color: hsl( 0, 0%, 60% );*/ - - background-color: var( --Q-operation-color-dark ); - transform: - translate( var( --Q-before-x ), var( --Q-before-y )) - rotate( var( --Q-before-rotation )); - z-index: -10; - /*z-index: 10;*/ - display: block; - box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); -} -.Q-circuit-palette .Q-circuit-operation-tile:after { - - transform: - translate( var( --Q-after-x ), var( --Q-after-y )) - rotate( var( --Q-after-rotation )); - box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); -} -.Q-circuit-operation:hover .Q-circuit-operation-tile { - - color: white; -} - - - - -.Q-circuit-operation-hadamard .Q-circuit-operation-tile { - - /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ - /*--Q-operation-color-main: var( --Q-color-red );*/ - - /*--Q-operation-color-hue: 0; - --Q-operation-color-main: hsl( 0, 0%, 10% );*/ - - -/* background: - linear-gradient( - - -33deg, - var( --Q-color-blue ) 20%, - #6f3c69 50%, - var( --Q-color-red ) 80% - );*/ -} -.Q-circuit-operation-identity .Q-circuit-operation-tile, -.Q-circuit-operation-control .Q-circuit-operation-tile, -.Q-circuit-operation-target .Q-circuit-operation-tile { - - /*--Q-operation-color-hue: var( --Q-color-orange-hue );*/ - /*--Q-operation-color-main: var( --Q-color-orange );*/ - border-radius: 100%; -} -.Q-circuit-operation-identity .Q-circuit-operation-tile, -.Q-circuit-operation-control .Q-circuit-operation-tile { - - top: calc( 50% - 0.7rem ); - left: calc( 50% - 0.7rem ); - width: 1.4rem; - height: 1.4rem; - overflow: hidden; -/* --Q-operation-color-hue: 0; - --Q-operation-color-main: hsl( 0, 0%, 10% );*/ -} -.Q-circuit-operation-pauli-x, -.Q-circuit-operation-pauli-y, -.Q-circuit-operation-pauli-z { - - /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ - /*--Q-operation-color-main: var( --Q-color-red );*/ - -/* --Q-operation-color-hue: 0; - --Q-operation-color-main: hsl( 0, 0%, 30% );*/ -} -.Q-circuit-operation-swap .Q-circuit-operation-tile { - - top: calc( 50% - 0.55rem ); - left: calc( 50% - 0.55rem ); - width: 1.2rem; - height: 1.2rem; - border-radius: 0; - transform-origin: center; - transform: rotate( 45deg ); - font-size: 0; -} - - -.Q-parameter-box-input-container { - position: relative; - text-align: center; - grid-auto-columns: 4rem; - grid-auto-flow: column; -} - -.Q-parameter-box-input { - position: relative; - border-radius: .2rem; - margin-left: 10px; - font-family: var( --Q-font-family-mono ); -} - -.Q-parameter-input-label { - position: relative; - color: var( --Q-color-blue ); - font-family: var( --Q-font-family-mono ); -} - - - - - /********************/ - /* */ - /* Other states */ - /* */ -/********************/ - - -.Q-circuit-palette > div:hover, -.Q-circuit-board-foreground > div:hover { - - outline: 2px solid var( --Q-hyperlink-internal-color ); - outline-offset: -2px; -} -.Q-circuit-palette > div:hover .Q-circuit-operation-tile { - - box-shadow: none; -} -/*.Q-circuit-palette > div:hover,*/ -.Q-circuit-board-foreground > div:hover { - - background-color: white; - color: black; -} - - - - - - -.Q-circuit-clipboard > div, -.Q-circuit-cell-selected { - - background-color: white; -} -.Q-circuit-clipboard > div:before, -.Q-circuit-cell-selected:before { - - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - display: block; - z-index: -10; - box-shadow: - 0 0 1rem rgba( 0, 0, 0, 0.2 ), - 0.4rem 0.4rem 0.2rem rgba( 0, 0, 0, 0.2 ); - outline: 1px solid hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 50% - ); - /*outline-offset: -1px;*/ -} - - - - -.Q-circuit-clipboard > div { - - background-color: white; -} -.Q-circuit-clipboard > div:before { - - /* - - This was very helpful! - https://blog.dudak.me/2014/css-shadows-under-adjacent-elements/ - - */ - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: -10; - display: block; - box-shadow: 0.4rem 0.4rem 0.3rem rgba( 0, 0, 0, 0.2 ); -} - - - - - - /***************/ - /* */ - /* Buttons */ - /* */ -/***************/ - - -.Q-circuit-locked .Q-circuit-toggle-lock, -.Q-circuit-locked .Q-circuit-toggle-lock:hover { - - background-color: var( --Q-color-red ); -} -.Q-circuit-toggle-lock { - - z-index: 3; - left: 0; - top: 0; - grid-column: 1; - grid-row: 1; - cursor: pointer; - font-size: 1.1rem; - text-shadow: none; - font-weight: normal; -} -.Q-circuit-button-undo, -.Q-circuit-button-redo { - - font-size: 1.2rem; - line-height: 2.6rem; - font-weight: normal; -} - - - -.Q-circuit p { - - padding: 1rem; - color: hsl( - - var( --Q-color-background-hue ), - var( --Q-color-background-saturation ), - 66% - ); -} - - diff --git a/build/q-old.js b/build/q-old.js deleted file mode 100644 index ddcf90e..0000000 --- a/build/q-old.js +++ /dev/null @@ -1,7874 +0,0 @@ - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const Q = function(){ - - - // Did we send arguments of the form - // ( bandwidth, timewidth )? - if( arguments.length === 2 && - Array.from( arguments ).every( function( argument ){ - - return Q.isUsefulInteger( argument ) - - })){ - - return new Q.Circuit( arguments[ 0 ], arguments[ 1 ]) - } - - - // Otherwise assume we are creating a circuit - // from a text block. - - return Q.Circuit.fromText( arguments[ 0 ]) -} - - - - -Object.assign( Q, { - - verbosity: 0.5, - log: function( verbosityThreshold, ...remainingArguments ){ - - if( Q.verbosity >= verbosityThreshold ) console.log( ...remainingArguments ) - return '(log)' - }, - warn: function(){ - - console.warn( ...arguments ) - return '(warn)' - }, - error: function(){ - - console.error( ...arguments ) - return '(error)' - }, - extractDocumentation: function( f ){ - - ` - I wanted a way to document code - that was cleaner, more legible, and more elegant - than the bullshit we put up with today. - Also wanted it to print nicely in the console. - ` - - f = f.toString() - - const - begin = f.indexOf( '`' ) + 1, - end = f.indexOf( '`', begin ), - lines = f.substring( begin, end ).split( '\n' ) - - - function countPrefixTabs( text ){ - - - // Is counting tabs “manually” - // actually more performant than regex? - - let count = index = 0 - while( text.charAt( index ++ ) === '\t' ) count ++ - return count - } - - - //------------------- TO DO! - // we should check that there is ONLY whitespace between the function opening and the tick mark! - // otherwise it’s not documentation. - - let - tabs = Number.MAX_SAFE_INTEGER - - lines.forEach( function( line ){ - - if( line ){ - - const lineTabs = countPrefixTabs( line ) - if( tabs > lineTabs ) tabs = lineTabs - } - }) - lines.forEach( function( line, i ){ - - if( line.trim() === '' ) line = '\n\n' - lines[ i ] = line.substring( tabs ).replace( / {2}$/, '\n' ) - }) - return lines.join( '' ) - }, - help: function( f ){ - - if( f === undefined ) f = Q - return Q.extractDocumentation( f ) - }, - constants: {}, - createConstant: function( key, value ){ - - //Object.freeze( value ) - this[ key ] = value - // Object.defineProperty( this, key, { - - // value, - // writable: false - // }) - // Object.defineProperty( this.constants, key, { - - // value, - // writable: false - // }) - this.constants[ key ] = this[ key ] - Object.freeze( this[ key ]) - }, - createConstants: function(){ - - if( arguments.length % 2 !== 0 ){ - - return Q.error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' ) - } - for( let i = 0; i < arguments.length; i += 2 ){ - - this.createConstant( arguments[ i ], arguments[ i + 1 ]) - } - }, - - - - - isUsefulNumber: function( n ){ - - return isNaN( n ) === false && - ( typeof n === 'number' || n instanceof Number ) && - n !== Infinity && - n !== -Infinity - }, - isUsefulInteger: function( n ){ - - return Q.isUsefulNumber( n ) && Number.isInteger( n ) - }, - loop: function(){}, - - - - - hypotenuse: function( x, y ){ - - let - a = Math.abs( x ), - b = Math.abs( y ) - - if( a < 2048 && b < 2048 ){ - - return Math.sqrt( a * a + b * b ) - } - if( a < b ){ - - a = b - b = x / y - } - else b = y / x - return a * Math.sqrt( 1 + b * b ) - }, - logHypotenuse: function( x, y ){ - - const - a = Math.abs( x ), - b = Math.abs( y ) - - if( x === 0 ) return Math.log( b ) - if( y === 0 ) return Math.log( a ) - if( a < 2048 && b < 2048 ){ - - return Math.log( x * x + y * y ) / 2 - } - return Math.log( x / Math.cos( Math.atan2( y, x ))) - }, - hyperbolicSine: function( n ){ - - return ( Math.exp( n ) - Math.exp( -n )) / 2 - }, - hyperbolicCosine: function( n ){ - - return ( Math.exp( n ) + Math.exp( -n )) / 2 - }, - round: function( n, d ){ - - if( typeof d !== 'number' ) d = 0 - const f = Math.pow( 10, d ) - return Math.round( n * f ) / f - }, - toTitleCase: function( text ){ - - text = text.replace( /_/g, ' ' ) - return text.toLowerCase().split( ' ' ).map( function( word ){ - - return word.replace( word[ 0 ], word[ 0 ].toUpperCase() ) - - }).join(' ') - }, - centerText: function( text, length, filler ){ - - if( length > text.length ){ - - if( typeof filler !== 'string' ) filler = ' ' - - const - padLengthLeft = Math.floor(( length - text.length ) / 2 ), - padLengthRight = length - text.length - padLengthLeft - - return text - .padStart( padLengthLeft + text.length, filler ) - .padEnd( length, filler ) - } - else return text - }, - - - - - - - - - namesIndex: 0, - shuffledNames: [], - shuffleNames$: function(){ - - let m = [] - for( let c = 0; c < Q.COLORS.length; c ++ ){ - - for( let a = 0; a < Q.ANIMALS.length; a ++ ){ - - m.push([ c, a, Math.random() ]) - } - } - Q.shuffledNames = m.sort( function( a, b ){ - - return a[ 2 ] - b[ 2 ] - }) - }, - getRandomName$: function(){ - - if( Q.shuffledNames.length === 0 ) Q.shuffleNames$() - - const - pair = Q.shuffledNames[ Q.namesIndex ], - name = Q.COLORS[ pair[ 0 ]] +' '+ Q.ANIMALS[ pair[ 1 ]] - - Q.namesIndex = ( Q.namesIndex + 1 ) % Q.shuffledNames.length - return name - }, - hueToColorName: function( hue ){ - - hue = hue % 360 - hue = Math.floor( hue / 10 ) - return Q.COLORS[ hue ] - }, - colorIndexToHue: function( i ){ - - return i * 10 - } - - - - -}) - - - - -Q.createConstants( - - 'REVISION', 19, - - - // Yeah... F’ing floating point numbers, Man! - // Here’s the issue: - // var a = new Q.ComplexNumber( 1, 2 ) - // a.multiply(a).isEqualTo( a.power( new Q.ComplexNumber( 2, 0 ))) - // That’s only true if Q.EPSILON >= Number.EPSILON * 6 - - 'EPSILON', Number.EPSILON * 6, - - 'RADIANS_TO_DEGREES', 180 / Math.PI, - - 'ANIMALS', [ - - 'Aardvark', - 'Albatross', - 'Alligator', - 'Alpaca', - 'Ant', - 'Anteater', - 'Antelope', - 'Ape', - 'Armadillo', - 'Baboon', - 'Badger', - 'Barracuda', - 'Bat', - 'Bear', - 'Beaver', - 'Bee', - 'Bison', - 'Boar', - 'Buffalo', - 'Butterfly', - 'Camel', - 'Caribou', - 'Cat', - 'Caterpillar', - 'Cattle', - 'Chamois', - 'Cheetah', - 'Chicken', - 'Chimpanzee', - 'Chinchilla', - 'Chough', - 'Clam', - 'Cobra', - 'Cod', - 'Cormorant', - 'Coyote', - 'Crab', - 'Crane', - 'Crocodile', - 'Crow', - 'Curlew', - 'Deer', - 'Dinosaur', - 'Dog', - 'Dogfish', - 'Dolphin', - 'Donkey', - 'Dotterel', - 'Dove', - 'Dragonfly', - 'Duck', - 'Dugong', - 'Dunlin', - 'Eagle', - 'Echidna', - 'Eel', - 'Eland', - 'Elephant', - 'Elephant seal', - 'Elk', - 'Emu', - 'Falcon', - 'Ferret', - 'Finch', - 'Fish', - 'Flamingo', - 'Fly', - 'Fox', - 'Frog', - 'Galago', - 'Gaur', - 'Gazelle', - 'Gerbil', - 'Giant Panda', - 'Giraffe', - 'Gnat', - 'Gnu', - 'Goat', - 'Goose', - 'Goldfinch', - 'Goldfish', - 'Gorilla', - 'Goshawk', - 'Grasshopper', - 'Grouse', - 'Guanaco', - 'Guinea fowl', - 'Guinea pig', - 'Gull', - 'Guppy', - 'Hamster', - 'Hare', - 'Hawk', - 'Hedgehog', - 'Hen', - 'Heron', - 'Herring', - 'Hippopotamus', - 'Hornet', - 'Horse', - 'Human', - 'Hummingbird', - 'Hyena', - 'Ide', - 'Jackal', - 'Jaguar', - 'Jay', - 'Jellyfish', - 'Kangaroo', - 'Koala', - 'Koi', - 'Komodo dragon', - 'Kouprey', - 'Kudu', - 'Lapwing', - 'Lark', - 'Lemur', - 'Leopard', - 'Lion', - 'Llama', - 'Lobster', - 'Locust', - 'Loris', - 'Louse', - 'Lyrebird', - 'Magpie', - 'Mallard', - 'Manatee', - 'Marten', - 'Meerkat', - 'Mink', - 'Mole', - 'Monkey', - 'Moose', - 'Mouse', - 'Mosquito', - 'Mule', - 'Narwhal', - 'Newt', - 'Nightingale', - 'Octopus', - 'Okapi', - 'Opossum', - 'Oryx', - 'Ostrich', - 'Otter', - 'Owl', - 'Ox', - 'Oyster', - 'Panther', - 'Parrot', - 'Partridge', - 'Peafowl', - 'Pelican', - 'Penguin', - 'Pheasant', - 'Pig', - 'Pigeon', - 'Pony', - 'Porcupine', - 'Porpoise', - 'Prairie Dog', - 'Quail', - 'Quelea', - 'Rabbit', - 'Raccoon', - 'Rail', - 'Ram', - 'Raven', - 'Reindeer', - 'Rhinoceros', - 'Rook', - 'Ruff', - 'Salamander', - 'Salmon', - 'Sand Dollar', - 'Sandpiper', - 'Sardine', - 'Scorpion', - 'Sea lion', - 'Sea Urchin', - 'Seahorse', - 'Seal', - 'Shark', - 'Sheep', - 'Shrew', - 'Shrimp', - 'Skunk', - 'Snail', - 'Snake', - 'Sow', - 'Spider', - 'Squid', - 'Squirrel', - 'Starling', - 'Stingray', - 'Stinkbug', - 'Stork', - 'Swallow', - 'Swan', - 'Tapir', - 'Tarsier', - 'Termite', - 'Tiger', - 'Toad', - 'Trout', - 'Tui', - 'Turkey', - 'Turtle', - // U - 'Vicuña', - 'Viper', - 'Vulture', - 'Wallaby', - 'Walrus', - 'Wasp', - 'Water buffalo', - 'Weasel', - 'Whale', - 'Wolf', - 'Wolverine', - 'Wombat', - 'Woodcock', - 'Woodpecker', - 'Worm', - 'Wren', - // X - 'Yak', - 'Zebra' - - ], - 'ANIMALS3', [ - - 'ape', - 'bee', - 'cat', - 'dog', - 'elk', - 'fox', - 'gup', - 'hen', - 'ide', - 'jay', - 'koi', - 'leo', - 'moo', - 'nit', - 'owl', - 'pig', - // Q ? - 'ram', - 'sow', - 'tui', - // U ? - // V ? - // W ? - // X ? - 'yak', - 'zeb' - ], - 'COLORS', [ - - 'Red', // 0 RED - 'Scarlet', // 10 - 'Tawny', // 20 - 'Carrot', // 30 - 'Pumpkin', // 40 - 'Mustard', // 50 - 'Lemon', // 60 Yellow - 'Lime', // 70 - 'Spring bud', // 80 - 'Spring grass',// 90 - 'Pear', // 100 - 'Kelly', // 110 - 'Green', // 120 GREEN - 'Malachite', // 130 - 'Sea green', // 140 - 'Sea foam', // 150 - 'Aquamarine', // 160 - 'Turquoise', // 170 - 'Cyan', // 180 Cyan - 'Pacific blue',// 190 - 'Baby blue', // 200 - 'Ocean blue', // 210 - 'Sapphire', // 220 - 'Azure', // 230 - 'Blue', // 240 BLUE - 'Cobalt', // 250 - 'Indigo', // 260 - 'Violet', // 270 - 'Lavender', // 280 - 'Purple', // 290 - 'Magenta', // 300 Magenta - 'Hot pink', // 310 - 'Fuschia', // 320 - 'Ruby', // 330 - 'Crimson', // 340 - 'Carmine' // 350 - ] -) - - - - -console.log( ` - - - QQQQQQ -QQ QQ -QQ QQ -QQ QQ -QQ QQ QQ -QQ QQ - QQQQ ${Q.REVISION} - - - -https://quantumjavascript.app - - - -` ) - - - - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.ComplexNumber = function( real, imaginary ){ - - ` - The set of “real numbers” (ℝ) contains any number that can be expressed - along an infinite timeline. https://en.wikipedia.org/wiki/Real_number - - … -3 -2 -1 0 +1 +2 +3 … - ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄ - √2 𝒆 Ï€ - - - Meanwhile, “imaginary numbers” (𝕀) consist of a real (ℝ) multiplier and - the symbol 𝒊, which is the impossible solution to the equation 𝒙² = −1. - Note that no number when multiplied by itself can ever result in a - negative product, but the concept of 𝒊 gives us a way to reason around - this imaginary scenario nonetheless. - https://en.wikipedia.org/wiki/Imaginary_number - - … -3𝒊 -2𝒊 -1𝒊 0𝒊 +1𝒊 +2𝒊 +3𝒊 … - ┄───┴───┴───┴───┴───┴───┴───┴───┄ - - - A “complex number“ (â„‚) is a number that can be expressed in the form - 𝒂 + 𝒃𝒊, where 𝒂 is the real component (ℝ) and 𝒃𝒊 is the imaginary - component (𝕀). https://en.wikipedia.org/wiki/Complex_number - - - Operation functions on Q.ComplexNumber instances generally accept as - arguments both sibling instances and pure Number instances, though the - value returned is always an instance of Q.ComplexNumber. - - ` - - if( real instanceof Q.ComplexNumber ){ - - imaginary = real.imaginary - real = real.real - Q.warn( 'Q.ComplexNumber tried to create a new instance with an argument that is already a Q.ComplexNumber — and that’s weird!' ) - } - else if( real === undefined ) real = 0 - if( imaginary === undefined ) imaginary = 0 - if(( Q.ComplexNumber.isNumberLike( real ) !== true && isNaN( real ) !== true ) || - ( Q.ComplexNumber.isNumberLike( imaginary ) !== true && isNaN( imaginary ) !== true )) - return Q.error( 'Q.ComplexNumber attempted to create a new instance but the arguments provided were not actual numbers.' ) - - this.real = real - this.imaginary = imaginary - this.index = Q.ComplexNumber.index ++ -} - - - - -Object.assign( Q.ComplexNumber, { - - index: 0, - help: function(){ return Q.help( this )}, - constants: {}, - createConstant: Q.createConstant, - createConstants: Q.createConstants, - - - - - toText: function( rNumber, iNumber, roundToDecimal, padPositive ){ - - // Should we round these numbers? - // Our default is yes: to 3 digits. - // Otherwise round to specified decimal. - - if( typeof roundToDecimal !== 'number' ) roundToDecimal = 3 - const factor = Math.pow( 10, roundToDecimal ) - rNumber = Math.round( rNumber * factor ) / factor - iNumber = Math.round( iNumber * factor ) / factor - - - // Convert padPositive - // from a potential Boolean - // to a String. - // If we don’t receive a FALSE - // then we’ll pad the positive numbers. - - padPositive = padPositive === false ? '' : ' ' - - - // We need the absolute values of each. - - let - rAbsolute = Math.abs( rNumber ), - iAbsolute = Math.abs( iNumber ) - - - // And an absolute value string. - - let - rText = rAbsolute.toString(), - iText = iAbsolute.toString() - - - // Is this an IMAGINARY-ONLY number? - // Don’t worry: -0 === 0. - - if( rNumber === 0 ){ - - if( iNumber === Infinity ) return padPositive +'∞i' - if( iNumber === -Infinity ) return '-∞i' - if( iNumber === 0 ) return padPositive +'0' - if( iNumber === -1 ) return '-i' - if( iNumber === 1 ) return padPositive +'i' - if( iNumber >= 0 ) return padPositive + iText +'i' - if( iNumber < 0 ) return '-'+ iText +'i' - return iText +'i'// NaN - } - - - // This number contains a real component - // and may also contain an imaginary one as well. - - if( rNumber === Infinity ) rText = padPositive +'∞' - else if( rNumber === -Infinity ) rText = '-∞' - else if( rNumber >= 0 ) rText = padPositive + rText - else if( rNumber < 0 ) rText = '-'+ rText - - if( iNumber === Infinity ) return rText +' + ∞i' - if( iNumber === -Infinity ) return rText +' - ∞i' - if( iNumber === 0 ) return rText - if( iNumber === -1 ) return rText +' - i' - if( iNumber === 1 ) return rText +' + i' - if( iNumber > 0 ) return rText +' + '+ iText +'i' - if( iNumber < 0 ) return rText +' - '+ iText +'i' - return rText +' + '+ iText +'i'// NaN - }, - - - - - isNumberLike: function( n ){ - - return isNaN( n ) === false && ( typeof n === 'number' || n instanceof Number ) - }, - isNaN: function( n ){ - - return isNaN( n.real ) || isNaN( n.imaginary ) - }, - isZero: function( n ){ - - return ( n.real === 0 || n.real === -0 ) && - ( n.imaginary === 0 || n.imaginary === -0 ) - }, - isFinite: function( n ){ - - return isFinite( n.real ) && isFinite( n.imaginary ) - }, - isInfinite: function( n ){ - - return !( this.isNaN( n ) || this.isFinite( n )) - }, - areEqual: function( a, b ){ - - return Q.ComplexNumber.operate( - - 'areEqual', a, b, - function( a, b ){ - - return Math.abs( a - b ) < Q.EPSILON - }, - function( a, b ){ - - return ( - - Math.abs( a - b.real ) < Q.EPSILON && - Math.abs( b.imaginary ) < Q.EPSILON - ) - }, - function( a, b ){ - - return ( - - Math.abs( a.real - b ) < Q.EPSILON && - Math.abs( a.imaginary ) < Q.EPSILON - ) - }, - function( a, b ){ - - return ( - - Math.abs( a.real - b.real ) < Q.EPSILON && - Math.abs( a.imaginary - b.imaginary ) < Q.EPSILON - ) - } - ) - }, - - - - - absolute: function( n ){ - - return Q.hypotenuse( n.real, n.imaginary ) - }, - conjugate: function( n ){ - - return new Q.ComplexNumber( n.real, n.imaginary * -1 ) - }, - operate: function( - - name, - a, - b, - numberAndNumber, - numberAndComplex, - complexAndNumber, - complexAndComplex ){ - - if( Q.ComplexNumber.isNumberLike( a )){ - - if( Q.ComplexNumber.isNumberLike( b )) return numberAndNumber( a, b ) - else if( b instanceof Q.ComplexNumber ) return numberAndComplex( a, b ) - else return Q.error( 'Q.ComplexNumber attempted to', name, 'with the number', a, 'and something that is neither a Number or Q.ComplexNumber:', b ) - } - else if( a instanceof Q.ComplexNumber ){ - - if( Q.ComplexNumber.isNumberLike( b )) return complexAndNumber( a, b ) - else if( b instanceof Q.ComplexNumber ) return complexAndComplex( a, b ) - else return Q.error( 'Q.ComplexNumber attempted to', name, 'with the complex number', a, 'and something that is neither a Number or Q.ComplexNumber:', b ) - } - else return Q.error( 'Q.ComplexNumber attempted to', name, 'with something that is neither a Number or Q.ComplexNumber:', a ) - }, - - - - - sine: function( n ){ - - const - a = n.real, - b = n.imaginary - - return new Q.ComplexNumber( - - Math.sin( a ) * Q.hyperbolicCosine( b ), - Math.cos( a ) * Q.hyperbolicSine( b ) - ) - }, - cosine: function( n ){ - - const - a = n.real, - b = n.imaginary - - return new Q.ComplexNumber( - - Math.cos( a ) * Q.hyperbolicCosine( b ), - -Math.sin( a ) * Q.hyperbolicSine( b ) - ) - }, - arcCosine: function( n ){ - - const - a = n.real, - b = n.imaginary, - t1 = Q.ComplexNumber.squareRoot( new Q.ComplexNumber( - - b * b - a * a + 1, - a * b * -2 - - )), - t2 = Q.ComplexNumber.log( new Q.ComplexNumber( - - t1.real - b, - t1.imaginary + a - )) - return new Q.ComplexNumber( Math.PI / 2 - t2.imaginary, t2.real ) - }, - arcTangent: function( n ){ - - const - a = n.real, - b = n.imaginary - - if( a === 0 ){ - - if( b === 1 ) return new Q.ComplexNumber( 0, Infinity ) - if( b === -1 ) return new Q.ComplexNumber( 0, -Infinity ) - } - - const - d = a * a + ( 1 - b ) * ( 1 - b ), - t = Q.ComplexNumber.log( new Q.ComplexNumber( - - ( 1 - b * b - a * a ) / d, - a / d * -2 - - )) - return new Q.ComplexNumber( t.imaginary / 2, t.real / 2 ) - }, - - - - - power: function( a, b ){ - - if( Q.ComplexNumber.isNumberLike( a )) a = new Q.ComplexNumber( a ) - if( Q.ComplexNumber.isNumberLike( b )) b = new Q.ComplexNumber( b ) - - - // Anything raised to the Zero power is 1. - - if( b.isZero() ) return Q.ComplexNumber.ONE - - - // Zero raised to any power is 0. - // Note: What happens if b.real is zero or negative? - // What happens if b.imaginary is negative? - // Do we really need those conditionals?? - - if( a.isZero() && - b.real > 0 && - b.imaginary >= 0 ){ - - return Q.ComplexNumber.ZERO - } - - - // If our exponent is Real (has no Imaginary component) - // then we’re really just raising to a power. - - if( b.imaginary === 0 ){ - - if( a.real >= 0 && a.imaginary === 0 ){ - - return new Q.ComplexNumber( Math.pow( a.real, b.real ), 0 ) - } - else if( a.real === 0 ){// If our base is Imaginary (has no Real component). - - switch(( b.real % 4 + 4 ) % 4 ){ - - case 0: - return new Q.ComplexNumber( Math.pow( a.imaginary, b.real ), 0 ) - case 1: - return new Q.ComplexNumber( 0, Math.pow( a.imaginary, b.real )) - case 2: - return new Q.ComplexNumber( -Math.pow( a.imaginary, b.real ), 0 ) - case 3: - return new Q.ComplexNumber( 0, -Math.pow( a.imaginary, b.real )) - } - } - } - - - const - arctangent2 = Math.atan2( a.imaginary, a.real ), - logHypotenuse = Q.logHypotenuse( a.real, a.imaginary ), - x = Math.exp( b.real * logHypotenuse - b.imaginary * arctangent2 ), - y = b.imaginary * logHypotenuse + b.real * arctangent2 - - return new Q.ComplexNumber( - - x * Math.cos( y ), - x * Math.sin( y ) - ) - }, - squareRoot: function( a ){ - - const - result = new Q.ComplexNumber( 0, 0 ), - absolute = Q.ComplexNumber.absolute( a ) - - if( a.real >= 0 ){ - - if( a.imaginary === 0 ){ - - result.real = Math.sqrt( a.real )// and imaginary already equals 0. - } - else { - - result.real = Math.sqrt( 2 * ( absolute + a.real )) / 2 - } - } - else { - - result.real = Math.abs( a.imaginary ) / Math.sqrt( 2 * ( absolute - a.real )) - } - if( a.real <= 0 ){ - - result.imaginary = Math.sqrt( 2 * ( absolute - a.real )) / 2 - } - else { - - result.imaginary = Math.abs( a.imaginary ) / Math.sqrt( 2 * ( absolute + a.real )) - } - if( a.imaginary < 0 ) result.imaginary *= -1 - return result - }, - log: function( a ){ - - return new Q.ComplexNumber( - - Q.logHypotenuse( a.real, a.imaginary ), - Math.atan2( a.imaginary, a.real ) - ) - }, - multiply: function( a, b ){ - - return Q.ComplexNumber.operate( - - 'multiply', a, b, - function( a, b ){ - - return new Q.ComplexNumber( a * b ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - a * b.real, - a * b.imaginary - ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - a.real * b, - a.imaginary * b - ) - }, - function( a, b ){ - - - // FOIL Method that shit. - // https://en.wikipedia.org/wiki/FOIL_method - - const - firsts = a.real * b.real, - outers = a.real * b.imaginary, - inners = a.imaginary * b.real, - lasts = a.imaginary * b.imaginary * -1// Because i² = -1. - - return new Q.ComplexNumber( - - firsts + lasts, - outers + inners - ) - } - ) - }, - divide: function( a, b ){ - - return Q.ComplexNumber.operate( - - 'divide', a, b, - function( a, b ){ - - return new Q.ComplexNumber( a / b ) - }, - function( a, b ){ - - return new Q.ComplexNumber( a ).divide( b ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - a.real / b, - a.imaginary / b - ) - }, - function( a, b ){ - - - // Ermergerd I had to look this up because it’s been so long. - // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/complex-conjugates-and-dividing-complex-numbers/a/dividing-complex-numbers-review - - const - conjugate = b.conjugate(), - numerator = a.multiply( conjugate ), - - - // The .imaginary will be ZERO for sure, - // so this forces a ComplexNumber.divide( Number ) ;) - - denominator = b.multiply( conjugate ).real - - return numerator.divide( denominator ) - } - ) - }, - add: function( a, b ){ - - return Q.ComplexNumber.operate( - - 'add', a, b, - function( a, b ){ - - return new Q.ComplexNumber( a + b ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - b.real + a, - b.imaginary - ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - a.real + b, - a.imaginary - ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - a.real + b.real, - a.imaginary + b.imaginary - ) - } - ) - }, - subtract: function( a, b ){ - - return Q.ComplexNumber.operate( - - 'subtract', a, b, - function( a, b ){ - - return new Q.ComplexNumber( a - b ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - b.real - a, - b.imaginary - ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - a.real - b, - a.imaginary - ) - }, - function( a, b ){ - - return new Q.ComplexNumber( - - a.real - b.real, - a.imaginary - b.imaginary - ) - } - ) - } -}) - - - - -Q.ComplexNumber.createConstants( - - 'ZERO', new Q.ComplexNumber( 0, 0 ), - 'ONE', new Q.ComplexNumber( 1, 0 ), - 'E', new Q.ComplexNumber( Math.E, 0 ), - 'PI', new Q.ComplexNumber( Math.PI, 0 ), - 'I', new Q.ComplexNumber( 0, 1 ), - 'EPSILON', new Q.ComplexNumber( Q.EPSILON, Q.EPSILON ), - 'INFINITY', new Q.ComplexNumber( Infinity, Infinity ), - 'NAN', new Q.ComplexNumber( NaN, NaN ) -) - - - - -Object.assign( Q.ComplexNumber.prototype, { - - - // NON-destructive operations. - - clone: function(){ - - return new Q.ComplexNumber( this.real, this.imaginary ) - }, - reduce: function(){ - - - // Note: this *might* kill function chaining. - - if( this.imaginary === 0 ) return this.real - return this - }, - toText: function( roundToDecimal, padPositive ){ - - - // Note: this will kill function chaining. - - return Q.ComplexNumber.toText( this.real, this.imaginary, roundToDecimal, padPositive ) - }, - - - isNaN: function( n ){ - - return Q.ComplexNumber.isNaN( this )// Returned boolean will kill function chaining. - }, - isZero: function( n ){ - - return Q.ComplexNumber.isZero( this )// Returned boolean will kill function chaining. - }, - isFinite: function( n ){ - - return Q.ComplexNumber.isFinite( this )// Returned boolean will kill function chaining. - }, - isInfinite: function( n ){ - - return Q.ComplexNumber.isInfinite( this )// Returned boolean will kill function chaining. - }, - isEqualTo: function( b ){ - - return Q.ComplexNumber.areEqual( this, b )// Returned boolean will kill function chaining. - }, - - - absolute: function(){ - - return Q.ComplexNumber.absolute( this )// Returned number will kill function chaining. - }, - conjugate: function(){ - - return Q.ComplexNumber.conjugate( this ) - }, - - - power: function( b ){ - - return Q.ComplexNumber.power( this, b ) - }, - squareRoot: function(){ - - return Q.ComplexNumber.squareRoot( this ) - }, - log: function(){ - - return Q.ComplexNumber.log( this ) - }, - multiply: function( b ){ - - return Q.ComplexNumber.multiply( this, b ) - }, - divide: function( b ){ - - return Q.ComplexNumber.divide( this, b ) - }, - add: function( b ){ - - return Q.ComplexNumber.add( this, b ) - }, - subtract: function( b ){ - - return Q.ComplexNumber.subtract( this, b ) - }, - - - - - // DESTRUCTIVE operations. - - copy$: function( b ){ - - if( b instanceof Q.ComplexNumber !== true ) - return Q.error( `Q.ComplexNumber attempted to copy something that was not a complex number in to this complex number #${this.index}.`, this ) - - this.real = b.real - this.imaginary = b.imaginary - return this - }, - conjugate$: function(){ - - return this.copy$( this.conjugate() ) - }, - power$: function( b ){ - - return this.copy$( this.power( b )) - }, - squareRoot$: function(){ - - return this.copy$( this.squareRoot() ) - }, - log$: function(){ - - return this.copy$( this.log() ) - }, - multiply$: function( b ){ - - return this.copy$( this.multiply( b )) - }, - divide$: function( b ){ - - return this.copy$( this.divide( b )) - }, - add$: function( b ){ - - return this.copy$( this.add( b )) - }, - subtract$: function( b ){ - - return this.copy$( this.subtract( b )) - } -}) - - - - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.Matrix = function(){ - - - // We’re keeping track of how many matrices are - // actually being generated. Just curiosity. - - this.index = Q.Matrix.index ++ - - - let matrixWidth = null - - - // Has Matrix been called with two numerical arguments? - // If so, we need to create an empty Matrix - // with dimensions of those values. - - if( arguments.length == 1 && - Q.ComplexNumber.isNumberLike( arguments[ 0 ])){ - - matrixWidth = arguments[ 0 ] - this.rows = new Array( matrixWidth ).fill( 0 ).map( function(){ - - return new Array( matrixWidth ).fill( 0 ) - }) - } - else if( arguments.length == 2 && - Q.ComplexNumber.isNumberLike( arguments[ 0 ]) && - Q.ComplexNumber.isNumberLike( arguments[ 1 ])){ - - matrixWidth = arguments[ 0 ] - this.rows = new Array( arguments[ 1 ]).fill( 0 ).map( function(){ - - return new Array( matrixWidth ).fill( 0 ) - }) - } - else { - - // Matrices’ primary organization is by rows, - // which is more congruent with our written langauge; - // primarily organizated by horizontally juxtaposed glyphs. - // That means it’s easier to write an instance invocation in code - // and easier to read when inspecting properties in the console. - - let matrixWidthIsBroken = false - this.rows = Array.from( arguments ) - this.rows.forEach( function( row ){ - - if( row instanceof Array !== true ) row = [ row ] - if( matrixWidth === null ) matrixWidth = row.length - else if( matrixWidth !== row.length ) matrixWidthIsBroken = true - }) - if( matrixWidthIsBroken ) - return Q.error( `Q.Matrix found upon initialization that matrix#${this.index} row lengths were not equal. You are going to have a bad time.`, this ) - } - - - - - - - // But for convenience we can also organize by columns. - // Note this represents the transposed version of itself! - - const matrix = this - this.columns = [] - for( let x = 0; x < matrixWidth; x ++ ){ - - const column = [] - for( let y = 0; y < this.rows.length; y ++ ){ - - - // Since we’re combing through here - // this is a good time to convert Number to ComplexNumber! - - const value = matrix.rows[ y ][ x ] - if( typeof value === 'number' ){ - - // console.log('Created a complex number!') - matrix.rows[ y ][ x ] = new Q.ComplexNumber( value ) - } - else if( value instanceof Q.ComplexNumber === false ){ - return Q.error( `Q.Matrix found upon initialization that matrix#${this.index} contained non-quantitative values. A+ for creativity, but F for functionality.`, this ) - } - - // console.log( x, y, matrix.rows[ y ][ x ]) - - - Object.defineProperty( column, y, { - - get: function(){ return matrix.rows[ y ][ x ]}, - set: function( n ){ matrix.rows[ y ][ x ] = n } - }) - } - this.columns.push( column ) - } -} - - - - - - - /////////////////////////// - // // - // Static properties // - // // -/////////////////////////// - - -Object.assign( Q.Matrix, { - - index: 0, - help: function(){ return Q.help( this )}, - constants: {},// Only holds references; an easy way to look up what constants exist. - createConstant: Q.createConstant, - createConstants: Q.createConstants, - - - isMatrixLike: function( obj ){ - - //return obj instanceof Q.Matrix || Q.Matrix.prototype.isPrototypeOf( obj ) - return obj instanceof this || this.prototype.isPrototypeOf( obj ) - }, - isWithinRange: function( n, minimum, maximum ){ - - return typeof n === 'number' && - n >= minimum && - n <= maximum && - n == parseInt( n ) - }, - getWidth: function( matrix ){ - - return matrix.columns.length - }, - getHeight: function( matrix ){ - - return matrix.rows.length - }, - haveEqualDimensions: function( matrix0, matrix1 ){ - - return ( - - matrix0.rows.length === matrix1.rows.length && - matrix0.columns.length === matrix1.columns.length - ) - }, - areEqual: function( matrix0, matrix1 ){ - - if( matrix0 instanceof Q.Matrix !== true ) return false - if( matrix1 instanceof Q.Matrix !== true ) return false - if( Q.Matrix.haveEqualDimensions( matrix0, matrix1 ) !== true ) return false - return matrix0.rows.reduce( function( state, row, r ){ - - return state && row.reduce( function( state, cellValue, c ){ - - return state && cellValue.isEqualTo( matrix1.rows[ r ][ c ]) - - }, true ) - - }, true ) - }, - - - - - createSquare: function( size, f ){ - - if( typeof size !== 'number' ) size = 2 - if( typeof f !== 'function' ) f = function(){ return 0 } - const data = [] - for( let y = 0; y < size; y ++ ){ - - const row = [] - for( let x = 0; x < size; x ++ ){ - - row.push( f( x, y )) - } - data.push( row ) - } - return new Q.Matrix( ...data ) - }, - createZero: function( size ){ - - return new Q.Matrix.createSquare( size ) - }, - createOne: function( size ){ - - return new Q.Matrix.createSquare( size, function(){ return 1 }) - }, - createIdentity: function( size ){ - - return new Q.Matrix.createSquare( size, function( x, y ){ return x === y ? 1 : 0 }) - }, - - - - - // Import FROM a format. - - from: function( format ){ - - if( typeof format !== 'string' ) format = 'Array' - const f = Q.Matrix[ 'from'+ format ] - format = format.toLowerCase() - if( typeof f !== 'function' ) - return Q.error( `Q.Matrix could not find an importer for “${format}” data.` ) - return f - }, - fromArray: function( array ){ - - return new Q.Matrix( ...array ) - }, - fromXsv: function( input, rowSeparator, valueSeparator ){ - - ` - Ingest string data organized by row, then by column - where rows are separated by one token (default: \n) - and column values are separated by another token - (default: \t). - - ` - - if( typeof rowSeparator !== 'string' ) rowSeparator = '\n' - if( typeof valueSeparator !== 'string' ) valueSeparator = '\t' - - const - inputRows = input.split( rowSeparator ), - outputRows = [] - - inputRows.forEach( function( inputRow ){ - - inputRow = inputRow.trim() - if( inputRow === '' ) return - - const outputRow = [] - inputRow.split( valueSeparator ).forEach( function( cellValue ){ - - outputRow.push( parseFloat( cellValue )) - }) - outputRows.push( outputRow ) - }) - return new Q.Matrix( ...outputRows ) - }, - fromCsv: function( csv ){ - - return Q.Matrix.fromXsv( csv.replace( /\r/g, '\n' ), '\n', ',' ) - }, - fromTsv: function( tsv ){ - - return Q.Matrix.fromXsv( tsv, '\n', '\t' ) - }, - fromHtml: function( html ){ - - return Q.Matrix.fromXsv( - - html - .replace( /\r?\n|\r||/g, '' ) - .replace( /<\/td>(\s*)<\/tr>/g, '' ) - .match( /(.*)<\/table>/i )[ 1 ], - '', - '' - ) - }, - - - - - // Export TO a format. - - toXsv: function( matrix, rowSeparator, valueSeparator ){ - - return matrix.rows.reduce( function( xsv, row ){ - - return xsv + rowSeparator + row.reduce( function( xsv, cell, c ){ - - return xsv + ( c > 0 ? valueSeparator : '' ) + cell.toText() - - }, '' ) - - }, '' ) - }, - toCsv: function( matrix ){ - - return Q.Matrix.toXsv( matrix, '\n', ',' ) - }, - toTsv: function( matrix ){ - - return Q.Matrix.toXsv( matrix, '\n', '\t' ) - }, - - - - - // Operate NON-destructive. - - add: function( matrix0, matrix1 ){ - - if( Q.Matrix.isMatrixLike( matrix0 ) !== true || - Q.Matrix.isMatrixLike( matrix1 ) !== true ){ - - return Q.error( `Q.Matrix attempted to add something that was not a matrix.` ) - } - if( Q.Matrix.haveEqualDimensions( matrix0, matrix1 ) !== true ) - return Q.error( `Q.Matrix cannot add matrix#${matrix0.index} of dimensions ${matrix0.columns.length}x${matrix0.rows.length} to matrix#${matrix1.index} of dimensions ${matrix1.columns.length}x${matrix1.rows.length}.`) - - return new Q.Matrix( ...matrix0.rows.reduce( function( resultMatrixRow, row, r ){ - - resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue, c ){ - - // resultMatrixColumn.push( cellValue + matrix1.rows[ r ][ c ]) - resultMatrixColumn.push( cellValue.add( matrix1.rows[ r ][ c ])) - return resultMatrixColumn - - }, [] )) - return resultMatrixRow - - }, [] )) - }, - multiplyScalar: function( matrix, scalar ){ - - if( Q.Matrix.isMatrixLike( matrix ) !== true ){ - - return Q.error( `Q.Matrix attempted to scale something that was not a matrix.` ) - } - if( typeof scalar !== 'number' ){ - - return Q.error( `Q.Matrix attempted to scale this matrix#${matrix.index} by an invalid scalar: ${scalar}.` ) - } - return new Q.Matrix( ...matrix.rows.reduce( function( resultMatrixRow, row ){ - - resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue ){ - - // resultMatrixColumn.push( cellValue * scalar ) - resultMatrixColumn.push( cellValue.multiply( scalar )) - return resultMatrixColumn - - }, [] )) - return resultMatrixRow - - }, [] )) - }, - multiply: function( matrix0, matrix1 ){ - - ` - Two matrices can be multiplied only when - the number of columns in the first matrix - equals the number of rows in the second matrix. - Reminder: Matrix multiplication is not commutative - so the order in which you multiply matters. - - - SEE ALSO - - https://en.wikipedia.org/wiki/Matrix_multiplication - ` - - if( Q.Matrix.isMatrixLike( matrix0 ) !== true || - Q.Matrix.isMatrixLike( matrix1 ) !== true ){ - - return Q.error( `Q.Matrix attempted to multiply something that was not a matrix.` ) - } - if( matrix0.columns.length !== matrix1.rows.length ){ - - return Q.error( `Q.Matrix attempted to multiply Matrix#${matrix0.index}(cols==${matrix0.columns.length}) by Matrix#${matrix1.index}(rows==${matrix1.rows.length}) but their dimensions were not compatible for this.` ) - } - const resultMatrix = [] - matrix0.rows.forEach( function( matrix0Row ){// Each row of THIS matrix - - const resultMatrixRow = [] - matrix1.columns.forEach( function( matrix1Column ){// Each column of OTHER matrix - - const sum = new Q.ComplexNumber() - matrix1Column.forEach( function( matrix1CellValue, index ){// Work down the column of OTHER matrix - - sum.add$( matrix0Row[ index ].multiply( matrix1CellValue )) - }) - resultMatrixRow.push( sum ) - }) - resultMatrix.push( resultMatrixRow ) - }) - //return new Q.Matrix( ...resultMatrix ) - return new this( ...resultMatrix ) - }, - multiplyTensor: function( matrix0, matrix1 ){ - - ` - https://en.wikipedia.org/wiki/Kronecker_product - https://en.wikipedia.org/wiki/Tensor_product - ` - - if( Q.Matrix.isMatrixLike( matrix0 ) !== true || - Q.Matrix.isMatrixLike( matrix1 ) !== true ){ - - return Q.error( `Q.Matrix attempted to tensor something that was not a matrix.` ) - } - - const - resultMatrix = [], - resultMatrixWidth = matrix0.columns.length * matrix1.columns.length, - resultMatrixHeight = matrix0.rows.length * matrix1.rows.length - - for( let y = 0; y < resultMatrixHeight; y ++ ){ - - const resultMatrixRow = [] - for( let x = 0; x < resultMatrixWidth; x ++ ){ - - const - matrix0X = Math.floor( x / matrix0.columns.length ), - matrix0Y = Math.floor( y / matrix0.rows.length ), - matrix1X = x % matrix1.columns.length, - matrix1Y = y % matrix1.rows.length - - resultMatrixRow.push( - - //matrix0.rows[ matrix0Y ][ matrix0X ] * matrix1.rows[ matrix1Y ][ matrix1X ] - matrix0.rows[ matrix0Y ][ matrix0X ].multiply( matrix1.rows[ matrix1Y ][ matrix1X ]) - ) - } - resultMatrix.push( resultMatrixRow ) - } - return new Q.Matrix( ...resultMatrix ) - } -}) - - - - - - - ////////////////////////////// - // // - // Prototype properties // - // // -////////////////////////////// - - -Object.assign( Q.Matrix.prototype, { - - isValidRow: function( r ){ - - return Q.Matrix.isWithinRange( r, 0, this.rows.length - 1 ) - }, - isValidColumn: function( c ){ - - return Q.Matrix.isWithinRange( c, 0, this.columns.length - 1 ) - }, - isValidAddress: function( x, y ){ - - return this.isValidRow( y ) && this.isValidColumn( x ) - }, - getWidth: function(){ - - return Q.Matrix.getWidth( this ) - }, - getHeight: function(){ - - return Q.Matrix.getHeight( this ) - }, - - - - - // Read NON-destructive by nature. (Except quantum reads of course! ROFL!!) - - read: function( x, y ){ - - ` - Equivalent to - this.columns[ x ][ y ] - or - this.rows[ y ][ x ] - but with safety checks. - ` - - if( this.isValidAddress( x, y )) return this.rows[ y ][ x ] - return Q.error( `Q.Matrix could not read from cell address (x=${x}, y=${y}) in matrix#${this.index}.`, this ) - }, - clone: function(){ - - return new Q.Matrix( ...this.rows ) - }, - isEqualTo: function( otherMatrix ){ - - return Q.Matrix.areEqual( this, otherMatrix ) - }, - - - toArray: function(){ - - return this.rows - }, - toXsv: function( rowSeparator, valueSeparator ){ - - return Q.Matrix.toXsv( this, rowSeparator, valueSeparator ) - }, - toCsv: function(){ - - return Q.Matrix.toXsv( this, '\n', ',' ) - }, - toTsv: function(){ - - return Q.Matrix.toXsv( this, '\n', '\t' ) - }, - toHtml: function(){ - - return this.rows.reduce( function( html, row ){ - - return html + row.reduce( function( html, cell ){ - - return html +'\n\t\t' - - }, '\n\t' ) + '\n\t' - - }, '\n
'+ cell.toText() +'
' ) +'\n
' - }, - - - - - // Write is DESTRUCTIVE by nature. Not cuz I hate ya. - - write$: function( x, y, n ){ - - ` - Equivalent to - this.columns[ x ][ y ] = n - or - this.rows[ y ][ x ] = n - but with safety checks. - ` - - if( this.isValidAddress( x, y )){ - - if( Q.ComplexNumber.isNumberLike( n )) n = new Q.ComplexNumber( n ) - if( n instanceof Q.ComplexNumber !== true ) return Q.error( `Attempted to write an invalid value (${n}) to matrix#${this.index} at x=${x}, y=${y}`, this ) - this.rows[ y ][ x ] = n - return this - } - return Q.error( `Invalid cell address for Matrix#${this.index}: x=${x}, y=${y}`, this ) - }, - copy$: function( matrix ){ - - if( Q.Matrix.isMatrixLike( matrix ) !== true ) - return Q.error( `Q.Matrix attempted to copy something that was not a matrix in to this matrix#${matrix.index}.`, this ) - - if( Q.Matrix.haveEqualDimensions( matrix, this ) !== true ) - return Q.error( `Q.Matrix cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this matrix#${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`, this ) - - const that = this - matrix.rows.forEach( function( row, r ){ - - row.forEach( function( n, c ){ - - that.rows[ r ][ c ] = n - }) - }) - return this - }, - fromArray$: function( array ){ - - return this.copy$( Q.Matrix.fromArray( array )) - }, - fromCsv$: function( csv ){ - - return this.copy$( Q.Matrix.fromCsv( csv )) - }, - fromTsv$: function( tsv ){ - - return this.copy$( Q.Matrix.fromTsv( tsv )) - }, - fromHtml$: function( html ){ - - return this.copy$( Q.Matrix.fromHtml( html )) - }, - - - - - // Operate NON-destructive. - - add: function( otherMatrix ){ - - return Q.Matrix.add( this, otherMatrix ) - }, - multiplyScalar: function( scalar ){ - - return Q.Matrix.multiplyScalar( this, scalar ) - }, - multiply: function( otherMatrix ){ - - return Q.Matrix.multiply( this, otherMatrix ) - }, - multiplyTensor: function( otherMatrix ){ - - return Q.Matrix.multiplyTensor( this, otherMatrix ) - }, - - - - - // Operate DESTRUCTIVE. - - add$: function( otherMatrix ){ - - return this.copy$( this.add( otherMatrix )) - }, - multiplyScalar$: function( scalar ){ - - return this.copy$( this.multiplyScalar( scalar )) - } -}) - - - - - - - ////////////////////////// - // // - // Static constants // - // // -////////////////////////// - - -Q.Matrix.createConstants( - - 'IDENTITY_2X2', Q.Matrix.createIdentity( 2 ), - 'IDENTITY_3X3', Q.Matrix.createIdentity( 3 ), - 'IDENTITY_4X4', Q.Matrix.createIdentity( 4 ), - - 'CONSTANT0_2X2', new Q.Matrix( - [ 1, 1 ], - [ 0, 0 ]), - - 'CONSTANT1_2X2', new Q.Matrix( - [ 0, 0 ], - [ 1, 1 ]), - - 'NEGATION_2X2', new Q.Matrix( - [ 0, 1 ], - [ 1, 0 ]), - - 'TEST_MAP_9X9', new Q.Matrix( - [ 11, 21, 31, 41, 51, 61, 71, 81, 91 ], - [ 12, 22, 32, 42, 52, 62, 72, 82, 92 ], - [ 13, 23, 33, 43, 53, 63, 73, 83, 93 ], - [ 14, 24, 34, 44, 54, 64, 74, 84, 94 ], - [ 15, 25, 35, 45, 55, 65, 75, 85, 95 ], - [ 16, 26, 36, 46, 56, 66, 76, 86, 96 ], - [ 17, 27, 37, 47, 57, 67, 77, 87, 97 ], - [ 18, 28, 38, 48, 58, 68, 78, 88, 98 ], - [ 19, 29, 39, 49, 59, 69, 79, 89, 99 ]) -) - - - - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.Qubit = function( a, b, symbol, name ){ - - - // If we’ve received an instance of Q.Matrix as our first argument - // then we’ll assume there are no further arguments - // and just use that matrix as our new Q.Qubit instance. - - if( Q.Matrix.isMatrixLike( a ) && b === undefined ){ - - b = a.rows[ 1 ][ 0 ] - a = a.rows[ 0 ][ 0 ] - } - else { - - - // All of our internal math now uses complex numbers - // rather than Number literals - // so we’d better convert! - - if( typeof a === 'number' ) a = new Q.ComplexNumber( a, 0 ) - if( typeof b === 'number' ) b = new Q.ComplexNumber( b, 0 ) - - - // If we receive undefined (or garbage inputs) - // let’s try to make it useable. - // This way we can always call Q.Qubit with no arguments - // to make a new qubit available for computing with. - - if( a instanceof Q.ComplexNumber !== true ) a = new Q.ComplexNumber( 1, 0 ) - if( b instanceof Q.ComplexNumber !== true ){ - - - // 1 - |𝒂|² = |𝒃|² - // So this does NOT account for if 𝒃 ought to be imaginary or not. - // Perhaps for completeness we could randomly decide - // to flip the real and imaginary components of 𝒃 after this line? - - b = Q.ComplexNumber.ONE.subtract( Math.pow( a.absolute(), 2 )).squareRoot() - } - } - - - // Sanity check! - // Does this constraint hold true? |𝒂|² + |𝒃|² = 1 - - if( Math.pow( a.absolute(), 2 ) + Math.pow( b.absolute(), 2 ) - 1 > Q.EPSILON ) - return Q.error( `Q.Qubit could not accept the initialization values of a=${a} and b=${b} because their squares do not add up to 1.` ) - - Q.Matrix.call( this, [ a ],[ b ]) - this.index = Q.Qubit.index ++ - - - // Convenience getters and setters for this qubit’s - // controll bit and target bit. - - Object.defineProperty( this, 'alpha', { - - get: function(){ return this.rows[ 0 ][ 0 ]}, - set: function( n ){ this.rows[ 0 ][ 0 ] = n } - }) - Object.defineProperty( this, 'beta', { - - get: function(){ return this.rows[ 1 ][ 0 ]}, - set: function( n ){ this.rows[ 1 ][ 0 ] = n } - }) - - - // Used for Dirac notation: |?⟩ - - if( typeof symbol === 'string' ) this.symbol = symbol - if( typeof name === 'string' ) this.name = name - if( this.symbol === undefined || this.name === undefined ){ - - const found = Object.values( Q.Qubit.constants ).find( function( qubit ){ - - return ( - - a.isEqualTo( qubit.alpha ) && - b.isEqualTo( qubit.beta ) - ) - }) - if( found === undefined ){ - - this.symbol = '?' - this.name = 'Unnamed' - } - else { - - if( this.symbol === undefined ) this.symbol = found.symbol - if( this.name === undefined ) this.name = found.name - } - } -} -Q.Qubit.prototype = Object.create( Q.Matrix.prototype ) -Q.Qubit.prototype.constructor = Q.Qubit - - - - -Object.assign( Q.Qubit, { - - index: 0, - help: function(){ return Q.help( this )}, - constants: {}, - createConstant: Q.createConstant, - createConstants: Q.createConstants, - - - - - findBy: function( key, value ){ - - return ( - - Object - .values( Q.Qubit.constants ) - .find( function( item ){ - - if( typeof value === 'string' && - typeof item[ key ] === 'string' ){ - - return value.toLowerCase() === item[ key ].toLowerCase() - } - return value === item[ key ] - }) - ) - }, - findBySymbol: function( symbol ){ - - return Q.Qubit.findBy( 'symbol', symbol ) - }, - findByName: function( name ){ - - return Q.Qubit.findBy( 'name', name ) - }, - findByBeta: function( beta ){ - - if( beta instanceof Q.ComplexNumber === false ){ - - beta = new Q.ComplexNumber( beta ) - } - return Object.values( Q.Qubit.constants ).find( function( qubit ){ - - return qubit.beta.isEqualTo( beta ) - }) - }, - areEqual: function( qubit0, qubit1 ){ - - return ( - - qubit0.alpha.isEqualTo( qubit1.alpha ) && - qubit0.beta.isEqualTo( qubit1.beta ) - ) - }, - collapse: function( qubit ){ - - const - alpha2 = Math.pow( qubit.alpha.absolute(), 2 ), - beta2 = Math.pow( qubit.beta.absolute(), 2 ), - randomNumberRange = Math.pow( 2, 32 ) - 1, - randomNumber = new Uint32Array( 1 ) - - // console.log( 'alpha^2', alpha2 ) - // console.log( 'beta^2', beta2 ) - window.crypto.getRandomValues( randomNumber ) - const randomNumberNormalized = randomNumber / randomNumberRange - if( randomNumberNormalized <= alpha2 ){ - - return new Q.Qubit( 1, 0 ) - } - else return new Q.Qubit( 0, 1 ) - }, - applyGate: function( qubit, gate, ...args ){ - - ` - This is means of inverting what comes first: - the Gate or the Qubit? - If the Gate only operates on a single qubit, - then it doesn’t matter and we can do this: - ` - - if( gate instanceof Q.Gate === false ) return Q.error( `Q.Qubit attempted to apply something that was not a gate to this qubit #${ qubit.index }.` ) - else return gate.applyToQubit( qubit, ...args ) - }, - toText: function( qubit ){ - - //return `|${qubit.beta.toText()}⟩` - return qubit.alpha.toText() +'\n'+ qubit.beta.toText() - }, - toStateVectorText: function( qubit ){ - - return `|${ qubit.beta.toText() }⟩` - }, - toStateVectorHtml: function( qubit ){ - - return `${ qubit.beta.toText() }` - }, - - - - // This code was a pain in the ass to figure out. - // I’m not fluent in trigonometry - // and none of the quantum primers actually lay out - // how to convert arbitrary qubit states - // to Bloch Sphere representation. - // Oh, they provide equivalencies for specific states, sure. - // I hope this is useful to you - // unless you are porting this to a terrible language - // like C# or Java or something ;) - - toBlochSphere: function( qubit ){ - - ` - Based on this qubit’s state return the - Polar angle θ (theta), - azimuth angle Ï• (phi), - Bloch vector, - corrected surface coordinate. - - https://en.wikipedia.org/wiki/Bloch_sphere - ` - - - // Polar angle θ (theta). - - const theta = Q.ComplexNumber.arcCosine( qubit.alpha ).multiply( 2 ) - if( isNaN( theta.real )) theta.real = 0 - if( isNaN( theta.imaginary )) theta.imaginary = 0 - - - // Azimuth angle Ï• (phi). - - const phi = Q.ComplexNumber.log( - - qubit.beta.divide( Q.ComplexNumber.sine( theta.divide( 2 ))) - ) - .divide( Q.ComplexNumber.I ) - if( isNaN( phi.real )) phi.real = 0 - if( isNaN( phi.imaginary )) phi.imaginary = 0 - - - // Bloch vector. - - const vector = { - - x: Q.ComplexNumber.sine( theta ).multiply( Q.ComplexNumber.cosine( phi )).real, - y: Q.ComplexNumber.sine( theta ).multiply( Q.ComplexNumber.sine( phi )).real, - z: Q.ComplexNumber.cosine( theta ).real - } - - - // Bloch vector’s axes are wonked. - // Let’s “correct” them for use with Three.js, etc. - - const position = { - - x: vector.y, - y: vector.z, - z: vector.x - } - - return { - - - // Wow does this make tweening easier down the road. - - alphaReal: qubit.alpha.real, - alphaImaginary: qubit.alpha.imaginary, - betaReal: qubit.beta.real, - betaImaginary: qubit.beta.imaginary, - - - // Ummm... I’m only returnig the REAL portions. Please forgive me! - - theta: theta.real, - phi: phi.real, - vector, // Wonked YZX vector for maths because maths. - position// Un-wonked XYZ for use by actual 3D engines. - } - }, - fromBlochVector: function( x, y, z ){ - - - //basically from a Pauli Rotation - } - -}) - - - - -Q.Qubit.createConstants( - - - // Opposing pairs: - // |H⟩ and |V⟩ - // |D⟩ and |A⟩ - // |R⟩ and |L⟩ - - 'HORIZONTAL', new Q.Qubit( 1, 0, 'H', 'Horizontal' ),// ZERO. - 'VERTICAL', new Q.Qubit( 0, 1, 'V', 'Vertical' ),// ONE. - 'DIAGONAL', new Q.Qubit( Math.SQRT1_2, Math.SQRT1_2, 'D', 'Diagonal' ), - 'ANTI_DIAGONAL', new Q.Qubit( Math.SQRT1_2, -Math.SQRT1_2, 'A', 'Anti-diagonal' ), - 'RIGHT_HAND_CIRCULAR_POLARIZED', new Q.Qubit( Math.SQRT1_2, new Q.ComplexNumber( 0, -Math.SQRT1_2 ), 'R', 'Right-hand Circular Polarized' ),// RHCP - 'LEFT_HAND_CIRCULAR_POLARIZED', new Q.Qubit( Math.SQRT1_2, new Q.ComplexNumber( 0, Math.SQRT1_2 ), 'L', 'Left-hand Circular Polarized' ) // LHCP -) - - - - -Object.assign( Q.Qubit.prototype, { - - copy$: function( matrix ){ - - if( Q.Matrix.isMatrixLike( matrix ) !== true ) - return Q.error( `Q.Qubit attempted to copy something that was not a matrix in this qubit #${qubit.index}.`, this ) - - if( Q.Matrix.haveEqualDimensions( matrix, this ) !== true ) - return Q.error( `Q.Qubit cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this qubit #${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`, this ) - - const that = this - matrix.rows.forEach( function( row, r ){ - - row.forEach( function( n, c ){ - - that.rows[ r ][ c ] = n - }) - }) - this.dirac = matrix.dirac - return this - }, - clone: function(){ - - return new Q.Qubit( this.alpha, this.beta ) - }, - isEqualTo: function( otherQubit ){ - - return Q.Qubit.areEqual( this, otherQubit )// Returns a Boolean, breaks function chaining! - }, - collapse: function(){ - - return Q.Qubit.collapse( this ) - }, - applyGate: function( gate, ...args ){ - - return Q.Qubit.applyGate( this, gate, ...args ) - }, - toText: function(){ - - return Q.Qubit.toText( this )// Returns a String, breaks function chaining! - }, - toStateVectorText: function(){ - - return Q.Qubit.toStateVectorText( this )// Returns a String, breaks function chaining! - }, - toStateVectorHtml: function(){ - - return Q.Qubit.toStateVectorHtml( this )// Returns a String, breaks function chaining! - }, - toBlochSphere: function(){ - - return Q.Qubit.toBlochSphere( this )// Returns an Object, breaks function chaining! - }, - collapse$: function(){ - - return this.copy$( Q.Qubit.collapse( this )) - }, - applyGate$: function( gate ){ - - return this.copy$( Q.Qubit.applyGate( this, gate )) - }, -}) - - - - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.Gate = function( params ){ - - Object.assign( this, params ) - this.index = Q.Gate.index ++ - - if( typeof this.symbol !== 'string' ) this.symbol = '?' - if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase() - const parameters = Object.assign( {}, params.parameters ) - this.parameters = parameters - - // We use symbols as unique identifiers - // among gate CONSTANTS - // so if you use the same symbol for a non-constant - // that’s not a deal breaker - // but it is good to know. - - const - scope = this, - foundConstant = Object - .values( Q.Gate.constants ) - .find( function( gate ){ - - return gate.symbol === scope.symbol - }) - - //Muting this warning in order to have parameterized gates (that don't totally mess with the constants), we need - //to make clones of the constants...a lot if you're using a lot of parameterized gates. Warning gets annoying :/. - // if( foundConstant ){ - - // Q.warn( `Q.Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant ) - // } - - if( typeof this.name !== 'string' ) this.name = 'Unknown' - if( typeof this.nameCss !== 'string' ) this.nameCss = 'unknown' - - - // If our gate’s matrix is to be - // dynamically created or updated - // then we ouoght to do that now. - - if( typeof this.updateMatrix$ === 'function' ) this.updateMatrix$() - - - // Every gate must have an applyToQubit method. - // If it doesn’t exist we’ll create one - // based on whether a matrix property exists or not. - - if( typeof this.applyToQubit !== 'function' ){ - - if( this.matrix instanceof Q.Matrix === true ){ - - this.applyToQubit = function( qubit ){ - - return new Q.Qubit( this.matrix.multiply( qubit )) - } - } - else { - - this.applyToQubit = function( qubit ){ return qubit } - } - } -} - - - - -Object.assign( Q.Gate, { - - index: 0, - constants: {}, - createConstant: Q.createConstant, - createConstants: Q.createConstants, - findBy: function( key, value ){ - - return ( - - Object - .values( Q.Gate.constants ) - .find( function( item ){ - - if( typeof value === 'string' && - typeof item[ key ] === 'string' ){ - - return value.toLowerCase() === item[ key ].toLowerCase() - } - return value === item[ key ] - }) - ) - }, - findBySymbol: function( symbol ){ - - return Q.Gate.findBy( 'symbol', symbol ) - }, - findByName: function( name ){ - - return Q.Gate.findBy( 'name', name ) - } -}) - - - - -Object.assign( Q.Gate.prototype, { - - clone: function( params ){ - - return new Q.Gate( Object.assign( {}, this, params )) - }, - applyToQubits: function(){ - - return Array.from( arguments ).map( this.applyToQubit.bind( this )) - }, - set$: function( key, value ){ - - this[ key ] = value - return this - }, - setSymbol$: function( value ){ - - return this.set$( 'symbol', value ) - } -}) - - - - -Q.Gate.createConstants( - - - // Operate on a single qubit. - - 'IDENTITY', new Q.Gate({ - - symbol: 'I', - symbolAmazonBraket: 'i', - symbolSvg: '', - name: 'Identity', - nameCss: 'identity', - matrix: Q.Matrix.IDENTITY_2X2 - }), - 'CURSOR', new Q.Gate({ - - symbol: '*', - symbolAmazonBraket: 'i', - symbolSvg: '', - name: 'Identity', - nameCss: 'identity', - matrix: Q.Matrix.IDENTITY_2X2 - }), - 'MEASURE', new Q.Gate({ - - symbol: 'M', - symbolAmazonBraket: 'm', - symbolSvg: '', - name: 'Measure', - nameCss: 'measure', - matrix: Q.Matrix.IDENTITY_2X2, - applyToQubit: function( state ){} - }), - 'HADAMARD', new Q.Gate({ - - symbol: 'H', - symbolAmazonBraket: 'h', - symbolSvg: '', - name: 'Hadamard', - nameCss: 'hadamard', - matrix: new Q.Matrix( - [ Math.SQRT1_2, Math.SQRT1_2 ], - [ Math.SQRT1_2, -Math.SQRT1_2 ]) - }), - 'PAULI_X', new Q.Gate({ - - symbol: 'X', - symbolAmazonBraket: 'x', - symbolSvg: '', - name: 'Pauli X', - nameCss: 'pauli-x', - matrix: new Q.Matrix( - [ 0, 1 ], - [ 1, 0 ]), - //ltnln: NOTE! can_be_controlled refers to whether or not the Braket SDK supports a controlled - //application of this gate; if we want Q to be able to support controlled gated regardless of whether - //or not Braket can, this must be changed.. - can_be_controlled: true - }, - ), - 'PAULI_Y', new Q.Gate({ - - symbol: 'Y', - symbolAmazonBraket: 'y', - symbolSvg: '', - name: 'Pauli Y', - nameCss: 'pauli-y', - matrix: new Q.Matrix( - [ 0, new Q.ComplexNumber( 0, -1 )], - [ new Q.ComplexNumber( 0, 1 ), 0 ]), - can_be_controlled: true - }, - ), - 'PAULI_Z', new Q.Gate({ - - symbol: 'Z', - symbolAmazonBraket: 'z', - symbolSvg: '', - name: 'Pauli Z', - nameCss: 'pauli-z', - matrix: new Q.Matrix( - [ 1, 0 ], - [ 0, -1 ]), - can_be_controlled: true - }, - ), - 'PHASE', new Q.Gate({ - - symbol: 'P', - symbolAmazonBraket: 'phaseshift',// ltnln edit: change from 'p' to 'phaseshift' - symbolSvg: '', - name: 'Phase', - nameCss: 'phase', - parameters: { "phi" : 1 }, - updateMatrix$: function( phi ){ - if( Q.isUsefulNumber( phi ) === true ) this.parameters[ "phi" ] = phi - this.matrix = new Q.Matrix( - [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters["phi"] ))]) - return this - }, - applyToQubit: function( qubit, phi ){ - - if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi ))]) - return new Q.Qubit( matrix.multiply( qubit )) - }, - can_be_controlled: true, - has_parameters: true - }), - 'PI_8', new Q.Gate({ - - symbol: 'T', - symbolAmazonBraket: 't',// !!! Double check this !!! - symbolSvg: '', - name: 'π ÷ 8', - nameCss: 'pi8', - matrix: new Q.Matrix( - [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, Math.PI / 4 )) ]) - }), - 'BLOCH', new Q.Gate({ - - symbol: 'B', - //symbolAmazonBraket: Does not exist. - symbolSvg: '', - name: 'Bloch sphere', - nameCss: 'bloch', - applyToQubit: function( qubit ){ - - // Create Bloch sphere visualizer instance. - } - }), - 'RX', new Q.Gate({ - - symbol: 'Rx', - symbolAmazonBraket: 'rx', - symbolSvg: '', - name: 'X Rotation', - nameCss: 'x-rotation', - parameters: { "phi" : Math.PI / 2 }, - updateMatrix$: function( phi ){ - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ], - [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 )]) - return this - }, - applyToQubit: function( qubit, phi ){ - - if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ], - [ new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 )]) - return new Q.Qubit( matrix.multiply( qubit )) - }, - has_parameters: true - }), - 'RY', new Q.Gate({ - - symbol: 'Ry', - symbolAmazonBraket: 'ry', - symbolSvg: '', - name: 'Y Rotation', - nameCss: 'y-rotation', - parameters: { "phi" : Math.PI / 2 }, - updateMatrix$: function( phi ){ - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ Math.cos( this.parameters[ "phi" ] / 2 ), -Math.sin( phi / 2 ) ], - [ Math.sin( this.parameters[ "phi" ] / 2 ), Math.cos( this.parameters[ "phi" ] / 2 )]) - return this - }, - applyToQubit: function( qubit, phi ){ - - if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ Math.cos( phi / 2 ), -Math.sin( phi / 2 ) ], - [ Math.sin( phi / 2 ), Math.cos( phi / 2 )]) - return new Q.Qubit( matrix.multiply( qubit )) - }, - has_parameters: true - }), - 'RZ', new Q.Gate({ - - symbol: 'Rz', - symbolAmazonBraket: 'rz', - symbolSvg: '', - name: 'Z Rotation', - nameCss: 'z-rotation', - parameters: { "phi" : Math.PI / 2 }, - updateMatrix$: function( phi ){ - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )), 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 ))]) - return this - }, - applyToQubit: function( qubit, phi ){ - - if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -phi / 2 )), 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 ))]) - return new Q.Qubit( matrix.multiply( qubit )) - }, - has_parameters: true - }), - 'UNITARY', new Q.Gate({ - - symbol: 'U', - symbolAmazonBraket: 'unitary', - symbolSvg: '', - name: 'Unitary', - nameCss: 'unitary', - //toAmazonBraket will have to use the following matrix as an argument for unitary() - parameters: { "phi" : Math.PI / 2, - "theta" : Math.PI / 2, - "lambda" : Math.PI / 2 }, - updateMatrix$: function( phi, theta, lambda ){ - //if all are valid, update; otherwise, update none. - if( (Q.isUsefulNumber( +phi ) === true) && (Q.isUsefulNumber( +theta ) === true) && (Q.isUsefulNumber( +lambda ) === true) ) { - this.parameters[ "phi" ] = +phi; - this.parameters[ "theta" ] = +theta; - this.parameters[ "lambda" ] = +lambda; - } - const a = Q.ComplexNumber.multiply( - Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 )) - const b = Q.ComplexNumber.multiply( - Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), -Math.sin( this.parameters[ "theta" ] / 2 )) - const c = Q.ComplexNumber.multiply( - Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), Math.sin( this.parameters[ "theta" ] / 2 )) - const d = Q.ComplexNumber.multiply( - Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 )) - this.matrix = new Q.Matrix( - [ a, b ], - [ c, d ]) - return this - }, - applyToQubit: function( qubit, phi, theta, lambda ){ - if( Q.isUsefulNumber( phi ) === true ) phi = this.parameters[ "phi" ] - if( Q.isUsefulNumber( theta ) === true ) theta = this.parameters[ "theta" ] - if( Q.isUsefulNumber( lambda ) === true ) lambda = this.parameters[ "lambda" ] - const a = Q.ComplexNumber.multiply( - Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( phi + lambda ) / 2 )), Math.cos( theta / 2 )); - const b = Q.ComplexNumber.multiply( - Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( phi - lambda ) / 2 )), -Math.sin( theta / 2 )); - const c = Q.ComplexNumber.multiply( - Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( phi - lambda ) / 2 )), Math.sin( theta / 2 )); - const d = Q.ComplexNumber.multiply( - Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( phi + lambda ) / 2 )), Math.cos( theta / 2 )); - const matrix = new Q.Matrix( - [ a, b ], - [ c, d ]) - return new Q.Qubit( matrix.multiply( qubit )) - }, - has_parameters: true - }), - 'NOT1_2', new Q.Gate({ - - symbol: 'V', - symbolAmazonBraket: 'v', - symbolSvg: '', - name: '√Not', - nameCss: 'not1-2', - matrix: new Q.Matrix( - [ new Q.ComplexNumber( 1, 1 ) / 2, new Q.ComplexNumber( 1, -1 ) / 2 ], - [ new Q.ComplexNumber( 1, -1 ) / 2, new Q.ComplexNumber( 1, 1 ) / 2 ]) - }), - 'PI_8_Dagger', new Q.Gate({ - - symbol: 'T†', - symbolAmazonBraket: 'ti', - symbolSvg: '', - name: 'PI_8_Dagger', - nameCss: 'pi8-dagger', - matrix: new Q.Matrix( - [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -Math.PI / 4 )) ]) - }), - 'NOT1_2_Dagger', new Q.Gate({ - - symbol: 'V†', - symbolAmazonBraket: 'vi', - symbolSvg: '', - name: '√Not_Dagger', - nameCss: 'not1-2-dagger', - matrix: new Q.Matrix( - [ new Q.ComplexNumber( 1, -1 ) / 2, new Q.ComplexNumber( 1, 1 ) / 2 ], - [ new Q.ComplexNumber( 1, 1 ) / 2, new Q.ComplexNumber( 1, -1 ) / 2 ]) - }), - //Note that S, S_Dagger, PI_8, and PI_8_Dagger can all be implemented by applying the PHASE gate - //using certain values of phi. - //These gates are included for completeness. - 'S', new Q.Gate({ - symbol: 'S*', //Gotta think of a better symbol name... - symbolAmazonBraket: 's', - symbolSvg: '', - name: 'π ÷ 4', - nameCss: 'pi4', - matrix: new Q.Matrix( - [ 1, 0 ], - [ 0, new Q.ComplexNumber( 0, 1 ) ]) - }), - 'S_Dagger', new Q.Gate({ - - symbol: 'S†', - symbolAmazonBraket: 'si', - symbolSvg: '', - name: 'π ÷ 4 Dagger', - nameCss: 'pi4-dagger', - matrix: new Q.Matrix( - [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -1 )) ]) - }), - // Operate on 2 qubits. - 'SWAP', new Q.Gate({ - - symbol: 'S', - symbolAmazonBraket: 'swap', - symbolSvg: '', - name: 'Swap', - nameCss: 'swap', - parameters: { "phi" : 0.0 }, - updateMatrix$: function( phi ) { - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ], - [ 0, Q.ComplexNumber.E.power(new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ], - [ 0, 0, 0, 1 ]) - return this - }, - applyToQubit: function( qubit, phi ) { - - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi )), 0 ], - [ 0, new Q.ComplexNumber( 0, 1 ), 0, 0 ], - [ 0, 0, 0, 1 ] - ) - return new Q.Qubit( matrix.multiply( qubit )) - }, - can_be_controlled: true, - has_parameters: true, - is_multi_qubit: true - }), - 'SWAP1_2', new Q.Gate({ - - symbol: '√S', - //symbolAmazonBraket: !!! UNKNOWN !!! - symbolSvg: '', - name: '√Swap', - nameCss: 'swap1-2', - matrix: new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, new Q.ComplexNumber( 0.5, 0.5 ), new Q.ComplexNumber( 0.5, -0.5 ), 0 ], - [ 0, new Q.ComplexNumber( 0.5, -0.5 ), new Q.ComplexNumber( 0.5, 0.5 ), 0 ], - [ 0, 0, 0, 1 ]), - is_multi_qubit: true - }), - 'ISWAP', new Q.Gate({ - - symbol: 'iS', - symbolAmazonBraket: 'iswap', - symbolSvg: '', - name: 'Imaginary Swap', - nameCss: 'iswap', - matrix: new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, 0, new Q.ComplexNumber( 0, 1 ), 0 ], - [ 0, new Q.ComplexNumber( 0, 1 ), 0, 0 ], - [ 0, 0, 0, 1 ]), - is_multi_qubit: true - }), - 'ISING-XX', new Q.Gate({ - - symbol: 'XX', - symbolAmazonBraket: 'xx', - symbolSvg: '', - name: 'Ising XX Coupling', - nameCss: 'ising-xx-coupling', - parameters: { "phi" : 1 }, - updateMatrix$: function( phi ) { - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ], - [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], - [ 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], - [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ]) - return this - }, - applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ Math.cos( phi / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ], - [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ], - [ 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], - [ new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ] - ) - return new Q.Qubit( matrix.multiply( qubit )) - }, - is_multi_qubit: true, - has_parameters: true - }), - 'ISING-XY', new Q.Gate({ - - symbol: 'XY', - symbolAmazonBraket: 'xy', - symbolSvg: '', - name: 'Ising XY Coupling', - nameCss: 'ising-xy-coupling', - parameters: { "phi" : 1 }, - updateMatrix$: function( phi ) { - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], - [ 0, new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], - [ 0, 0, 0, 1 ]) - return this - }, - applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, Math.sin( phi / 2 )), 0 ], - [ 0, new Q.ComplexNumber( 0, Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], - [ 0, 0, 0, 1 ] - ) - return new Q.Qubit( matrix.multiply( qubit )) - }, - is_multi_qubit: true, - has_parameters: true - }), - 'ISING-YY', new Q.Gate({ - - symbol: 'YY', - symbolAmazonBraket: 'yy', - symbolSvg: '', - name: 'Ising YY Coupling', - nameCss: 'ising-yy-coupling', - parameters: { "phi" : 1 }, - updateMatrix$: function( phi ) { - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )) ], - [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], - [ 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], - [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ]) - return this - }, - applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ Math.cos( phi / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ], - [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ], - [ 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], - [ new Q.ComplexNumber( 0, Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ] - ) - return new Q.Qubit( matrix.multiply( qubit )) - }, - is_multi_qubit: true, - has_parameters: true - }), - 'ISING-ZZ', new Q.Gate({ - - symbol: 'ZZ', - symbolAmazonBraket: 'zz', - symbolSvg: '', - name: 'Ising ZZ Coupling', - nameCss: 'ising-zz-coupling', - parameters: { "phi" : 1 }, - updateMatrix$: function( phi ) { - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0 ], - [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0], - [ 0, 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )) ]) - return this - }, - applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0, 0, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0, 0 ], - [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0], - [ 0, 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -phi / 2 )) ] - ) - return new Q.Qubit( matrix.multiply( qubit )) - }, - is_multi_qubit: true, - has_parameters: true - }), - 'CPhase00', new Q.Gate({ - - symbol: '00', //placeholder - symbolAmazonBraket: 'cphaseshift00', - symbolSvg: '', - name: 'Controlled Phase Shift 00', - nameCss: 'cphase00', - parameters: { "phi" : 1 }, - updateMatrix$: function( phi ) { - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0, 0 ], - [ 0, 1, 0, 0 ], - [ 0, 0, 1, 0 ], - [ 0, 0, 0, 1 ]) - return this - }, - applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi )), 0, 0, 0 ], - [ 0, 1, 0, 0 ], - [ 0, 0, 1, 0 ], - [ 0, 0, 0, 1 ] - ) - return new Q.Qubit( matrix.multiply( qubit )) - }, - is_multi_qubit: true, - has_parameters: true - }), - 'CPhase01', new Q.Gate({ - - symbol: '01', //placeholder - symbolAmazonBraket: 'cphaseshift01', - symbolSvg: '', - name: 'Controlled Phase Shift 01', - nameCss: 'cphase01', - parameters: { "phi" : 1 }, - updateMatrix$: function( phi ) { - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ], - [ 0, 0, 1, 0 ], - [ 0, 0, 0, 1 ]) - return this - }, - applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0, 0 ], - [ 0, 0, 1, 0 ], - [ 0, 0, 0, 1 ] - ) - return new Q.Qubit( matrix.multiply( qubit )) - }, - is_multi_qubit: true, - has_parameters: true - }), - 'CPhase10', new Q.Gate({ - - symbol: '10', //placeholder - symbolAmazonBraket: 'cphaseshift10', - symbolSvg: '', - name: 'Controlled Phase Shift 10', - nameCss: 'cphase01', - parameters: { "phi" : 1 }, - updateMatrix$: function( phi ) { - - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, 1, 0, 0 ], - [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ], - [ 0, 0, 0, 1 ]) - return this - }, - applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, 1, 0, 0 ], - [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0 ], - [ 0, 0, 0, 1 ] - ) - return new Q.Qubit( matrix.multiply( qubit )) - }, - is_multi_qubit: true, - has_parameters: true - }), - 'CSWAP', new Q.Gate({ - - symbol: 'CSWAP', - symbolAmazonBraket: 'cswap', - symbolSvg: '', - name: 'Controlled Swap', - nameCss: 'controlled-swap', - matrix: new Q.Matrix( - [1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1] - ) - }) - /* - - - All further gates, - such as Toffoli (CCNOT) - or Fredkin (CSWAP) - can be easily constructed - from the above gates - using Q conveniences. - - - */ -) - - - - - - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.History = function( instance ){ - - this.instance = instance - this.entries = [[{ - - redo: {}, - undo: [{}] - }]] - this.index = 0 - this.isRecording = true -} - - - - -Object.assign( Q.History.prototype, { - - assess: function(){ - - const instance = this.instance - if( this.index > 0 ){ - - window.dispatchEvent( new CustomEvent( - - 'Q.History undo is capable', { detail: { instance }} - )) - } - else { - - window.dispatchEvent( new CustomEvent( - - 'Q.History undo is depleted', { detail: { instance }} - )) - } - if( this.index + 1 < this.entries.length ){ - - window.dispatchEvent( new CustomEvent( - - 'Q.History redo is capable', { detail: { instance }} - )) - } - else { - - window.dispatchEvent( new CustomEvent( - - 'Q.History redo is depleted', { detail: { instance }} - )) - } - return this - }, - createEntry$: function(){ - - this.entries.splice( this.index + 1 ) - this.entries.push([]) - this.index = this.entries.length - 1 - }, - record$: function( entry ){ - - - // Are we recording this history? - // Usually, yes. - // But if our history state is “playback” - // then we will NOT record this. - - if( this.isRecording ){ - - this.entries[ this.index ].push( entry ) - this.index = this.entries.length - 1 - this.assess() - } - return this - }, - step$: function( direction ){ - - - // If we are stepping backward (undo) - // we cannot go back further than index === 0 - // which we would happen if index is already 0 - // before we subtract 1. - // Similarly, if stepping forward (redo) - // we cannot go further than index === entries.length - 1 - // which would happen if the index is already entries.length - // before we add 1. - - if( - ( direction < 0 && this.index < 1 ) || - ( direction > 0 && this.index > this.entries.length - 2 ) - ) return false - - - // Before we step backward (undo) or forward (redo) - // we need to turn OFF history recording. - - this.isRecording = false - - const - instance = this.instance, - command = direction < 0 ? 'undo' : 'redo' - - - // If we are stepping forward (redo) - // then we need to advance the history index - // BEFORE we execute. - - if( direction > 0 ) this.index ++ - - - // Take this history entry, which itself is an Array. - // It may contain several tasks. - // Put my thing down, flip and reverse it. - // .ti esrever dna pilf ,nwod gniht ym tuP - - const entry = direction > 0 ? - Array.from( this.entries[ this.index ]) : - Array.from( this.entries[ this.index ]).reverse() - - entry - .reduce( function( tasks, subentry, s ){ - - return tasks.concat( subentry[ command ]) - - }, [] ) - .forEach( function( task, i ){ - - if( typeof task.func === 'function' ){ - - task.func.apply( instance, task.args ) - } - }) - - - // If we are stepping backward (undo) - // then we decrement the history index - // AFTER the execution above. - - if( direction < 0 ) this.index -- - - - // It’s now safe to turn recording back on. - - this.isRecording = true - - - // Emit an event so the GUI or anyone else listening - // can know if we have available undo or redo commands - // based on where or index is. - - this.assess() - return true - }, - undo$: function(){ return this.step$( -1 )}, - redo$: function(){ return this.step$( 1 )}, - report: function(){ - - const argsParse = function( output, entry, i ){ - - if( i > 0 ) output += ', ' - return output + ( typeof entry === 'object' && entry.name ? entry.name : entry ) - } - return this.entries.reduce( function( output, entry, i ){ - - output += '\n\n'+ i + ' ════════════════════════════════════════'+ - entry.reduce( function( output, entry, i ){ - - output += '\n\n '+ i +' ────────────────────────────────────────\n' - if( entry.redo ){ - - output += '\n ⟳ Redo ── '+ entry.redo.name +' ' - if( entry.redo.args ) output += entry.redo.args.reduce( argsParse, '' ) - } - output += entry.undo.reduce( function( output, entry, i ){ - - output += '\n ⟲ Undo '+ i +' ── '+ entry.name +' ' - if( entry.args ) output += entry.args.reduce( argsParse, '' ) - return output - - }, '' ) - - return output - - }, '' ) - return output - - }, 'History entry cursor: '+ this.index ) - } -}) - - - - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.Circuit = function( bandwidth, timewidth ){ - - - // What number Circuit is this - // that we’re attempting to make here? - - this.index = Q.Circuit.index ++ - - - // How many qubits (registers) shall we use? - - if( !Q.isUsefulInteger( bandwidth )) bandwidth = 3 - this.bandwidth = bandwidth - - - // How many operations can we perform on each qubit? - // Each operation counts as one moment; one clock tick. - - if( !Q.isUsefulInteger( timewidth )) timewidth = 5 - this.timewidth = timewidth - - - // We’ll start with Horizontal qubits (zeros) as inputs - // but we can of course modify this after initialization. - - this.qubits = new Array( bandwidth ).fill( Q.Qubit.HORIZONTAL ) - - - // What operations will we perform on our qubits? - - this.operations = [] - - - // Does our circuit need evaluation? - // Certainly, yes! - // (And will again each time it is modified.) - - this.needsEvaluation = true - - - // When our circuit is evaluated - // we store those results in this array. - - this.results = [] - this.matrix = null - - - // Undo / Redo history. - - this.history = new Q.History( this ) -} - - - - -Object.assign( Q.Circuit, { - - index: 0, - help: function(){ return Q.help( this )}, - constants: {}, - createConstant: Q.createConstant, - createConstants: Q.createConstants, - - - fromText: function( text ){ - - - // This is a quick way to enable `fromText()` - // to return a default new Q.Circuit(). - - if( text === undefined ) return new Q.Circuit() - - // Is this a String Template -- as opposed to a regular String? - // If so, let’s convert it to a regular String. - // Yes, this maintains the line breaks. - - if( text.raw !== undefined ) text = ''+text.raw - return Q.Circuit.fromTableTransposed( - - text - .trim() - .split( /\r?\n/ ) - .filter( function( item ){ return item.length }) - .map( function( item, r ){ - - return item - .trim() - .split( /[-+\s+=+]/ ) - .filter( function( item ){ return item.length }) - .map( function( item, m ){ - - //const matches = item.match( /(^\w+)(#(\w+))*(\.(\d+))*/ ) - const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ ) - return { - - gateSymbol: matches[ 1 ], - operationMomentId: matches[ 3 ], - mappingIndex: +matches[ 5 ] - } - }) - }) - ) - }, - - - - - - - - - - - - - - - -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// Working out a new syntax here... Patience please! - - - fromText2: function( text ){ - - - text = ` - H C C - I C1 C1 - I X1 S1 - I X1 S1` - - - // This is a quick way to enable `fromText()` - // to return a default new Q.Circuit(). - - if( text === undefined ) return new Q.Circuit() - - - // Is this a String Template -- as opposed to a regular String? - // If so, let’s convert it to a regular String. - // Yes, this maintains the line breaks. - - if( text.raw !== undefined ) text = ''+text.raw - - - - text - .trim() - .split( /\r?\n/ ) - .filter( function( item ){ return item.length }) - .map( function( item, r ){ - - return item - .trim() - .split( /[-+\s+=+]/ ) - .filter( function( item ){ return item.length }) - .map( function( item, m ){ - - // +++++++++++++++++++++++ - // need to map LETTER[] optional NUMBER ] - - const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ ) - - //const matches = item.match( /(^\w+)(#(\w+))*(\.(\d+))*/ ) - // const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ ) - // return { - - // gateSymbol: matches[ 1 ], - // operationMomentId: matches[ 3 ], - // mappingIndex: +matches[ 5 ] - // } - }) - }) - - }, - - - -//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - - - - - - - - - - - - fromTableTransposed: function( table ){ - const - bandwidth = table.length, - timewidth = table.reduce( function( max, moments ){ - - return Math.max( max, moments.length ) - - }, 0 ), - circuit = new Q.Circuit( bandwidth, timewidth ) - - circuit.bandwidth = bandwidth - circuit.timewidth = timewidth - for( let r = 0; r < bandwidth; r ++ ){ - - const registerIndex = r + 1 - for( let m = 0; m < timewidth; m ++ ){ - - const - momentIndex = m + 1, - operation = table[ r ][ m ] - let siblingHasBeenFound = false - for( let s = 0; s < r; s ++ ){ - - const sibling = table[ s ][ m ] - if( operation.gateSymbol === sibling.gateSymbol && - operation.operationMomentId === sibling.operationMomentId && - Q.isUsefulInteger( operation.mappingIndex ) && - Q.isUsefulInteger( sibling.mappingIndex ) && - operation.mappingIndex !== sibling.mappingIndex ){ - - - // We’ve found a sibling ! - const operationsIndex = circuit.operations.findIndex( function( operation ){ - - return ( - - operation.momentIndex === momentIndex && - operation.registerIndices.includes( s + 1 ) - ) - }) - // console.log( 'operationsIndex?', operationsIndex ) - circuit.operations[ operationsIndex ].registerIndices[ operation.mappingIndex ] = registerIndex - circuit.operations[ operationsIndex ].isControlled = operation.gateSymbol != '*'// Q.Gate.SWAP. - siblingHasBeenFound = true - } - } - if( siblingHasBeenFound === false && operation.gateSymbol !== 'I' ){ - - const - gate = Q.Gate.findBySymbol( operation.gateSymbol ), - registerIndices = [] - - if( Q.isUsefulInteger( operation.mappingIndex )){ - - registerIndices[ operation.mappingIndex ] = registerIndex - } - else registerIndices[ 0 ] = registerIndex - circuit.operations.push({ - - gate, - momentIndex, - registerIndices, - isControlled: false, - operationMomentId: operation.operationMomentId - }) - } - } - } - circuit.sort$() - return circuit - }, - - - - - controlled: function( U ){ - - - // we should really just replace this with a nice Matrix.copy({}) command!!!! - - // console.log( 'U?', U ) - - const - size = U.getWidth(), - result = Q.Matrix.createIdentity( size * 2 ) - - // console.log( 'U', U.toTsv() ) - // console.log( 'size', size ) - // console.log( 'result', result.toTsv() ) - - for( let x = 0; x < size; x ++ ){ - - for( let y = 0; y < size; y ++ ){ - - const v = U.read( x, y ) - // console.log( `value at ${x}, ${y}`, v ) - result.write$( x + size, y + size, v ) - } - } - return result - }, - - - - // Return transformation over entire nqubit register that applies U to - // specified qubits (in order given). - // Algorithm from Lee Spector's "Automatic Quantum Computer Programming" - // Page 21 in the 2004 PDF? - // http://148.206.53.84/tesiuami/S_pdfs/AUTOMATIC%20QUANTUM%20COMPUTER%20PROGRAMMING.pdf - - expandMatrix: function( circuitBandwidth, U, qubitIndices ){ - - // console.log( 'EXPANDING THE MATRIX...' ) - // console.log( 'this one: U', U.toTsv()) - - const _qubits = [] - const n = Math.pow( 2, circuitBandwidth ) - - - // console.log( 'qubitIndices used by this operation:', qubitIndices ) - // console.log( 'qubits before slice', qubitIndices ) - // qubitIndices = qubitIndices.slice( 0 ) - // console.log( 'qubits AFTER slice', qubitIndices ) - - - - - for( let i = 0; i < qubitIndices.length; i ++ ){ - - //qubitIndices[ i ] = ( circuitBandwidth - 1 ) - qubitIndices[ i ] - qubitIndices[ i ] = ( circuitBandwidth - 0 ) - qubitIndices[ i ] - } - // console.log( 'qubits AFTER manipulation', qubitIndices ) - - - qubitIndices.reverse() - for( let i = 0; i < circuitBandwidth; i ++ ){ - - if( qubitIndices.indexOf( i ) == -1 ){ - - _qubits.push( i ) - } - } - - - // console.log( 'qubitIndices vs _qubits:' ) - // console.log( 'qubitIndices', qubitIndices ) - // console.log( '_qubits', _qubits ) - - - - const result = new Q.Matrix.createZero( n ) - - - // const X = numeric.rep([n, n], 0); - // const Y = numeric.rep([n, n], 0); - - - let i = n - while( i -- ){ - - let j = n - while( j -- ){ - - let - bitsEqual = true, - k = _qubits.length - - while( k -- ){ - - if(( i & ( 1 << _qubits[ k ])) != ( j & ( 1 << _qubits[ k ]))){ - - bitsEqual = false - break - } - } - if( bitsEqual ){ - - // console.log( 'bits ARE equal' ) - let - istar = 0, - jstar = 0, - k = qubitIndices.length - - while( k -- ){ - - const q = qubitIndices[ k ] - istar |= (( i & ( 1 << q )) >> q ) << k - jstar |= (( j & ( 1 << q )) >> q ) << k - } - //console.log( 'U.read( istar, jstar )', U.read( istar, jstar ).toText() ) - - // console.log( 'before write$', result.toTsv()) - - // console.log( 'U.read at ', istar, jstar, '=', U.read( istar, jstar ).toText()) - result.write$( i, j, U.read( istar, jstar )) - - // console.log( 'after write$', result.toTsv()) - - // X[i][j] = U.x[ istar ][ jstar ] - // Y[i][j] = U.y[ istar ][ jstar ] - } - // else console.log('bits NOT equal') - } - } - //return new numeric.T(X, Y); - - // console.log( 'expanded matrix to:', result.toTsv() ) - return result - }, - - - - - evaluate: function( circuit ){ - - - // console.log( circuit.toDiagram() ) - - - window.dispatchEvent( new CustomEvent( - - 'Q.Circuit.evaluate began', { - - detail: { circuit } - } - )) - - - // Our circuit’s operations must be in the correct order - // before we attempt to step through them! - - circuit.sort$() - - - - // Create a new matrix (or more precisely, a vector) - // that is a 1 followed by all zeros. - // - // ┌ ┐ - // │ 1 │ - // │ 0 │ - // │ 0 │ - // │ . │ - // │ . │ - // │ . │ - // └ ┘ - - const state = new Q.Matrix( 1, Math.pow( 2, circuit.bandwidth )) - state.write$( 0, 0, 1 ) - - - - - // Create a state matrix from this circuit’s input qubits. - - // const state2 = circuit.qubits.reduce( function( state, qubit, i ){ - - // if( i > 0 ) return state.multiplyTensor( qubit ) - // else return state - - // }, circuit.qubits[ 0 ]) - // console.log( 'Initial state', state2.toTsv() ) - // console.log( 'multiplied', state2.multiplyTensor( state ).toTsv() ) - - - - - - const operationsTotal = circuit.operations.length - let operationsCompleted = 0 - let matrix = circuit.operations.reduce( function( state, operation, i ){ - - - - let U - if( operation.registerIndices.length < Infinity ){ - - if( operation.isControlled ){ - //if( operation.registerIndices.length > 1 ){ - - // operation.gate = Q.Gate.PAULI_X - // why the F was this hardcoded in there?? what was i thinking?! - // OH I KNOW ! - // that was from back when i represented this as "C" -- its own gate - // rather than an X with multiple registers. - // so now no need for this "if" block at all. - // will remove in a few cycles. - } - U = operation.gate.matrix - } - else { - - // This is for Quantum Fourier Transforms (QFT). - // Will have to come back to this at a later date! - } - // console.log( operation.gate.name, U.toTsv() ) - - - - - - // Yikes. May need to separate registerIndices in to controls[] and targets[] ?? - // Works for now tho..... - // Houston we have a problem. Turns out, not every gate with registerIndices.length > 1 is - // controlled. - // This is a nasty fix, leads to a lot of edge cases. (For instance: hard-coding cswaps...) But just experimenting. - if(!operation.gate.is_multi_qubit || (operation.gate.symbol == 'S' && operation.registerIndices.length > 2) && operation.gate.can_be_controlled) { - for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){ - - U = Q.Circuit.controlled( U ) - //console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() ) - } - } - - - // We need to send a COPY of the registerIndices Array - // to .expandMatrix() - // otherwise it *may* modify the actual registerIndices Array - // and wow -- tracking down that bug was painful! - - const registerIndices = operation.registerIndices.slice() - state = Q.Circuit.expandMatrix( - - circuit.bandwidth, - U, - registerIndices - - ).multiply( state ) - - - - operationsCompleted ++ - const progress = operationsCompleted / operationsTotal - - - window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate progressed', { detail: { - - circuit, - progress, - operationsCompleted, - operationsTotal, - momentIndex: operation.momentIndex, - registerIndices: operation.registerIndices, - gate: operation.gate.name, - state - - }})) - - - // console.log( `\n\nProgress ... ${ Math.round( operationsCompleted / operationsTotal * 100 )}%`) - // console.log( 'Moment .....', operation.momentIndex ) - // console.log( 'Registers ..', JSON.stringify( operation.registerIndices )) - // console.log( 'Gate .......', operation.gate.name ) - // console.log( 'Intermediate result:', state.toTsv() ) - // console.log( '\n' ) - - - return state - - }, state ) - - - // console.log( 'result matrix', matrix.toTsv() ) - - - - - const outcomes = matrix.rows.reduce( function( outcomes, row, i ){ - - outcomes.push({ - - state: '|'+ parseInt( i, 10 ).toString( 2 ).padStart( circuit.bandwidth, '0' ) +'⟩', - probability: Math.pow( row[ 0 ].absolute(), 2 ) - }) - return outcomes - - }, [] ) - - - - circuit.needsEvaluation = false - circuit.matrix = matrix - circuit.results = outcomes - - - - window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate completed', { detail: { - // circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: { - - circuit, - results: outcomes - - }})) - - - - - return matrix - } -}) - - - - - - - -Object.assign( Q.Circuit.prototype, { - - clone: function(){ - - const - original = this, - clone = original.copy() - - clone.qubits = original.qubits.slice() - clone.results = original.results.slice() - clone.needsEvaluation = original.needsEvaluation - - return clone - }, - evaluate$: function(){ - - Q.Circuit.evaluate( this ) - return this - }, - report$: function( length ){ - - if( this.needsEvaluation ) this.evaluate$() - if( !Q.isUsefulInteger( length )) length = 20 - - const - circuit = this, - text = this.results.reduce( function( text, outcome, i ){ - - const - probabilityPositive = Math.round( outcome.probability * length ), - probabilityNegative = length - probabilityPositive - - return text +'\n' - + ( i + 1 ).toString().padStart( Math.ceil( Math.log10( Math.pow( 2, circuit.qubits.length ))), ' ' ) +' ' - + outcome.state +' ' - + ''.padStart( probabilityPositive, '█' ) - + ''.padStart( probabilityNegative, '░' ) - + Q.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance' - - }, '' ) + '\n' - return text - }, - try$: function(){ - - if( this.needsEvaluation ) this.evaluate$() - - - // We need to “stack” our probabilities from 0..1. - - const outcomesStacked = new Array( this.results.length ) - this.results.reduce( function( sum, outcome, i ){ - - sum += outcome.probability - outcomesStacked[ i ] = sum - return sum - - }, 0 ) - - - // Now we can pick a random number - // and return the first outcome - // with a probability equal to or greater than - // that random number. - - const - randomNumber = Math.random(), - randomIndex = outcomesStacked.findIndex( function( index ){ - - return randomNumber <= index - }) - - - // Output that to the console - // but return the random index - // so we can pipe that to something else - // should we want to :) - - // console.log( this.outcomes[ randomIndex ].state ) - return randomIndex - }, - - - - - //////////////// - // // - // Output // - // // - //////////////// - - - // This is absolutely required by toTable. - - sort$: function(){ - - - // Sort this circuit’s operations - // primarily by momentIndex, - // then by the first registerIndex. - - this.operations.sort( function( a, b ){ - - if( a.momentIndex === b.momentIndex ){ - - - // Note that we are NOT sorting registerIndices here! - // We are merely asking which set of indices contain - // the lowest register index. - // If we instead sorted the registerIndices - // we could confuse which qubit is the controller - // and which is the controlled! - - return Math.min( ...a.registerIndices ) - Math.min( b.registerIndices ) - } - else { - - return a.momentIndex - b.momentIndex - } - }) - return this - }, - - - - - - - /////////////////// - // // - // Exporters // - // // - /////////////////// - - - // Many export functions rely on toTable - // and toTable itself absolutely relies on - // a circuit’s operations to be SORTED correctly. - // We could force circuit.sort$() here, - // but then toTable would become toTable$ - // and every exporter that relies on it would - // also become destructive. - - toTable: function(){ - - const - table = new Array( this.timewidth ), - circuit = this - - - // Sure, this is equal to table.length - // but isn’t legibility and convenience everything? - - table.timewidth = this.timewidth - - - // Similarly, this should be equal to table[ 0 ].length - // or really table[ i >= 0; i < table.length ].length, - // but again, lowest cognitive hurdle is key ;) - - table.bandwidth = this.bandwidth - - - // First, let’s establish a “blank” table - // that contains an identity operation - // for each register during each moment. - - table.fill( 0 ).forEach( function( element, index, array ){ - - const operations = new Array( circuit.bandwidth ) - operations.fill( 0 ).forEach( function( element, index, array ){ - - array[ index ] = { - - symbol: 'I', - symbolDisplay: 'I', - name: 'Identity', - nameCss: 'identity', - gateInputIndex: 0, - bandwidth: 0, - thisGateAmongMultiQubitGatesIndex: 0, - aSiblingIsAbove: false, - aSiblingIsBelow: false - } - }) - array[ index ] = operations - }) - - - // Now iterate through the circuit’s operations list - // and note those operations in our table. - // NOTE: This relies on operations being pre-sorted with .sort$() - // prior to the .toTable() call. - - let - momentIndex = 1, - multiRegisterOperationIndex = 0, - gateTypesUsedThisMoment = {} - - this.operations.forEach( function( operation, operationIndex, operations ){ - - - // We need to keep track of - // how many multi-register operations - // occur during this moment. - - if( momentIndex !== operation.momentIndex ){ - - table[ momentIndex ].gateTypesUsedThisMoment = gateTypesUsedThisMoment - momentIndex = operation.momentIndex - multiRegisterOperationIndex = 0 - gateTypesUsedThisMoment = {} - } - if( operation.registerIndices.length > 1 ){ - - table[ momentIndex - 1 ].multiRegisterOperationIndex = multiRegisterOperationIndex - multiRegisterOperationIndex ++ - } - if( gateTypesUsedThisMoment[ operation.gate.symbol ] === undefined ){ - - gateTypesUsedThisMoment[ operation.gate.symbol ] = 1 - } - else gateTypesUsedThisMoment[ operation.gate.symbol ] ++ - - - // By default, an operation’s CSS name - // is its regular name, all lowercase, - // with all spaces replaced by hyphens. - - let nameCss = operation.gate.name.toLowerCase().replace( /\s+/g, '-' ) - - - operation.registerIndices.forEach( function( registerIndex, indexAmongSiblings ){ - - let isMultiRegisterOperation = false - if( operation.registerIndices.length > 1 ){ - - isMultiRegisterOperation = true - if( indexAmongSiblings === operation.registerIndices.length - 1 ){ - - nameCss = 'target' - } - else { - - nameCss = 'control' - } - - // May need to re-visit the code above in consideration of SWAPs. - - } - table[ operation.momentIndex - 1 ][ registerIndex - 1 ] = { - - symbol: operation.gate.symbol, - symbolDisplay: operation.gate.symbol, - name: operation.gate.name, - nameCss, - operationIndex, - momentIndex: operation.momentIndex, - registerIndex, - isMultiRegisterOperation, - multiRegisterOperationIndex, - gatesOfThisTypeNow: gateTypesUsedThisMoment[ operation.gate.symbol ], - indexAmongSiblings, - siblingExistsAbove: Math.min( ...operation.registerIndices ) < registerIndex, - siblingExistsBelow: Math.max( ...operation.registerIndices ) > registerIndex - } - }) - -/* - - -++++++++++++++++++++++ - -Non-fatal problem to solve here: - -Previously we were concerned with “gates of this type used this moment” -when we were thinking about CNOT as its own special gate. -But now that we treat CNOT as just connected X gates, -we now have situations -where a moment can have one “CNOT” but also a stand-alone X gate -and toTable will symbol the “CNOT” as X.0 -(never X.1, because it’s the only multi-register gate that moment) -but still uses the symbol X.0 instead of just X -because there’s another stand-alone X there tripping the logic!!! - - - - - -*/ - - - // if( operationIndex === operations.length - 1 ){ - - table[ momentIndex - 1 ].gateTypesUsedThisMoment = gateTypesUsedThisMoment - // } - }) - - - - - - - - - - - - table.forEach( function( moment, m ){ - - moment.forEach( function( operation, o ){ - - if( operation.isMultiRegisterOperation ){ - - if( moment.gateTypesUsedThisMoment[ operation.symbol ] > 1 ){ - - operation.symbolDisplay = operation.symbol +'.'+ ( operation.gatesOfThisTypeNow - 1 ) - } - operation.symbolDisplay += '#'+ operation.indexAmongSiblings - } - }) - }) - - - // Now we can easily read down each moment - // and establish the moment’s character width. - // Very useful for text-based diagrams ;) - - table.forEach( function( moment ){ - - const maximumWidth = moment.reduce( function( maximumWidth, operation ){ - - return Math.max( maximumWidth, operation.symbolDisplay.length ) - - }, 1 ) - moment.maximumCharacterWidth = maximumWidth - }) - - - // We can also do this for the table as a whole. - - table.maximumCharacterWidth = table.reduce( function( maximumWidth, moment ){ - - return Math.max( maximumWidth, moment.maximumCharacterWidth ) - - }, 1 ) - - - // I think we’re done here. - - return table - }, - toText: function( makeAllMomentsEqualWidth ){ - - ` - Create a text representation of this circuit - using only common characters, - ie. no fancy box-drawing characters. - This is the complement of Circuit.fromText() - ` - - const - table = this.toTable(), - output = new Array( table.bandwidth ).fill( '' ) - - for( let x = 0; x < table.timewidth; x ++ ){ - - for( let y = 0; y < table.bandwidth; y ++ ){ - - let cellString = table[ x ][ y ].symbolDisplay.padEnd( table[ x ].maximumCharacterWidth, '-' ) - if( makeAllMomentsEqualWidth && x < table.timewidth - 1 ){ - - cellString = table[ x ][ y ].symbolDisplay.padEnd( table.maximumCharacterWidth, '-' ) - } - if( x > 0 ) cellString = '-'+ cellString - output[ y ] += cellString - } - } - return '\n'+ output.join( '\n' ) - // return output.join( '\n' ) - }, - toDiagram: function( makeAllMomentsEqualWidth ){ - - ` - Create a text representation of this circuit - using fancy box-drawing characters. - ` - - const - scope = this, - table = this.toTable(), - output = new Array( table.bandwidth * 3 + 1 ).fill( '' ) - - output[ 0 ] = ' ' - scope.qubits.forEach( function( qubit, q ){ - - const y3 = q * 3 - output[ y3 + 1 ] += ' ' - output[ y3 + 2 ] += 'r'+ ( q + 1 ) +' |'+ qubit.beta.toText().trim() +'⟩─' - output[ y3 + 3 ] += ' ' - }) - for( let x = 0; x < table.timewidth; x ++ ){ - - const padToLength = makeAllMomentsEqualWidth - ? table.maximumCharacterWidth - : table[ x ].maximumCharacterWidth - - output[ 0 ] += Q.centerText( 'm'+ ( x + 1 ), padToLength + 4 ) - for( let y = 0; y < table.bandwidth; y ++ ){ - - let - operation = table[ x ][ y ], - first = '', - second = '', - third = '' - - if( operation.symbol === 'I' ){ - - first += ' ' - second += '──' - third += ' ' - - first += ' '.padEnd( padToLength ) - second += Q.centerText( '○', padToLength, '─' ) - third += ' '.padEnd( padToLength ) - - first += ' ' - if( x < table.timewidth - 1 ) second += '──' - else second += ' ' - third += ' ' - } - else { - - if( operation.isMultiRegisterOperation ){ - - first += '╭─' - third += '╰─' - } - else { - - first += '┌─' - third += '└─' - } - second += '┤ ' - - first += '─'.padEnd( padToLength, '─' ) - second += Q.centerText( operation.symbolDisplay, padToLength ) - third += '─'.padEnd( padToLength, '─' ) - - - if( operation.isMultiRegisterOperation ){ - - first += '─╮' - third += '─╯' - } - else { - - first += '─┐' - third += '─┘' - } - second += x < table.timewidth - 1 ? ' ├' : ' │' - - if( operation.isMultiRegisterOperation ){ - - let n = ( operation.multiRegisterOperationIndex * 2 ) % ( table[ x ].maximumCharacterWidth + 1 ) + 1 - if( operation.siblingExistsAbove ){ - - first = first.substring( 0, n ) +'┴'+ first.substring( n + 1 ) - } - if( operation.siblingExistsBelow ){ - - third = third.substring( 0, n ) +'┬'+ third.substring( n + 1 ) - } - } - } - const y3 = y * 3 - output[ y3 + 1 ] += first - output[ y3 + 2 ] += second - output[ y3 + 3 ] += third - } - } - return '\n'+ output.join( '\n' ) - }, - - - - - // Oh yes my friends... WebGL is coming! - - toShader: function(){ - - }, - toGoogleCirq: function(){ -/* - - -cirq.GridQubit(4,5) - -https://cirq.readthedocs.io/en/stable/tutorial.html - -*/ - const header = `import cirq` - - return headers - }, - toAmazonBraket: function(){ - let is_valid_braket_circuit = true - const header = `import boto3 -from braket.aws import AwsDevice -from braket.circuits import Circuit - -my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket -my_prefix = "Your-Folder-Name" # the name of the folder in the bucket -s3_folder = (my_bucket, my_prefix)\n -device = LocalSimulator()\n\n` -//TODO (ltnln): Syntax is different for simulators and actual quantum computers. Should there be a default? Should there be a way to change? -//vs an actual quantum computer? May not be necessary. - let variables = '' - let num_unitaries = 0 - //`qjs_circuit = Circuit().h(0).cnot(0,1)` - //ltnln change: from gate.AmazonBraketName -> gate.symbolAmazonBraket - let circuit = this.operations.reduce( function( string, operation ){ - let awsGate = operation.gate.symbolAmazonBraket !== undefined ? - operation.gate.symbolAmazonBraket : - operation.gate.symbol.substr( 0, 1 ).toLowerCase() - if( operation.gate.symbolAmazonBraket === undefined ) is_valid_braket_circuit = false - if( operation.gate.symbol === 'X' ) { - if( operation.registerIndices.length === 1 ) awsGate = operation.gate.symbolAmazonBraket - else if( operation.registerIndices.length === 2 ) awsGate = 'cnot' - else if( operation.registerIndices.length === 3) awsGate = 'ccnot' - else is_valid_braket_circuit = false - } - - else if( operation.gate.symbol === 'S' ) { - if( operation.gate.parameters["phi"] === 0 ) { - awsGate = operation.registerIndices.length == 2 ? awsGate : "cswap" - return string +'.'+ awsGate +'(' + - operation.registerIndices.reduce( function( string, registerIndex, r ){ - - return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) - - }, '' ) + ')' - } - awsGate = 'pswap' - } - //ltnln note: removed the if( operation.gate.symbol == '*') branch as it should be covered by - //the inclusion of the CURSOR gate. - else if( operation.gate.symbol === 'Y' || operation.gate.symbol === 'Z' || operation.gate.symbol === 'P' ) { - if( operation.registerIndices.length === 1) awsGate = operation.gate.symbolAmazonBraket - else if( operation.registerIndices.length === 2 ) awsGate = (operation.gate.symbol === 'Y') ? 'cy' : (operation.gate.symbol === 'Z') ? 'cz' : 'cphaseshift' - else is_valid_braket_circuit = false - } - //for all unitary gates, there must be a line of code to initialize the matrix for use - //in Braket's .u(matrix=my_unitary, targets[0]) function - else if( operation.gate.symbol === 'U') { - //check that this truly works as a unique id - is_valid_braket_circuit &= operation.registerIndices.length === 1 - const new_matrix = `unitary_` + num_unitaries - num_unitaries++ - const a = Q.ComplexNumber.toText(Math.cos(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), - Math.sin(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2)) - .replace('i', 'j') - const b = Q.ComplexNumber.toText(-Math.cos(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), - -Math.sin(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2) - .replace('i', 'j') - const c = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), - -Math.sin((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2) - .replace('i', 'j') - const d = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), - Math.sin((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2)) / 2) - .replace('i', 'j') - variables += new_matrix + ` = np.array(` + - `[[` + a + ', ' + b + `],`+ - `[` + c + ', ' + d + `]])\n` - return string +'.'+ awsGate +'(' + new_matrix +','+ - operation.registerIndices.reduce( function( string, registerIndex, r ){ - - return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) - - }, '' ) + ')' - } - // I believe this line should ensure that we don't include any controlled single-qubit gates that aren't allowed in Braket. - // The registerIndices.length > 1 technically shouldn't be necessary, but if changes are made later, it's just for safety. - else is_valid_braket_circuit &= (operation.registerIndices.length === 1) || ( operation.registerIndices.length > 1 && operation.gate.is_multi_qubit ) - return string +'.'+ awsGate +'(' + - operation.registerIndices.reduce( function( string, registerIndex, r ){ - - return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) - - }, '' ) + ((operation.gate.has_parameters) ? - Object.values( operation.gate.parameters ).reduce( function( string, parameter ) { - return string + "," + parameter - }, '') - : '') + ')' - - }, 'qjs_circuit = Circuit()' ) - variables += '\n' - if( this.operations.length === 0 ) circuit += '.i(0)'// Quick fix to avoid an error here! - - const footer = `\n\ntask = device.run(qjs_circuit, s3_folder, shots=100) -print(task.result().measurement_counts)` - return is_valid_braket_circuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###` - }, - toLatex: function(){ - - /* - - \Qcircuit @C=1em @R=.7em { - & \ctrl{2} & \targ & \gate{U} & \qw \\ - & \qw & \ctrl{-1} & \qw & \qw \\ - & \targ & \ctrl{-1} & \ctrl{-2} & \qw \\ - & \qw & \ctrl{-1} & \qw & \qw - } - - No "&"" means it’s an input. So could also do this: - \Qcircuit @C=1.4em @R=1.2em { - - a & i \\ - 1 & x - } - */ - - return '\\Qcircuit @C=1.0em @R=0.7em {\n' + - this.toTable() - .reduce( function( array, moment, m ){ - - moment.forEach( function( operation, o, operations ){ - - let command = 'qw' - if( operation.symbol !== 'I' ){ - - if( operation.isMultiRegisterOperation ){ - - if( operation.indexAmongSiblings === 0 ){ - - if( operation.symbol === 'X' ) command = 'targ' - else command = operation.symbol.toLowerCase() - } - else if( operation.indexAmongSiblings > 0 ) command = 'ctrl{?}' - } - else command = operation.symbol.toLowerCase() - } - operations[ o ].latexCommand = command - }) - const maximumCharacterWidth = moment.reduce( function( maximumCharacterWidth, operation ){ - - return Math.max( maximumCharacterWidth, operation.latexCommand.length ) - - }, 0 ) - moment.forEach( function( operation, o ){ - - array[ o ] += '& \\'+ operation.latexCommand.padEnd( maximumCharacterWidth ) +' ' - }) - return array - - }, new Array( this.bandwidth ).fill( '\n\t' )) - .join( '\\\\' ) + - '\n}' - }, - - - - - - - ////////////// - // // - // Edit // - // // - ////////////// - - - get: function( momentIndex, registerIndex ){ - - return this.operations.find( function( op ){ - - return op.momentIndex === momentIndex && - op.registerIndices.includes( registerIndex ) - }) - }, - clear$: function( momentIndex, registerIndices ){ - - const circuit = this - - - // Validate our arguments. - - if( arguments.length !== 2 ) - Q.warn( `Q.Circuit.clear$ expected 2 arguments but received ${ arguments.length }.` ) - if( Q.isUsefulInteger( momentIndex ) !== true ) - return Q.error( `Q.Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid moment index:`, momentIndex ) - if( Q.isUsefulInteger( registerIndices )) registerIndices = [ registerIndices ] - if( registerIndices instanceof Array !== true ) - return Q.error( `Q.Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid register indices array:`, registerIndices ) - - - // Let’s find any operations - // with a footprint at this moment index and one of these register indices - // and collect not only their content, but their index in the operations array. - // (We’ll need that index to splice the operations array later.) - - const foundOperations = circuit.operations.reduce( function( filtered, operation, o ){ - - if( operation.momentIndex === momentIndex && - operation.registerIndices.some( function( registerIndex ){ - - return registerIndices.includes( registerIndex ) - }) - ) filtered.push({ - - index: o, - momentIndex: operation.momentIndex, - registerIndices: operation.registerIndices, - gate: operation.gate - }) - return filtered - - }, [] ) - - - // Because we held on to each found operation’s index - // within the circuit’s operations array - // we can now easily splice them out of the array. - - foundOperations.reduce( function( deletionsSoFar, operation ){ - - circuit.operations.splice( operation.index - deletionsSoFar, 1 ) - return deletionsSoFar + 1 - - }, 0 ) - - - // IMPORTANT! - // Operations must be sorted properly - // for toTable to work reliably with - // multi-register operations!! - - this.sort$() - - - // Let’s make history. - - if( foundOperations.length ){ - - this.history.record$({ - - redo: { - - name: 'clear$', - func: circuit.clear$, - args: Array.from( arguments ) - }, - undo: foundOperations.reduce( function( undos, operation ){ - - undos.push({ - - name: 'set$', - func: circuit.set$, - args: [ - - operation.gate, - operation.momentIndex, - operation.registerIndices - ] - }) - return undos - - }, [] ) - }) - - - // Let anyone listening, - // including any circuit editor interfaces, - // know about what we’ve just completed here. - - foundOperations.forEach( function( operation ){ - - window.dispatchEvent( new CustomEvent( - - 'Q.Circuit.clear$', { detail: { - - circuit, - momentIndex, - registerIndices: operation.registerIndices - }} - )) - }) - } - - - // Enable that “fluent interface” method chaining :) - - return circuit - }, - - - setProperty$: function( key, value ){ - - this[ key ] = value - return this - }, - setName$: function( name ){ - - if( typeof name === 'function' ) name = name() - return this.setProperty$( 'name', name ) - }, - - - set$: function( gate, momentIndex, registerIndices, parameters = {} ){ - - const circuit = this - // Is this a valid gate? - // We clone the gate rather than using the constant; this way, if we change it's parameters, we don't change the constant. - if( typeof gate === 'string' ) gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( gate ) ) - if( gate instanceof Q.Gate !== true ) return Q.error( `Q.Circuit attempted to add a gate (${ gate }) to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate ) - - - // Is this a valid moment index? - - if( Q.isUsefulNumber( momentIndex ) !== true || - Number.isInteger( momentIndex ) !== true || - momentIndex < 1 || momentIndex > this.timewidth ){ - - return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at a moment index that is not valid:`, momentIndex ) - } - - - // Are these valid register indices? - - if( typeof registerIndices === 'number' ) registerIndices = [ registerIndices ] - if( registerIndices instanceof Array !== true ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an invalid register indices array:`, registerIndices ) - if( registerIndices.length === 0 ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an empty register indices array:`, registerIndices ) - if( registerIndices.reduce( function( accumulator, registerIndex ){ - - // console.log(accumulator && - // registerIndex > 0 && - // registerIndex <= circuit.bandwidth) - return ( - - accumulator && - registerIndex > 0 && - registerIndex <= circuit.bandwidth - ) - - }, false )){ - - return Q.warn( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with some out of range qubit indices:`, registerIndices ) - } - - - // Ok, now we can check if this set$ command - // is redundant. - - const - isRedundant = !!circuit.operations.find( function( operation ){ - - return ( - - momentIndex === operation.momentIndex && - gate === operation.gate && - registerIndices.length === operation.registerIndices.length && - registerIndices.every( val => operation.registerIndices.includes( val )) - ) - }) - - - // If it’s NOT redundant - // then we’re clear to proceed. - - if( isRedundant !== true ){ - - - // If there’s already an operation here, - // we’d better get rid of it! - // This will also entirely remove any multi-register operations - // that happen to have a component at this moment / register. - - this.clear$( momentIndex, registerIndices ) - - - // Finally. - // Finally we can actually set this operation. - // Aren’t you glad we handle all this for you? - - const - //TODO: For ltnln (have to fix) - // a) allow users to control whatever they want! Just because it's not allowed in Braket - // doesn't mean they shouldn't be allowed to do it in Q! (Probably fixable by adjusting toAmazonBraket) - // b) Controlling a multi_qubit gate will not treat the control icon like a control gate! - isControlled = registerIndices.length > 1 && gate !== Q.Gate.SWAP && gate.can_be_controlled !== undefined - operation = { - - gate, - momentIndex, - registerIndices, - isControlled - } - //perform parameter update here!!! - Object.keys(parameters).forEach( element => { parameters[element] = +parameters[element] }) - if(gate.has_parameters) gate.updateMatrix$.apply( gate, Object.values(parameters) ) - this.operations.push( operation ) - - - // IMPORTANT! - // Operations must be sorted properly - // for toTable to work reliably with - // multi-register operations!! - - this.sort$() - - - // Let’s make history. - const redo_args = Array.from( arguments ) - Object.assign( redo_args[ redo_args.length - 1 ], parameters ) - this.history.record$({ - - redo: { - - name: 'set$', - func: circuit.set$, - args: redo_args - }, - undo: [{ - - name: 'clear$', - func: circuit.clear$, - args: [ momentIndex, registerIndices ] - }] - }) - - - // Emit an event that we have set an operation - // on this circuit. - - window.dispatchEvent( new CustomEvent( - - 'Q.Circuit.set$', { detail: { - - circuit, - operation - }} - )) - } - return circuit - }, - - - - - determineRanges: function( options ){ - - if( options === undefined ) options = {} - let { - - qubitFirstIndex, - qubitRange, - qubitLastIndex, - momentFirstIndex, - momentRange, - momentLastIndex - - } = options - - if( typeof qubitFirstIndex !== 'number' ) qubitFirstIndex = 0 - if( typeof qubitLastIndex !== 'number' && typeof qubitRange !== 'number' ) qubitLastIndex = this.bandwidth - if( typeof qubitLastIndex !== 'number' && typeof qubitRange === 'number' ) qubitLastIndex = qubitFirstIndex + qubitRange - else if( typeof qubitLastIndex === 'number' && typeof qubitRange !== 'number' ) qubitRange = qubitLastIndex - qubitFirstIndex - else return Q.error( `Q.Circuit attempted to copy a circuit but could not understand what qubits to copy.` ) - - if( typeof momentFirstIndex !== 'number' ) momentFirstIndex = 0 - if( typeof momentLastIndex !== 'number' && typeof momentRange !== 'number' ) momentLastIndex = this.timewidth - if( typeof momentLastIndex !== 'number' && typeof momentRange === 'number' ) momentLastIndex = momentFirstIndex + momentRange - else if( typeof momentLastIndex === 'number' && typeof momentRange !== 'number' ) momentRange = momentLastIndex - momentFirstIndex - else return Q.error( `Q.Circuit attempted to copy a circuit but could not understand what moments to copy.` ) - - Q.log( 0.8, - - '\nQ.Circuit copy operation:', - '\n\n qubitFirstIndex', qubitFirstIndex, - '\n qubitLastIndex ', qubitLastIndex, - '\n qubitRange ', qubitRange, - '\n\n momentFirstIndex', momentFirstIndex, - '\n momentLastIndex ', momentLastIndex, - '\n momentRange ', momentRange, - '\n\n' - ) - - return { - - qubitFirstIndex, - qubitRange, - qubitLastIndex, - momentFirstIndex, - momentRange, - momentLastIndex - } - }, - - - copy: function( options, isACutOperation ){ - - const original = this - let { - - registerFirstIndex, - registerRange, - registerLastIndex, - momentFirstIndex, - momentRange, - momentLastIndex - - } = this.determineRanges( options ) - - const copy = new Q.Circuit( registerRange, momentRange ) - - original.operations - .filter( function( operation ){ - - return ( operation.registerIndices.every( function( registerIndex ){ - - return ( - - operation.momentIndex >= momentFirstIndex && - operation.momentIndex < momentLastIndex && - operation.registerIndex >= registerFirstIndex && - operation.registerIndex < registerLastIndex - ) - })) - }) - .forEach( function( operation ){ - - const adjustedRegisterIndices = operation.registerIndices.map( function( registerIndex ){ - - return registerIndex - registerFirstIndex - }) - copy.set$( - - operation.gate, - 1 + m - momentFirstIndex, - adjustedRegisterIndices - ) - }) - - - // The cut$() operation just calls copy() - // with the following boolean set to true. - // If this is a cut we need to - // replace all gates in this area with identity gates. - - // UPDATE !!!! - // will come back to fix!! - // with new style it's now just a matter of - // splicing out these out of circuit.operations - - - - if( isACutOperation === true ){ - - /* - for( let m = momentFirstIndex; m < momentLastIndex; m ++ ){ - - original.moments[ m ] = new Array( original.bandwidth ) - .fill( 0 ) - .map( function( qubit, q ){ - - return { - - gate: Q.Gate.IDENTITY, - registerIndices: [ q ] - } - }) - }*/ - } - return copy - }, - cut$: function( options ){ - - return this.copy( options, true ) - }, - - - - - - - - /* - - - - - If covers all moments for 1 or more qubits then - 1. go through each moment and remove those qubits - 2. remove hanging operations. (right?? don’t want them?) - - - - - */ - - spliceCut$: function( options ){ - - let { - - qubitFirstIndex, - qubitRange, - qubitLastIndex, - momentFirstIndex, - momentRange, - momentLastIndex - - } = this.determineRanges( options ) - - - // Only three options are valid: - // 1. Selection area covers ALL qubits for a series of moments. - // 2. Selection area covers ALL moments for a seriies of qubits. - // 3. Both of the above (splice the entire circuit). - - if( qubitRange !== this.bandwidth && - momentRange !== this.timewidth ){ - - return Q.error( `Q.Circuit attempted to splice circuit #${this.index} by an area that did not include all qubits _or_ all moments.` ) - } - - - // If the selection area covers all qubits for 1 or more moments - // then splice the moments array. - - if( qubitRange === this.bandwidth ){ - - - // We cannot use Array.prototype.splice() for this - // because we need a DEEP copy of the array - // and splice() will only make a shallow copy. - - this.moments = this.moments.reduce( function( accumulator, moment, m ){ - - if( m < momentFirstIndex - 1 || m >= momentLastIndex - 1 ) accumulator.push( moment ) - return accumulator - - }, []) - this.timewidth -= momentRange - - //@@ And how do we implement splicePaste$() here? - } - - - // If the selection area covers all moments for 1 or more qubits - // then iterate over each moment and remove those qubits. - - if( momentRange === this.timewidth ){ - - - // First, let’s splice the inputs array. - - this.inputs.splice( qubitFirstIndex, qubitRange ) - //@@ this.inputs.splice( qubitFirstIndex, qubitRange, qubitsToPaste?? ) - - - // Now we can make the proper adjustments - // to each of our moments. - - this.moments = this.moments.map( function( operations ){ - - - // Remove operations that pertain to the removed qubits. - // Renumber the remaining operations’ qubitIndices. - - return operations.reduce( function( accumulator, operation ){ - - if( operation.qubitIndices.every( function( index ){ - - return index < qubitFirstIndex || index >= qubitLastIndex - - })) accumulator.push( operation ) - return accumulator - - }, []) - .map( function( operation ){ - - operation.qubitIndices = operation.qubitIndices.map( function( index ){ - - return index >= qubitLastIndex ? index - qubitRange : index - }) - return operation - }) - }) - this.bandwidth -= qubitRange - } - - - // Final clean up. - - this.removeHangingOperations$() - this.fillEmptyOperations$() - - - return this// Or should we return the cut area?! - }, - splicePaste$: function(){ - - - }, - - - - - - // This is where “hanging operations” get interesting! - // when you paste one circuit in to another - // and that clipboard circuit has hanging operations - // those can find a home in the circuit its being pasted in to! - - - paste$: function( other, atMoment = 0, atQubit = 0, shouldClean = true ){ - - const scope = this - this.timewidth = Math.max( this.timewidth, atMoment + other.timewidth ) - this.bandwidth = Math.max( this.bandwidth, atQubit + other.bandwidth ) - this.ensureMomentsAreReady$() - this.fillEmptyOperations$() - other.moments.forEach( function( moment, m ){ - - moment.forEach( function( operation ){ - - //console.log( 'past over w this:', m + atMoment, operation ) - - scope.set$( - - operation.gate, - m + atMoment + 1, - operation.qubitIndices.map( function( qubitIndex ){ - - return qubitIndex + atQubit - }) - ) - }) - }) - if( shouldClean ) this.removeHangingOperations$() - this.fillEmptyOperations$() - return this - }, - pasteInsert$: function( other, atMoment, atQubit ){ - - // if( other.alphandwidth !== this.bandwidth && - // other.timewidth !== this.timewidth ) return Q.error( 'Q.Circuit attempted to pasteInsert Circuit A', other, 'in to circuit B', this, 'but neither their bandwidth or timewidth matches.' ) - - - - - if( shouldClean ) this.removeHangingOperations$() - this.fillEmptyOperations$() - return this - - }, - expand$: function(){ - - // expand either bandwidth or timewidth, fill w identity - - - this.fillEmptyOperations$() - return thiis - }, - - - - - - - - trim$: function( options ){ - - ` - Edit this circuit by trimming off moments, qubits, or both. - We could have implemented trim$() as a wrapper around copy$(), - similar to how cut$ is a wrapper around copy$(). - But this operates on the existing circuit - instead of returning a new one and returning that. - ` - - let { - - qubitFirstIndex, - qubitRange, - qubitLastIndex, - momentFirstIndex, - momentRange, - momentLastIndex - - } = this.determineRanges( options ) - - - // First, trim the moments down to desired size. - - this.moments = this.moments.slice( momentFirstIndex, momentLastIndex ) - this.timewidth = momentRange - - - // Then, trim the bandwidth down. - - this.inputs = this.inputs.slice( qubitFirstIndex, qubitLastIndex ) - this.bandwidth = qubitRange - - - // Finally, remove all gates where - // gate’s qubit indices contain an index < qubitFirstIndex, - // gate’s qubit indices contain an index > qubitLastIndex, - // and fill those holes with Identity gates. - - this.removeHangingOperations$() - this.fillEmptyOperations$() - - return this - } -}) - - - - - - - -// Against my predilection for verbose clarity... -// I offer you super short convenience methods -// that do NOT use the $ suffix to delcare they are destructive. -// Don’t shoot your foot off. - -Object.entries( Q.Gate.constants ).forEach( function( entry ){ - - const - gateConstantName = entry[ 0 ], - gate = entry[ 1 ], - set$ = function( momentIndex, registerIndexOrIndices ){ - - this.set$( gate, momentIndex, registerIndexOrIndices ) - return this - } - Q.Circuit.prototype[ gateConstantName ] = set$ - Q.Circuit.prototype[ gate.symbol ] = set$ - Q.Circuit.prototype[ gate.symbol.toLowerCase() ] = set$ -}) - - - -/* -const bells = [ - - - // Verbose without shortcuts. - - new Q.Circuit( 2, 2 ) - .set$( Q.Gate.HADAMARD, 1, [ 1 ]) - .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]), - - new Q.Circuit( 2, 2 ) - .set$( Q.Gate.HADAMARD, 1, 1 ) - .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]), - - - // Uses Q.Gate.findBySymbol() to lookup gates. - - new Q.Circuit( 2, 2 ) - .set$( 'H', 1, [ 1 ]) - .set$( 'X', 2, [ 1 , 2 ]), - - new Q.Circuit( 2, 2 ) - .set$( 'H', 1, 1 ) - .set$( 'X', 2, [ 1 , 2 ]), - - - // Convenience gate functions -- constant name. - - new Q.Circuit( 2, 2 ) - .HADAMARD( 1, [ 1 ]) - .PAULI_X( 2, [ 1, 2 ]), - - new Q.Circuit( 2, 2 ) - .HADAMARD( 1, 1 ) - .PAULI_X( 2, [ 1, 2 ]), - - - // Convenience gate functions -- uppercase symbol. - - new Q.Circuit( 2, 2 ) - .H( 1, [ 1 ]) - .X( 2, [ 1, 2 ]), - - new Q.Circuit( 2, 2 ) - .H( 1, 1 ) - .X( 2, [ 1, 2 ]), - - - // Convenience gate functions -- lowercase symbol. - - new Q.Circuit( 2, 2 ) - .h( 1, [ 1 ]) - .x( 2, [ 1, 2 ]), - - new Q.Circuit( 2, 2 )// Perhaps the closest to Braket style. - .h( 1, 1 ) - .x( 2, [ 1, 2 ]), - - - // Q function -- bandwidth / timewidth arguments. - - Q( 2, 2 ) - .h( 1, [ 1 ]) - .x( 2, [ 1, 2 ]), - - Q( 2, 2 ) - .h( 1, 1 ) - .x( 2, [ 1, 2 ]), - - - // Q function -- text block argument - // with operation symbols - // and operation component IDs. - - Q` - H-X.0#0 - I-X.0#1`, - - - // Q function -- text block argument - // using only component IDs - // (ie. no operation symbols) - // because the operation that the - // components should belong to is NOT ambiguous. - - Q` - H-X#0 - I-X#1`, - - - // Q function -- text block argument - // as above, but using only whitespace - // to partition between moments. - - Q` - H X#0 - I X#1` -], -bellsAreEqual = !!bells.reduce( function( a, b ){ - - return a.toText() === b.toText() ? a : NaN - -}) -if( bellsAreEqual ){ - - console.log( `\n\nYES. All of ${ bells.length } our “Bell” circuits are equal.\n\n`, bells ) -} -*/ - - - - - - - -Q.Circuit.createConstants( - - 'BELL', Q` - - H X#0 - I X#1 - `, - // 'GROVER', Q` - - // H X *#0 X#0 I X#0 I I I X#0 I I I X#0 I X H X I *#0 - // H X I X#1 *#0 X#1 *#0 X#0 I I I X#0 X I H X I I I I - // H X I I I I I X#1 *#0 X#1 *#0 X#1 *#0 X#1 I *#0 X H X I - // H X *#1 I *#1 I *#1 I *#1 I *#1 I *#1 I I *#1 X H X *#1 - // ` - - //https://docs.microsoft.com/en-us/quantum/concepts/circuits?view=qsharp-preview - // 'TELEPORT', Q.(` - - // I-I--H-M---v - // H-C0-I-M-v-v - // I-C1-I-I-X-Z- - // `) -) - - - - - - - - - - - - - - - - - -/* - - -%%HTML - - - - - -%%javascript -Q.braket( element ) - - - - -*/ - - - -//%%javascript - - - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.Circuit.Editor = function( circuit, targetEl ){ - - - // First order of business, - // we require a valid circuit. - - if( circuit instanceof Q.Circuit !== true ) circuit = new Q.Circuit() - this.circuit = circuit - this.index = Q.Circuit.Editor.index ++ - - - // Q.Circuit.Editor is all about the DOM - // so we’re going to get some use out of this - // stupid (but convenient) shorthand here. - - const createDiv = function(){ - - return document.createElement( 'div' ) - } - - - - - // We want to “name” our circuit editor instance - // but more importantly we want to give it a unique DOM ID. - // Keep in mind we can have MULTIPLE editors - // for the SAME circuit! - // This is a verbose way to do it, - // but each step is clear and I needed clarity today! ;) - - this.name = typeof circuit.name === 'string' ? - circuit.name : - 'Q Editor '+ this.index - - - // If we’ve been passed a target DOM element - // we should use that as our circuit element. - - if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl ) - const circuitEl = targetEl instanceof HTMLElement ? targetEl : createDiv() - circuitEl.classList.add( 'Q-circuit' ) - - - // If the target element already has an ID - // then we want to use that as our domID. - - if( typeof circuitEl.getAttribute( 'id' ) === 'string' ){ - - this.domId = circuitEl.getAttribute( 'id' ) - } - - - // Otherwise let’s transform our name value - // into a usable domId. - - else { - - let domIdBase = this.name - .replace( /^[^a-z]+|[^\w:.-]+/gi, '-' ), - domId = domIdBase, - domIdAttempt = 1 - - while( document.getElementById( domId ) !== null ){ - - domIdAttempt ++ - domId = domIdBase +'-'+ domIdAttempt - } - this.domId = domId - circuitEl.setAttribute( 'id', this.domId ) - } - - - - - // We want a way to easily get to the circuit - // from this interface’s DOM element. - // (But we don’t need a way to reference this DOM element - // from the circuit. A circuit can have many DOM elements!) - // And we also want an easy way to reference this DOM element - // from this Editor instance. - - circuitEl.circuit = circuit - this.domElement = circuitEl - - - // Create a toolbar for containing buttons. - - const toolbarEl = createDiv() - circuitEl.appendChild( toolbarEl ) - toolbarEl.classList.add( 'Q-circuit-toolbar' ) - - - // Create a toggle switch for locking the circuit. - - const lockToggle = createDiv() - toolbarEl.appendChild( lockToggle ) - lockToggle.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-lock' ) - lockToggle.setAttribute( 'title', 'Lock / unlock' ) - lockToggle.innerText = '🔓' - - - // Create an “Undo” button - // that enables and disables - // based on available undo history. - - const undoButton = createDiv() - toolbarEl.appendChild( undoButton ) - undoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-undo' ) - undoButton.setAttribute( 'title', 'Undo' ) - undoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) - undoButton.innerHTML = '⟲' - window.addEventListener( 'Q.History undo is depleted', function( event ){ - - if( event.detail.instance === circuit ) - undoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) - }) - window.addEventListener( 'Q.History undo is capable', function( event ){ - - if( event.detail.instance === circuit ) - undoButton.removeAttribute( 'Q-disabled' ) - }) - - - // Create an “Redo” button - // that enables and disables - // based on available redo history. - - const redoButton = createDiv() - toolbarEl.appendChild( redoButton ) - redoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-redo' ) - redoButton.setAttribute( 'title', 'Redo' ) - redoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) - redoButton.innerHTML = '⟳' - window.addEventListener( 'Q.History redo is depleted', function( event ){ - - if( event.detail.instance === circuit ) - redoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) - }) - window.addEventListener( 'Q.History redo is capable', function( event ){ - - if( event.detail.instance === circuit ) - redoButton.removeAttribute( 'Q-disabled' ) - }) - - - // Create a button for joining - // an “identity cursor” - // and one or more same-gate operations - // into a controlled operation. - // (Will be enabled / disabled from elsewhere.) - - const controlButton = createDiv() - toolbarEl.appendChild( controlButton ) - controlButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-control' ) - controlButton.setAttribute( 'title', 'Create controlled operation' ) - controlButton.setAttribute( 'Q-disabled', 'Q-disabled' ) - controlButton.innerText = 'C' - - - // Create a button for joining - // two “identity cursors” - // into a swap operation. - // (Will be enabled / disabled from elsewhere.) - - const swapButton = createDiv() - toolbarEl.appendChild( swapButton ) - swapButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle-swap' ) - swapButton.setAttribute( 'title', 'Create swap operation' ) - swapButton.setAttribute( 'Q-disabled', 'Q-disabled' ) - swapButton.innerText = 'S' - - - // Create a circuit board container - // so we can house a scrollable circuit board. - - const boardContainerEl = createDiv() - circuitEl.appendChild( boardContainerEl ) - boardContainerEl.classList.add( 'Q-circuit-board-container' ) - //boardContainerEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress ) - boardContainerEl.addEventListener( 'mouseleave', function(){ - Q.Circuit.Editor.unhighlightAll( circuitEl ) - }) - - const boardEl = createDiv() - boardContainerEl.appendChild( boardEl ) - boardEl.classList.add( 'Q-circuit-board' ) - - const backgroundEl = createDiv() - boardEl.appendChild( backgroundEl ) - backgroundEl.classList.add( 'Q-circuit-board-background' ) - - const parameterEl = createDiv() - boardEl.appendChild( parameterEl ) - parameterEl.classList.add( 'Q-parameters-box' ) - // Create background highlight bars - // for each row. - - for( let i = 0; i < circuit.bandwidth; i ++ ){ - - const rowEl = createDiv() - backgroundEl.appendChild( rowEl ) - rowEl.style.position = 'relative' - rowEl.style.gridRowStart = i + 2 - rowEl.style.gridColumnStart = 1 - rowEl.style.gridColumnEnd = Q.Circuit.Editor.momentIndexToGridColumn( circuit.timewidth ) + 1 - rowEl.setAttribute( 'register-index', i + 1 ) - - const wireEl = createDiv() - rowEl.appendChild( wireEl ) - wireEl.classList.add( 'Q-circuit-register-wire' ) - } - - - // Create background highlight bars - // for each column. - - for( let i = 0; i < circuit.timewidth; i ++ ){ - - const columnEl = createDiv() - backgroundEl.appendChild( columnEl ) - columnEl.style.gridRowStart = 2 - columnEl.style.gridRowEnd = Q.Circuit.Editor.registerIndexToGridRow( circuit.bandwidth ) + 1 - columnEl.style.gridColumnStart = i + 3 - columnEl.setAttribute( 'moment-index', i + 1 ) - } - - - // Create the circuit board foreground - // for all interactive elements. - - const foregroundEl = createDiv() - boardEl.appendChild( foregroundEl ) - foregroundEl.classList.add( 'Q-circuit-board-foreground' ) - - - // Add “Select All” toggle button to upper-left corner. - - const selectallEl = createDiv() - foregroundEl.appendChild( selectallEl ) - selectallEl.classList.add( 'Q-circuit-header', 'Q-circuit-selectall' ) - selectallEl.setAttribute( 'title', 'Select all' ) - selectallEl.setAttribute( 'moment-index', '0' ) - selectallEl.setAttribute( 'register-index', '0' ) - selectallEl.innerHTML = '↘' - - - // Add register index symbols to left-hand column. - - for( let i = 0; i < circuit.bandwidth; i ++ ){ - - const - registerIndex = i + 1, - registersymbolEl = createDiv() - - foregroundEl.appendChild( registersymbolEl ) - registersymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-label' ) - registersymbolEl.setAttribute( 'title', 'Register '+ registerIndex +' of '+ circuit.bandwidth ) - registersymbolEl.setAttribute( 'register-index', registerIndex ) - registersymbolEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( registerIndex ) - registersymbolEl.innerText = registerIndex - } - - - // Add “Add register” button.q - - const addRegisterEl = createDiv() - foregroundEl.appendChild( addRegisterEl ) - addRegisterEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-add' ) - addRegisterEl.setAttribute( 'title', 'Add register' ) - addRegisterEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( circuit.bandwidth + 1 ) - addRegisterEl.innerText = '+' - - - // Add moment index symbols to top row. - - for( let i = 0; i < circuit.timewidth; i ++ ){ - - const - momentIndex = i + 1, - momentsymbolEl = createDiv() - - foregroundEl.appendChild( momentsymbolEl ) - momentsymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-label' ) - momentsymbolEl.setAttribute( 'title', 'Moment '+ momentIndex +' of '+ circuit.timewidth ) - momentsymbolEl.setAttribute( 'moment-index', momentIndex ) - momentsymbolEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ) - momentsymbolEl.innerText = momentIndex - } - - - // Add “Add moment” button. - - const addMomentEl = createDiv() - foregroundEl.appendChild( addMomentEl ) - addMomentEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-add' ) - addMomentEl.setAttribute( 'title', 'Add moment' ) - addMomentEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( circuit.timewidth + 1 ) - addMomentEl.innerText = '+' - - - // Add input values. - - circuit.qubits.forEach( function( qubit, i ){ - - const - rowIndex = i + 1, - inputEl = createDiv() - - inputEl.classList.add( 'Q-circuit-header', 'Q-circuit-input' ) - inputEl.setAttribute( 'title', `Qubit #${ rowIndex } starting value` ) - inputEl.setAttribute( 'register-index', rowIndex ) - inputEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( rowIndex ) - inputEl.innerText = qubit.beta.toText() - foregroundEl.appendChild( inputEl ) - }) - - - // Add operations. - - circuit.operations.forEach( function( operation ){ - Q.Circuit.Editor.set( circuitEl, operation ) - }) - - - // Add event listeners. - - circuitEl.addEventListener( 'mousedown', Q.Circuit.Editor.onPointerPress ) - circuitEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress ) - window.addEventListener( - - 'Q.Circuit.set$', - Q.Circuit.Editor.prototype.onExternalSet.bind( this ) - ) - window.addEventListener( - - 'Q.Circuit.clear$', - Q.Circuit.Editor.prototype.onExternalClear.bind( this ) - ) - - - // How can we interact with this circuit - // through code? (How cool is this?!) - - const referenceEl = document.createElement( 'p' ) - circuitEl.appendChild( referenceEl ) - referenceEl.innerHTML = ` - This circuit is accessible in your - JavaScript console - as document.getElementById('${ this.domId }').circuit` - //document.getElementById('Q-Editor-0').circuit - //$('#${ this.domId }') - - - // Put a note in the JavaScript console - // that includes how to reference the circuit via code - // and an ASCII diagram for reference. - - Q.log( 0.5, - - `\n\nCreated a DOM interface for $('#${ this.domId }').circuit\n\n`, - circuit.toDiagram(), - '\n\n\n' - ) -} - - -// Augment Q.Circuit to have this functionality. - -Q.Circuit.toDom = function( circuit, targetEl ){ - - return new Q.Circuit.Editor( circuit, targetEl ).domElement -} -Q.Circuit.prototype.toDom = function( targetEl ){ - - return new Q.Circuit.Editor( this, targetEl ).domElement -} - - - - - - - - -Object.assign( Q.Circuit.Editor, { - - index: 0, - help: function(){ return Q.help( this )}, - dragEl: null, - gridColumnToMomentIndex: function( gridColumn ){ return +gridColumn - 2 }, - momentIndexToGridColumn: function( momentIndex ){ return momentIndex + 2 }, - gridRowToRegisterIndex: function( gridRow ){ return +gridRow - 1 }, - registerIndexToGridRow: function( registerIndex ){ return registerIndex + 1 }, - gridSize: 4,// CSS: grid-auto-columns = grid-auto-rows = 4rem. - pointToGrid: function( p ){ - - - // Take a 1-dimensional point value - // (so either an X or a Y but not both) - // and return what CSS grid cell contains it - // based on our 4rem × 4rem grid setup. - - const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize ) - return 1 + Math.floor( p / ( rem * Q.Circuit.Editor.gridSize )) - }, - gridToPoint: function( g ){ - - - // Take a 1-dimensional grid cell value - // (so either a row or a column but not both) - // and return the minimum point value it contains. - - const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize ) - return rem * Q.Circuit.Editor.gridSize * ( g - 1 ) - }, - getInteractionCoordinates: function( event, pageOrClient ){ - - if( typeof pageOrClient !== 'string' ) pageOrClient = 'client'//page - if( event.changedTouches && - event.changedTouches.length ) return { - - x: event.changedTouches[ 0 ][ pageOrClient +'X' ], - y: event.changedTouches[ 0 ][ pageOrClient +'Y' ] - } - return { - - x: event[ pageOrClient +'X' ], - y: event[ pageOrClient +'Y' ] - } - }, - createPalette: function( targetEl ){ - - if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl ) - - const - paletteEl = targetEl instanceof HTMLElement ? targetEl : document.createElement( 'div' ), - randomRangeAndSign = function( min, max ){ - - const r = min + Math.random() * ( max - min ) - return Math.floor( Math.random() * 2 ) ? r : -r - } - - //ltnln: added missing Braket operations. - paletteEl.classList.add( 'Q-circuit-palette' ) - 'H,X,Y,Z,P,Rx,Ry,Rz,U,V,V†,S*,S†,T,T†,00,01,10,√S,iS,XX,XY,YY,ZZ,*' - .split( ',' ) - .forEach( function( symbol ){ - - const gate = Q.Gate.findBySymbol( symbol ) - - const operationEl = document.createElement( 'div' ) - paletteEl.appendChild( operationEl ) - operationEl.classList.add( 'Q-circuit-operation' ) - operationEl.classList.add( 'Q-circuit-operation-'+ gate.nameCss ) - operationEl.setAttribute( 'gate-symbol', symbol ) - operationEl.setAttribute( 'title', gate.name ) - - const tileEl = document.createElement( 'div' ) - operationEl.appendChild( tileEl ) - tileEl.classList.add( 'Q-circuit-operation-tile' ) - if( symbol !== Q.Gate.CURSOR.symbol ) tileEl.innerText = symbol - - ;[ 'before', 'after' ].forEach( function( layer ){ - - tileEl.style.setProperty( '--Q-'+ layer +'-rotation', randomRangeAndSign( 0.5, 4 ) +'deg' ) - tileEl.style.setProperty( '--Q-'+ layer +'-x', randomRangeAndSign( 1, 4 ) +'px' ) - tileEl.style.setProperty( '--Q-'+ layer +'-y', randomRangeAndSign( 1, 3 ) +'px' ) - }) - }) - - paletteEl.addEventListener( 'mousedown', Q.Circuit.Editor.onPointerPress ) - paletteEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress ) - return paletteEl - } -}) - - - - - - - ///////////////////////// - // // - // Operation CLEAR // - // // -///////////////////////// - - -Q.Circuit.Editor.prototype.onExternalClear = function( event ){ - - if( event.detail.circuit === this.circuit ){ - - Q.Circuit.Editor.clear( this.domElement, { - - momentIndex: event.detail.momentIndex, - registerIndices: event.detail.registerIndices - }) - } -} -Q.Circuit.Editor.clear = function( circuitEl, operation ){ - - const momentIndex = operation.momentIndex - operation.registerIndices.forEach( function( registerIndex ){ - - Array - .from( circuitEl.querySelectorAll( - - `[moment-index="${ momentIndex }"]`+ - `[register-index="${ registerIndex }"]` - - )) - .forEach( function( op ){ - - op.parentNode.removeChild( op ) - }) - }) -} - - - - - - - /////////////////////// - // // - // Operation SET // - // // -/////////////////////// - - -Q.Circuit.Editor.prototype.onExternalSet = function( event ){ - - if( event.detail.circuit === this.circuit ){ - - Q.Circuit.Editor.set( this.domElement, event.detail.operation ) - } -} -Q.Circuit.Editor.set = function( circuitEl, operation ){ - const - backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ), - foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ), - circuit = circuitEl.circuit, - operationIndex = circuitEl.circuit.operations.indexOf( operation ) - - operation.registerIndices.forEach( function( registerIndex, i ){ - const operationEl = document.createElement( 'div' ) - foregroundEl.appendChild( operationEl ) - operationEl.classList.add( 'Q-circuit-operation', 'Q-circuit-operation-'+ operation.gate.nameCss ) - // operationEl.setAttribute( 'operation-index', operationIndex ) - operationEl.setAttribute( 'gate-symbol', operation.gate.symbol ) - operationEl.setAttribute( 'gate-index', operation.gate.index )// Used as an application-wide unique ID! - operationEl.setAttribute( 'moment-index', operation.momentIndex ) - operationEl.setAttribute( 'register-index', registerIndex ) - operationEl.setAttribute( 'register-array-index', i )// Where within the registerIndices array is this operations fragment located? - operationEl.setAttribute( 'is-controlled', operation.isControlled ) - operationEl.setAttribute( 'title', operation.gate.name ) - operationEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( operation.momentIndex ) - operationEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( registerIndex ) - if( operation.gate.has_parameters ) Object.keys(operation.gate.parameters).forEach( element => { - operationEl.setAttribute( element, operation.gate.parameters[element] ) //adds a parameter attribute to the operation! - }) - const tileEl = document.createElement( 'div' ) - operationEl.appendChild( tileEl ) - tileEl.classList.add( 'Q-circuit-operation-tile' ) - if( operation.gate.symbol !== Q.Gate.CURSOR.symbol ) tileEl.innerText = operation.gate.symbol - - - // Add operation link wires - // for multi-qubit operations. - - if( operation.registerIndices.length > 1 ){ - - operationEl.setAttribute( 'register-indices', operation.registerIndices ) - operationEl.setAttribute( 'register-indices-index', i ) - operationEl.setAttribute( - - 'sibling-indices', - operation.registerIndices - .filter( function( siblingRegisterIndex ){ - - return registerIndex !== siblingRegisterIndex - }) - ) - operation.registerIndices.forEach( function( registerIndex, i ){ - - if( i < operation.registerIndices.length - 1 ){ - - const - siblingRegisterIndex = operation.registerIndices[ i + 1 ], - registerDelta = Math.abs( siblingRegisterIndex - registerIndex ), - start = Math.min( registerIndex, siblingRegisterIndex ), - end = Math.max( registerIndex, siblingRegisterIndex ), - containerEl = document.createElement( 'div' ), - linkEl = document.createElement( 'div' ) - - backgroundEl.appendChild( containerEl ) - containerEl.setAttribute( 'moment-index', operation.momentIndex ) - containerEl.setAttribute( 'register-index', registerIndex ) - containerEl.classList.add( 'Q-circuit-operation-link-container' ) - containerEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( start ) - containerEl.style.gridRowEnd = Q.Circuit.Editor.registerIndexToGridRow( end + 1 ) - containerEl.style.gridColumn = Q.Circuit.Editor.momentIndexToGridColumn( operation.momentIndex ) - - containerEl.appendChild( linkEl ) - linkEl.classList.add( 'Q-circuit-operation-link' ) - if( registerDelta > 1 ) linkEl.classList.add( 'Q-circuit-operation-link-curved' ) - } - }) - if( operation.isControlled && i === 0 ){ - operationEl.classList.add( 'Q-circuit-operation-control' ) - operationEl.setAttribute( 'title', 'Control' ) - tileEl.innerText = '' - } - else operationEl.classList.add( 'Q-circuit-operation-target' ) - } - }) -} - - - - -Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){ - - const - selectedOperations = Array - .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) - - - // We must have at least two operations selected, - // hopefully a control and something else, - // in order to attempt a join. - - if( selectedOperations.length < 2 ) return false - - - // Note the different moment indices present - // among the selected operations. - - const moments = selectedOperations.reduce( function( moments, operationEl ){ - - moments[ operationEl.getAttribute( 'moment-index' )] = true - return moments - - }, {} ) - - - // All selected operations must be in the same moment. - - if( Object.keys( moments ).length > 1 ) return false - - - // If there are multi-register operations present, - // regardless of whether those are controls or swaps, - // all siblings must be present - // in order to join a new gate to this selection. - - // I’m sure we can make this whole routine much more efficient - // but its results are correct and boy am I tired ;) - - const allSiblingsPresent = selectedOperations - .reduce( function( status, operationEl ){ - - const registerIndicesString = operationEl.getAttribute( 'register-indices' ) - - - // If it’s a single-register operation - // there’s no need to search further. - - if( !registerIndicesString ) return status - - - // How many registers are in use - // by this operation? - - const - registerIndicesLength = registerIndicesString - .split( ',' ) - .map( function( registerIndex ){ - - return +registerIndex - }) - .length, - - - // How many of this operation’s siblings - // (including itself) can we find? - - allSiblingsLength = selectedOperations - .reduce( function( siblings, operationEl ){ - - if( operationEl.getAttribute( 'register-indices' ) === registerIndicesString ){ - - siblings.push( operationEl ) - } - return siblings - - }, []) - .length - - - // Did we find all of the siblings for this operation? - // Square that with previous searches. - - return status && allSiblingsLength === registerIndicesLength - - }, true ) - - - // If we’re missing some siblings - // then we cannot modify whatever we have selected here. - - if( allSiblingsPresent !== true ) return false - - // Note the different gate types present - // among the selected operations. - - const gates = selectedOperations.reduce( function( gates, operationEl ){ - const gateSymbol = operationEl.getAttribute( 'gate-symbol' ) - if( !Q.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1 - else gates[ gateSymbol ] ++ - return gates - - }, {} ) - - - // Note if each operation is already controlled or not. - - const { - - totalControlled, - totalNotControlled - - } = selectedOperations - .reduce( function( stats, operationEl ){ - - if( operationEl.getAttribute( 'is-controlled' ) === 'true' ) - stats.totalControlled ++ - else stats.totalNotControlled ++ - return stats - - }, { - - totalControlled: 0, - totalNotControlled: 0 - }) - - // This could be ONE “identity cursor” - // and one or more of a regular single gate - // that is NOT already controlled. - - if( gates[ Q.Gate.CURSOR.symbol ] === 1 && - Object.keys( gates ).length === 2 && - totalNotControlled === selectedOperations.length ){ - - return true - } - - - // There’s NO “identity cursor” - // but there is one or more of specific gate type - // and at least one of those is already controlled. - - if( gates[ Q.Gate.CURSOR.symbol ] === undefined && - Object.keys( gates ).length === 1 && - totalControlled > 0 && - totalNotControlled > 0 ){ - - return true - } - - - // Any other combination allowed? Nope! - - return false -} -Q.Circuit.Editor.createControl = function( circuitEl ){ - - if( Q.Circuit.Editor.isValidControlCandidate( circuitEl ) !== true ) return this - - - const - circuit = circuitEl.circuit, - selectedOperations = Array - .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )), - - - // Are any of these controlled operations?? - // If so, we need to find its control component - // and re-use it. - - existingControlEl = selectedOperations.find( function( operationEl ){ - - return ( - - operationEl.getAttribute( 'is-controlled' ) === 'true' && - operationEl.getAttribute( 'register-array-index' ) === '0' - ) - }), - - - // One control. One or more targets. - - control = existingControlEl || selectedOperations - .find( function( el ){ - - return el.getAttribute( 'gate-symbol' ) === Q.Gate.CURSOR.symbol - }), - targets = selectedOperations - .reduce( function( targets, el ){ - - //if( el.getAttribute( 'gate-symbol' ) !== '!' ) targets.push( el ) - if( el !== control ) targets.push( el ) - return targets - - }, [] ) - - - // Ready to roll. - - circuit.history.createEntry$() - selectedOperations.forEach( function( operationEl ){ - - circuit.clear$( - - +operationEl.getAttribute( 'moment-index' ), - +operationEl.getAttribute( 'register-index' ) - ) - }) - circuit.set$( - targets[ 0 ].getAttribute( 'gate-symbol' ), - +control.getAttribute( 'moment-index' ), - [ +control.getAttribute( 'register-index' )].concat( - - targets.reduce( function( registers, operationEl ){ - - registers.push( +operationEl.getAttribute( 'register-index' )) - return registers - - }, [] ) - ) - ) - - - // Update our toolbar button states. - - Q.Circuit.Editor.onSelectionChanged( circuitEl ) - Q.Circuit.Editor.onCircuitChanged( circuitEl ) - - return this -} - - - - -Q.Circuit.Editor.isValidSwapCandidate = function( circuitEl ){ - - const - selectedOperations = Array - .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) - - - // We can only swap between two registers. - // No crazy rotation-swap bullshit. (Yet.) - if( selectedOperations.length !== 2 ) return false - - - // Both operations must be “identity cursors.” - // If so, we are good to go. - - areBothCursors = selectedOperations.every( function( operationEl ){ - - return operationEl.getAttribute( 'gate-symbol' ) === Q.Gate.CURSOR.symbol - }) - if( areBothCursors ) return true - - - // Otherwise this is not a valid swap candidate. - - return false -} -Q.Circuit.Editor.createSwap = function( circuitEl ){ - - if( Q.Circuit.Editor.isValidSwapCandidate( circuitEl ) !== true ) return this - - const - selectedOperations = Array - .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )), - momentIndex = +selectedOperations[ 0 ].getAttribute( 'moment-index' ) - registerIndices = selectedOperations - .reduce( function( registerIndices, operationEl ){ - - registerIndices.push( +operationEl.getAttribute( 'register-index' )) - return registerIndices - - }, [] ), - circuit = circuitEl.circuit - - - // Create the swap operation. - - circuit.history.createEntry$() - selectedOperations.forEach( function( operation ){ - - circuit.clear$( - - +operation.getAttribute( 'moment-index' ), - +operation.getAttribute( 'register-index' ) - ) - }) - circuit.set$( - - Q.Gate.SWAP, - momentIndex, - registerIndices - ) - - - // Update our toolbar button states. - - Q.Circuit.Editor.onSelectionChanged( circuitEl ) - Q.Circuit.Editor.onCircuitChanged( circuitEl ) - - return this -} - - - - -Q.Circuit.Editor.onSelectionChanged = function( circuitEl ){ - - const controlButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-control' ) - if( Q.Circuit.Editor.isValidControlCandidate( circuitEl )){ - - controlButtonEl.removeAttribute( 'Q-disabled' ) - } - else controlButtonEl.setAttribute( 'Q-disabled', true ) - - const swapButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-swap' ) - if( Q.Circuit.Editor.isValidSwapCandidate( circuitEl )){ - - swapButtonEl.removeAttribute( 'Q-disabled' ) - } - else swapButtonEl.setAttribute( 'Q-disabled', true ) -} -Q.Circuit.Editor.onCircuitChanged = function( circuitEl ){ - - const circuit = circuitEl.circuit - window.dispatchEvent( new CustomEvent( - - 'Q gui altered circuit', - { detail: { circuit: circuit }} - )) - - // Should we trigger a circuit.evaluate$() here? - // Particularly when we move all that to a new thread?? - // console.log( originCircuit.report$() ) ?? -} - - - - - -Q.Circuit.Editor.unhighlightAll = function( circuitEl ){ - - Array.from( circuitEl.querySelectorAll( - - '.Q-circuit-board-background > div,'+ - '.Q-circuit-board-foreground > div' - )) - .forEach( function( el ){ - - el.classList.remove( 'Q-circuit-cell-highlighted' ) - }) -} - - - - - - - ////////////////////// - // // - // Pointer MOVE // - // // -////////////////////// - - -Q.Circuit.Editor.onPointerMove = function( event ){ - - - // We need our cursor coordinates straight away. - // We’ll use that both for dragging (immediately below) - // and for hover highlighting (further below). - // Let’s also hold on to a list of all DOM elements - // that contain this X, Y point - // and also see if one of those is a circuit board container. - - const - { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ), - foundEls = document.elementsFromPoint( x, y ), - boardContainerEl = foundEls.find( function( el ){ - - return el.classList.contains( 'Q-circuit-board-container' ) - }) - - - // Are we in the middle of a circuit clipboard drag? - // If so we need to move that thing! - - if( Q.Circuit.Editor.dragEl !== null ){ - - - // ex. Don’t scroll on touch devices! - - event.preventDefault() - - - // This was a very useful resource - // for a reality check on DOM coordinates: - // https://javascript.info/coordinates - - Q.Circuit.Editor.dragEl.style.left = ( x + window.pageXOffset + Q.Circuit.Editor.dragEl.offsetX ) +'px' - Q.Circuit.Editor.dragEl.style.top = ( y + window.pageYOffset + Q.Circuit.Editor.dragEl.offsetY ) +'px' - - if( !boardContainerEl && Q.Circuit.Editor.dragEl.circuitEl ) Q.Circuit.Editor.dragEl.classList.add( 'Q-circuit-clipboard-danger' ) - else Q.Circuit.Editor.dragEl.classList.remove( 'Q-circuit-clipboard-danger' ) - } - - - // If we’re not over a circuit board container - // then there’s no highlighting work to do - // so let’s bail now. - - if( !boardContainerEl ) return - - - // Now we know we have a circuit board - // so we must have a circuit - // and if that’s locked then highlighting changes allowed! - - const circuitEl = boardContainerEl.closest( '.Q-circuit' ) - if( circuitEl.classList.contains( 'Q-circuit-locked' )) return - - - // Ok, we’ve found a circuit board. - // First, un-highlight everything. - - Array.from( boardContainerEl.querySelectorAll(` - - .Q-circuit-board-background > div, - .Q-circuit-board-foreground > div - - `)).forEach( function( el ){ - - el.classList.remove( 'Q-circuit-cell-highlighted' ) - }) - - - // Let’s prioritize any element that is “sticky” - // which means it can appear OVER another grid cell. - const - cellEl = foundEls.find( function( el ){ - - const style = window.getComputedStyle( el ) - return ( - - style.position === 'sticky' && ( - - el.getAttribute( 'moment-index' ) !== null || - el.getAttribute( 'register-index' ) !== null - ) - ) - }), - highlightByQuery = function( query ){ - - Array.from( boardContainerEl.querySelectorAll( query )) - .forEach( function( el ){ - - el.classList.add( 'Q-circuit-cell-highlighted' ) - }) - } - - - // If we’ve found one of these “sticky” cells - // let’s use its moment and/or register data - // to highlight moments or registers (or all). - - if( cellEl ){ - - const - momentIndex = cellEl.getAttribute( 'moment-index' ), - registerIndex = cellEl.getAttribute( 'register-index' ) - - if( momentIndex === null ){ - - highlightByQuery( `div[register-index="${ registerIndex }"]` ) - return - } - if( registerIndex === null ){ - - highlightByQuery( `div[moment-index="${ momentIndex }"]` ) - return - } - highlightByQuery(` - - .Q-circuit-board-background > div[moment-index], - .Q-circuit-board-foreground > .Q-circuit-operation - - `) - return - } - - - // Ok, we know we’re hovering over the circuit board - // but we’re not on a “sticky” cell. - // We might be over an operation, but we might not. - // No matter -- we’ll infer the moment and register indices - // from the cursor position. - - const - boardElBounds = boardContainerEl.getBoundingClientRect(), - xLocal = x - boardElBounds.left + boardContainerEl.scrollLeft + 1, - yLocal = y - boardElBounds.top + boardContainerEl.scrollTop + 1, - columnIndex = Q.Circuit.Editor.pointToGrid( xLocal ), - rowIndex = Q.Circuit.Editor.pointToGrid( yLocal ), - momentIndex = Q.Circuit.Editor.gridColumnToMomentIndex( columnIndex ), - registerIndex = Q.Circuit.Editor.gridRowToRegisterIndex( rowIndex ) - - - // If this hover is “out of bounds” - // ie. on the same row or column as an “Add register” or “Add moment” button - // then let’s not highlight anything. - - if( momentIndex > circuitEl.circuit.timewidth || - registerIndex > circuitEl.circuit.bandwidth ) return - - - // If we’re at 0, 0 or below that either means - // we’re over the “Select all” button (already taken care of above) - // or over the lock toggle button. - // Either way, it’s time to bail. - - if( momentIndex < 1 || registerIndex < 1 ) return - - - // If we’ve made it this far that means - // we have valid moment and register indices. - // Highlight them! - - highlightByQuery(` - - div[moment-index="${ momentIndex }"], - div[register-index="${ registerIndex }"] - `) - return -} - - - - /////////////////////// - // // - // Pointer PRESS // - // // -/////////////////////// - - -Q.Circuit.Editor.onPointerPress = function( event ){ - // This is just a safety net - // in case something terrible has ocurred. - // (ex. Did the user click and then their mouse ran - // outside the window but browser didn’t catch it?) - console.log("event target: ", event.target); - if( Q.Circuit.Editor.dragEl !== null ){ - - Q.Circuit.Editor.onPointerRelease( event ) - return - } - const - targetEl = event.target, - circuitEl = targetEl.closest( '.Q-circuit' ), - paletteEl = targetEl.closest( '.Q-circuit-palette' ) - parameterEl = targetEl.closest( '.Q-parameters-box' ) - - // If we can’t find a circuit that’s a really bad sign - // considering this event should be fired when a circuit - // is clicked on. So... bail! - - if( !circuitEl && !paletteEl ) return - - // This is a bit of a gamble. - // There’s a possibility we’re not going to drag anything, - // but we’ll prep these variables here anyway - // because both branches of if( circuitEl ) and if( paletteEl ) - // below will have access to this scope. - - dragEl = document.createElement( 'div' ) - dragEl.classList.add( 'Q-circuit-clipboard' ) - const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ) - - - // Are we dealing with a circuit interface? - // ie. NOT a palette interface. - - if( circuitEl && !parameterEl ){ - - // Shall we toggle the circuit lock? - - const - circuit = circuitEl.circuit, - circuitIsLocked = circuitEl.classList.contains( 'Q-circuit-locked' ), - lockEl = targetEl.closest( '.Q-circuit-toggle-lock' ) - - if( lockEl ){ - - // const toolbarEl = Array.from( circuitEl.querySelectorAll( '.Q-circuit-button' )) - if( circuitIsLocked ){ - - circuitEl.classList.remove( 'Q-circuit-locked' ) - lockEl.innerText = '🔓' - } - else { - - circuitEl.classList.add( 'Q-circuit-locked' ) - lockEl.innerText = '🔒' - Q.Circuit.Editor.unhighlightAll( circuitEl ) - } - - - // We’ve toggled the circuit lock button - // so we should prevent further propagation - // before proceeding further. - // That includes running all this code again - // if it was originally fired by a mouse event - // and about to be fired by a touch event! - - event.preventDefault() - event.stopPropagation() - return - } - - - // If our circuit is already “locked” - // then there’s nothing more to do here. - - if( circuitIsLocked ) { - - Q.warn( `User attempted to interact with a circuit editor but it was locked.` ) - return - } - - - const - cellEl = targetEl.closest(` - - .Q-circuit-board-foreground > div, - .Q-circuit-palette > div - `), - undoEl = targetEl.closest( '.Q-circuit-button-undo' ), - redoEl = targetEl.closest( '.Q-circuit-button-redo' ), - controlEl = targetEl.closest( '.Q-circuit-toggle-control' ), - swapEl = targetEl.closest( '.Q-circuit-toggle-swap' ), - addMomentEl = targetEl.closest( '.Q-circuit-moment-add' ), - addRegisterEl = targetEl.closest( '.Q-circuit-register-add' ) - - if( !cellEl && - !undoEl && - !redoEl && - !controlEl && - !swapEl && - !addMomentEl && - !addRegisterEl ) return - - - // By this point we know that the circuit is unlocked - // and that we’ll activate a button / drag event / etc. - // So we need to hault futher event propagation - // including running this exact code again if this was - // fired by a touch event and about to again by mouse. - // This may SEEM redundant because we did this above - // within the lock-toggle button code - // but we needed to NOT stop propagation if the circuit - // was already locked -- for scrolling and such. - - event.preventDefault() - event.stopPropagation() - - - if( undoEl && circuit.history.undo$() ){ - - Q.Circuit.Editor.onSelectionChanged( circuitEl ) - Q.Circuit.Editor.onCircuitChanged( circuitEl ) - } - if( redoEl && circuit.history.redo$() ){ - - Q.Circuit.Editor.onSelectionChanged( circuitEl ) - Q.Circuit.Editor.onCircuitChanged( circuitEl ) - } - if( controlEl ) Q.Circuit.Editor.createControl( circuitEl ) - if( swapEl ) Q.Circuit.Editor.createSwap( circuitEl ) - if( addMomentEl ) console.log( '→ Add moment' ) - if( addRegisterEl ) console.log( '→ Add register' ) - - - // We’re done dealing with external buttons. - // So if we can’t find a circuit CELL - // then there’s nothing more to do here. - - if( !cellEl ) return - - // Once we know what cell we’ve pressed on - // we can get the momentIndex and registerIndex - // from its pre-defined attributes. - // NOTE that we are getting CSS grid column and row - // from our own conversion function and NOT from - // asking its styles. Why? Because browsers convert - // grid commands to a shorthand less easily parsable - // and therefore makes our code and reasoning - // more prone to quirks / errors. Trust me! - - const - momentIndex = +cellEl.getAttribute( 'moment-index' ), - registerIndex = +cellEl.getAttribute( 'register-index' ), - columnIndex = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ), - rowIndex = Q.Circuit.Editor.registerIndexToGridRow( registerIndex ) - - - // Looks like our circuit is NOT locked - // and we have a valid circuit CELL - // so let’s find everything else we could need. - - const - selectallEl = targetEl.closest( '.Q-circuit-selectall' ), - registersymbolEl = targetEl.closest( '.Q-circuit-register-label' ), - momentsymbolEl = targetEl.closest( '.Q-circuit-moment-label' ), - inputEl = targetEl.closest( '.Q-circuit-input' ), - operationEl = targetEl.closest( '.Q-circuit-operation' ) - - // +++++++++++++++ - // We’ll have to add some input editing capability later... - // Of course you can already do this in code! - // For now though most quantum code assumes all qubits - // begin with a value of zero so this is mostly ok ;) - - if( inputEl ){ - - console.log( '→ Edit input Qubit value at', registerIndex ) - return - } - - - // Let’s inspect a group of items via a CSS query. - // If any of them are NOT “selected” (highlighted) - // then select them all. - // But if ALL of them are already selected - // then UNSELECT them all. - - function toggleSelection( query ){ - - const - operations = Array.from( circuitEl.querySelectorAll( query )), - operationsSelectedLength = operations.reduce( function( sum, element ){ - - sum += +element.classList.contains( 'Q-circuit-cell-selected' ) - return sum - - }, 0 ) - - if( operationsSelectedLength === operations.length ){ - - operations.forEach( function( el ){ - el.classList.remove( 'Q-circuit-cell-selected' ) - }) - } - else { - - operations.forEach( function( el ){ - - el.classList.add( 'Q-circuit-cell-selected' ) - }) - } - Q.Circuit.Editor.onSelectionChanged( circuitEl ) - } - - - // Clicking on the “selectAll” button - // or any of the Moment symbols / Register symbols - // causes a selection toggle. - // In the future we may want to add - // dragging of entire Moment columns / Register rows - // to splice them out / insert them elsewhere - // when a user clicks and drags them. - - if( selectallEl ){ - - toggleSelection( '.Q-circuit-operation' ) - return - } - if( momentsymbolEl ){ - - toggleSelection( `.Q-circuit-operation[moment-index="${ momentIndex }"]` ) - return - } - if( registersymbolEl ){ - - toggleSelection( `.Q-circuit-operation[register-index="${ registerIndex }"]` ) - return - } - - - // Right here we can made a big decision: - // If you’re not pressing on an operation - // then GO HOME. - - if( !operationEl ) return - // If we've doubleclicked on an operation and the operation has parameters, we should be able - // to edit those parameters regardless of whether or not the circuit is locked. - if( event.detail == 2) { - const operation = Q.Gate.findBySymbol(operationEl.getAttribute( 'gate-symbol' )) - if( operation.has_parameters ) { - Q.Circuit.Editor.onDoubleclick( event, operationEl ) - return - } - } - - // Ok now we know we are dealing with an operation. - // This preserved selection state information - // will be useful for when onPointerRelease is fired. - - if( operationEl.classList.contains( 'Q-circuit-cell-selected' )){ - operationEl.wasSelected = true - } - else operationEl.wasSelected = false - - - // And now we can proceed knowing that - // we need to select this operation - // and possibly drag it - // as well as any other selected operations. - - operationEl.classList.add( 'Q-circuit-cell-selected' ) - const selectedOperations = Array.from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) - dragEl.circuitEl = circuitEl - dragEl.originEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) - - - // These are the default values; - // will be used if we’re only dragging one operation around. - // But if dragging more than one operation - // and we’re dragging the clipboard by an operation - // that is NOT in the upper-left corner of the clipboard - // then we need to know what the offset is. - // (Will be calculated below.) - - dragEl.columnIndexOffset = 1 - dragEl.rowIndexOffset = 1 - - - // Now collect all of the selected operations, - // rip them from the circuit board’s foreground layer - // and place them on the clipboard. - - let - columnIndexMin = Infinity, - rowIndexMin = Infinity - - selectedOperations.forEach( function( el ){ - - - // WORTH REPEATING: - // Once we know what cell we’ve pressed on - // we can get the momentIndex and registerIndex - // from its pre-defined attributes. - // NOTE that we are getting CSS grid column and row - // from our own conversion function and NOT from - // asking its styles. Why? Because browsers convert - // grid commands to a shorthand less easily parsable - // and therefore makes our code and reasoning - // more prone to quirks / errors. Trust me! - - const - momentIndex = +el.getAttribute( 'moment-index' ), - registerIndex = +el.getAttribute( 'register-index' ), - columnIndex = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ), - rowIndex = Q.Circuit.Editor.registerIndexToGridRow( registerIndex ) - - columnIndexMin = Math.min( columnIndexMin, columnIndex ) - rowIndexMin = Math.min( rowIndexMin, rowIndex ) - el.classList.remove( 'Q-circuit-cell-selected' ) - el.origin = { momentIndex, registerIndex, columnIndex, rowIndex } - dragEl.appendChild( el ) - }) - selectedOperations.forEach( function( el ){ - - const - columnIndexForClipboard = 1 + el.origin.columnIndex - columnIndexMin, - rowIndexForClipboard = 1 + el.origin.rowIndex - rowIndexMin - - el.style.gridColumn = columnIndexForClipboard - el.style.gridRow = rowIndexForClipboard - - - // If this operation element is the one we grabbed - // (mostly relevant if we’re moving multiple operations at once) - // we need to know what the “offset” so everything can be - // placed correctly relative to this drag-and-dropped item. - - if( el.origin.columnIndex === columnIndex && - el.origin.rowIndex === rowIndex ){ - - dragEl.columnIndexOffset = columnIndexForClipboard - dragEl.rowIndexOffset = rowIndexForClipboard - } - }) - - - // We need an XY offset that describes the difference - // between the mouse / finger press position - // and the clipboard’s intended upper-left position. - // To do that we need to know the press position (obviously!), - // the upper-left bounds of the circuit board’s foreground, - // and the intended upper-left bound of clipboard. - - const - boardEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ), - bounds = boardEl.getBoundingClientRect(), - minX = Q.Circuit.Editor.gridToPoint( columnIndexMin ), - minY = Q.Circuit.Editor.gridToPoint( rowIndexMin ) - - dragEl.offsetX = bounds.left + minX - x - dragEl.offsetY = bounds.top + minY - y - dragEl.momentIndex = momentIndex - dragEl.registerIndex = registerIndex - } - else if( paletteEl ){ - const operationEl = targetEl.closest( '.Q-circuit-operation' ) - - if( !operationEl ) return - - const - bounds = operationEl.getBoundingClientRect(), - { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ) - - dragEl.appendChild( operationEl.cloneNode( true )) - dragEl.originEl = paletteEl - dragEl.offsetX = bounds.left - x - dragEl.offsetY = bounds.top - y - } - else if( parameterEl ){ - const exitEl = targetEl.closest( '.Q-parameter-box-exit' ) - if( !exitEl ) return - parameterEl.style.display = 'none' - const foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) - operationEl = foregroundEl.querySelector( `[moment-index="${ parameterEl.getAttribute( 'operation-moment-index' )}"]` + - `[register-index="${ parameterEl.getAttribute( 'operation-register-index' )}"]` ) - parameters = {} - operationSkeleton = Q.Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' )) - Object.keys( operationSkeleton.parameters ).forEach( key => { - parameters[ key ] = operationEl.getAttribute( key ) ? operationEl.getAttribute( key ) : operationSkeleton.parameters[ key ] - }) - //on exiting the parameter-input-box, we should update the circuit!! - circuitEl.circuit.set$( - operationEl.getAttribute( 'gate-symbol' ), - +operationEl.getAttribute( 'moment-index' ), - operationEl.getAttribute( 'register-indices' ) ? operationEl.getAttribute( 'register-indices' ).split(',').map( i => +i ) : - [ +operationEl.getAttribute( 'register-index' )], - parameters - ) - parameterEl.innerHTML = "" - return - } - dragEl.timestamp = Date.now() - - - // Append the clipboard to the document, - // establish a global reference to it, - // and trigger a draw of it in the correct spot. - - document.body.appendChild( dragEl ) - Q.Circuit.Editor.dragEl = dragEl - Q.Circuit.Editor.onPointerMove( event ) -} - - - - - - - ///////////////////////// - // // - // Pointer RELEASE // - // // -///////////////////////// - - -Q.Circuit.Editor.onPointerRelease = function( event ){ - - - // If there’s no dragEl then bail immediately. - if( Q.Circuit.Editor.dragEl === null ) return - // Looks like we’re moving forward with this plan, - // so we’ll take control of the input now. - - event.preventDefault() - event.stopPropagation() - - - // We can’t get the drop target from the event. - // Think about it: What was under the mouse / finger - // when this drop event was fired? THE CLIPBOARD ! - // So instead we need to peek at what elements are - // under the mouse / finger, skipping element [0] - // because that will be the clipboard. - - // doing this because elementsFromPoint() doesnt work well with JSDOM for testing purposes - const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ) - const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container") - if( boardContainerAll.length === 0 ) return - let boardContainerEl = Array.from(boardContainerAll).find((element) => { - let rect = element.getBoundingClientRect() - let clientX = rect.left - let clientY = rect.top - let height = element.offsetHeight - let width = element.offsetWidth - return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height ) - }) - returnToOrigin = function(){ - - - // We can only do a “true” return to origin - // if we were dragging from a circuit. - // If we were dragging from a palette - // we can just stop dragging. - - if( Q.Circuit.Editor.dragEl.circuitEl ){ - - Array.from( Q.Circuit.Editor.dragEl.children ).forEach( function( el ){ - - Q.Circuit.Editor.dragEl.originEl.appendChild( el ) - el.style.gridColumn = el.origin.columnIndex - el.style.gridRow = el.origin.rowIndex - if( el.wasSelected === true ) el.classList.remove( 'Q-circuit-cell-selected' ) - else el.classList.add( 'Q-circuit-cell-selected' ) - }) - Q.Circuit.Editor.onSelectionChanged( Q.Circuit.Editor.dragEl.circuitEl ) - } - document.body.removeChild( Q.Circuit.Editor.dragEl ) - Q.Circuit.Editor.dragEl = null - } - - - // If we have not dragged on to a circuit board - // then we’re throwing away this operation. - - if( !boardContainerEl ){ - - if( Q.Circuit.Editor.dragEl.circuitEl ){ - - const - originCircuitEl = Q.Circuit.Editor.dragEl.circuitEl - originCircuit = originCircuitEl.circuit - - originCircuit.history.createEntry$() - Array - .from( Q.Circuit.Editor.dragEl.children ) - .forEach( function( child ){ - - originCircuit.clear$( - - child.origin.momentIndex, - child.origin.registerIndex - ) - }) - Q.Circuit.Editor.onSelectionChanged( originCircuitEl ) - Q.Circuit.Editor.onCircuitChanged( originCircuitEl ) - } - - - // TIME TO DIE. - // Let’s keep a private reference to - // the current clipboard. - - let clipboardToDestroy = Q.Circuit.Editor.dragEl - - - // Now we can remove our dragging reference. - - Q.Circuit.Editor.dragEl = null - - - // Add our CSS animation routine - // which will run for 1 second. - // If we were SUPER AWESOME - // we would have also calculated drag momentum - // and we’d let this glide away! - - clipboardToDestroy.classList.add( 'Q-circuit-clipboard-destroy' ) - - - // And around the time that animation is completing - // we can go ahead and remove our clipboard from the DOM - // and kill the reference. - - setTimeout( function(){ - - document.body.removeChild( clipboardToDestroy ) - clipboardToDestroy = null - - }, 500 ) - - - // No more to do here. Goodbye. - - return - } - - - // If we couldn’t determine a circuitEl - // from the drop target, - // or if there is a target circuit but it’s locked, - // then we need to return these dragged items - // to their original circuit. - - const circuitEl = boardContainerEl.closest( '.Q-circuit' ) - if( circuitEl.classList.contains( 'Q-circuit-locked' )){ - - returnToOrigin() - return - } - - - // Time to get serious. - // Where exactly are we dropping on to this circuit?? - - const - circuit = circuitEl.circuit, - bounds = boardContainerEl.getBoundingClientRect(), - droppedAtX = x - bounds.left + boardContainerEl.scrollLeft, - droppedAtY = y - bounds.top + boardContainerEl.scrollTop, - droppedAtMomentIndex = Q.Circuit.Editor.gridColumnToMomentIndex( - - Q.Circuit.Editor.pointToGrid( droppedAtX ) - ), - droppedAtRegisterIndex = Q.Circuit.Editor.gridRowToRegisterIndex( - - Q.Circuit.Editor.pointToGrid( droppedAtY ) - ), - foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) - - - // If this is a self-drop - // we can also just return to origin and bail. - - if( Q.Circuit.Editor.dragEl.circuitEl === circuitEl && - Q.Circuit.Editor.dragEl.momentIndex === droppedAtMomentIndex && - Q.Circuit.Editor.dragEl.registerIndex === droppedAtRegisterIndex ){ - - returnToOrigin() - return - } - - - // Is this a valid drop target within this circuit? - - if( - droppedAtMomentIndex < 1 || - droppedAtMomentIndex > circuit.timewidth || - droppedAtRegisterIndex < 1 || - droppedAtRegisterIndex > circuit.bandwidth - ){ - returnToOrigin() - return - } - - - // Finally! Work is about to be done! - // All we need to do is tell the circuit itself - // where we need to place these dragged items. - // It will do all the validation for us - // and then fire events that will place new elements - // where they need to go! - - const - draggedOperations = Array.from( Q.Circuit.Editor.dragEl.children ), - draggedMomentDelta = droppedAtMomentIndex - Q.Circuit.Editor.dragEl.momentIndex, - draggedRegisterDelta = droppedAtRegisterIndex - Q.Circuit.Editor.dragEl.registerIndex, - setCommands = [] - - - // Whatever the next action is that we perform on the circuit, - // this was user-initiated via the graphic user interface (GUI). - - circuit.history.createEntry$() - - - // Now let’s work our way through each of the dragged operations. - // If some of these are components of a multi-register operation - // the sibling components will get spliced out of the array - // to avoid processing any specific operation more than once. - - draggedOperations.forEach( function( childEl, i ){ - - let - momentIndexTarget = droppedAtMomentIndex, - registerIndexTarget = droppedAtRegisterIndex - - if( Q.Circuit.Editor.dragEl.circuitEl ){ - - momentIndexTarget += childEl.origin.momentIndex - Q.Circuit.Editor.dragEl.momentIndex - registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex - } - - - // Is this a multi-register operation? - // If so, this is also a from-circuit drop - // rather than a from-palette drop. - - const registerIndicesString = childEl.getAttribute( 'register-indices' ) - if( registerIndicesString ){ - - // What are ALL of the registerIndices - // associated with this multi-register operation? - // (We may use them later as a checklist.) - - const - registerIndices = registerIndicesString - .split( ',' ) - .map( function( str ){ return +str }), - - - // Lets look for ALL of the sibling components of this operation. - // Later we’ll check and see if the length of this array - // is equal to the total number of components for this operation. - // If they’re equal then we know we’re dragging the WHOLE thing. - // Otherwise we need to determine if it needs to break apart - // and if so, what that nature of that break might be. - - foundComponents = Array.from( - - Q.Circuit.Editor.dragEl.querySelectorAll( - - `[moment-index="${ childEl.origin.momentIndex }"]`+ - `[register-indices="${ registerIndicesString }"]` - ) - ) - .sort( function( a, b ){ - - const - aRegisterIndicesIndex = +a.getAttribute( 'register-indices-index' ), - bRegisterIndicesIndex = +b.getAttribute( 'register-indices-index' ) - - return aRegisterIndicesIndex - bRegisterIndicesIndex - }), - allComponents = Array.from( Q.Circuit.Editor.dragEl.circuitEl.querySelectorAll( - - `[moment-index="${ childEl.origin.momentIndex }"]`+ - `[register-indices="${ registerIndicesString }"]` - )), - remainingComponents = allComponents.filter( function( componentEl, i ){ - - return !foundComponents.includes( componentEl ) - }), - - - // We can’t pick the gate symbol - // off the 0th gate in the register indices array - // because that will be an identity / control / null gate. - // We need to look at slot 1. - - component1 = Q.Circuit.Editor.dragEl.querySelector( - - `[moment-index="${ childEl.origin.momentIndex }"]`+ - `[register-index="${ registerIndices[ 1 ] }"]` - ), - gatesymbol = component1 ? - component1.getAttribute( 'gate-symbol' ) : - childEl.getAttribute( 'gate-symbol' ) - - - // We needed to grab the above gatesymbol information - // before we sent any clear$ commands - // which would in turn delete those componentEls. - // We’ve just completed that, - // so now’s the time to send a clear$ command - // before we do any set$ commands. - - draggedOperations.forEach( function( childEl ){ - - Q.Circuit.Editor.dragEl.circuitEl.circuit.clear$( - - childEl.origin.momentIndex, - childEl.origin.registerIndex - ) - }) - - - // FULL MULTI-REGISTER DRAG (TO ANY POSITION ON ANY CIRCUIT). - // If we are dragging all of the components - // of a multi-register operation - // then we are good to go. - - if( registerIndices.length === foundComponents.length ){ - - const operationSkeleton = Q.Gate.findBySymbol( gatesymbol ) - parameters = {} - if( operationSkeleton.has_parameters ) { - Object.keys( operationSkeleton.parameters ).forEach( key => { - parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] - }) - } - //circuit.set$( - setCommands.push([ - - gatesymbol, - momentIndexTarget, - - - // We need to remap EACH register index here - // according to the drop position. - // Let’s let set$ do all the validation on this. - - registerIndices.map( function( registerIndex ){ - - const siblingDelta = registerIndex - childEl.origin.registerIndex - registerIndexTarget = droppedAtRegisterIndex - if( Q.Circuit.Editor.dragEl.circuitEl ){ - - registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex + siblingDelta - } - return registerIndexTarget - }), - parameters - // ) - ]) - } - - - // IN-MOMENT (IN-CIRCUIT) PARTIAL MULTI-REGISTER DRAG. - // It appears we are NOT dragging all components - // of a multi-register operation. - // But if we’re dragging within the same circuit - // and we’re staying within the same moment index - // that might be ok! - - else if( Q.Circuit.Editor.dragEl.circuitEl === circuitEl && - momentIndexTarget === childEl.origin.momentIndex ){ - - - // We must ensure that only one component - // can sit at each register index. - // This copies registerIndices, - // but inverts the key : property relationship. - const registerMap = registerIndices - .reduce( function( registerMap, registerIndex, r ){ - - registerMap[ registerIndex ] = r - return registerMap - - }, {} ) - - - // First, we must remove each dragged component - // from the register it was sitting at. - - foundComponents.forEach( function( component ){ - - const componentRegisterIndex = +component.getAttribute( 'register-index' ) - - - // Remove this component from - // where this component used to be. - - delete registerMap[ componentRegisterIndex ] - }) - - - // Now we can seat it at its new position. - // Note: This may OVERWRITE one of its siblings! - // And that’s ok. - foundComponents.forEach( function( component ){ - - const - componentRegisterIndex = +component.getAttribute( 'register-index' ), - registerGrabDelta = componentRegisterIndex - Q.Circuit.Editor.dragEl.registerIndex - - - // Now put it where it wants to go, - // possibly overwriting a sibling component! - //ltnln: if a multiqubit operation component that requires a sibling, overwrites its sibling, both/all components should be destroyed - registerMap[ - - componentRegisterIndex + draggedRegisterDelta - - ] = +component.getAttribute( 'register-indices-index' ) - }) - - - // Now let’s flip that registerMap - // back into an array of register indices. - - const fixedRegistersIndices = Object.entries( registerMap ) - .reduce( function( registers, entry, i ){ - - registers[ +entry[ 1 ]] = +entry[ 0 ] - return registers - - }, [] ) - - - // This will remove any blank entries in the array - // ie. if a dragged sibling overwrote a seated one. - - .filter( function( entry ){ - return Q.isUsefulInteger( entry ) - }) - - const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ) - parameters = {} - if( operationSkeleton.has_parameters ) { - Object.keys( operationSkeleton.parameters ).forEach( key => { - parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] - }) - } - // Finally, we’re ready to set. - // circuit.set$( - setCommands.push([ - //ltnln: if a component of an operation that requires a sibling pair overwrites its sibling, we want it removed entirely. - fixedRegistersIndices.length < 2 && Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? - Q.Gate.NOOP : - childEl.getAttribute( 'gate-symbol' ), - momentIndexTarget, - fixedRegistersIndices, - parameters - // ) - ]) - } - else { - remainingComponents.forEach( function( componentEl, i ){ - //circuit.set$( - const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ) - parameters = {} - if( operationSkeleton.has_parameters ) { - Object.keys( operationSkeleton.parameters ).forEach( key => { - parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ] - }) - } - setCommands.push([ - - +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? - gatesymbol : - Q.Gate.NOOP, - +componentEl.getAttribute( 'moment-index' ), - +componentEl.getAttribute( 'register-index' ), - parameters - // ) - ]) - }) - - - // Finally, let’s separate and update - // all the components that were part of the drag. - - foundComponents.forEach( function( componentEl ){ - const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ) - parameters = {} - if( operationSkeleton.has_parameters ) { - Object.keys( operationSkeleton.parameters ).forEach( key => { - parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ] - }) - } - setCommands.push([ - //ltnln: temporary fix: certain multiqubit operations should only be represented in pairs of registers. If one is removed (i.e. a single component of the pair) - //then the entire operation should be removed. - +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? - componentEl.getAttribute( 'gate-symbol' ) : - Q.Gate.NOOP, - +componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta, - +componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta, - parameters - // ) - ]) - }) - } - - - // We’ve just completed the movement - // of a multi-register operation. - // But all of the sibling components - // will also trigger this process - // unless we remove them - // from the draggd operations array. - - let j = i + 1 - while( j < draggedOperations.length ){ - - const possibleSibling = draggedOperations[ j ] - if( possibleSibling.getAttribute( 'gate-symbol' ) === gatesymbol && - possibleSibling.getAttribute( 'register-indices' ) === registerIndicesString ){ - - draggedOperations.splice( j, 1 ) - } - else j ++ - } - } - - - // This is just a single-register operation. - // How simple this looks - // compared to all the gibberish above. - - else { - - - // First, if this operation comes from a circuit - // (and not a circuit palette) - // make sure the old positions are cleared away. - - if( Q.Circuit.Editor.dragEl.circuitEl ){ - - draggedOperations.forEach( function( childEl ){ - - Q.Circuit.Editor.dragEl.circuitEl.circuit.clear$( - - childEl.origin.momentIndex, - childEl.origin.registerIndex - ) - }) - } - - - // And now set$ the operation - // in its new home. - - // circuit.set$( - let registerIndices = [ registerIndexTarget ] - //ltnln: By default, multiqubit gates appear in pairs on the circuit rather than - // requiring the user to have to pair them like with Swap/CNot. - const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' )) - if(operationSkeleton.is_multi_qubit ) { - registerIndices.push( registerIndexTarget == circuit.bandwidth ? registerIndexTarget - 1 : registerIndexTarget + 1) - } - let parameters = {} - if( operationSkeleton.has_parameters ) { - Object.keys( operationSkeleton.parameters ).forEach( key => { - parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] - }) - } - setCommands.push([ - childEl.getAttribute( 'gate-symbol' ), - momentIndexTarget, - registerIndices, - parameters - // ) - ]) - } - }) - - - // DO IT DO IT DO IT - - setCommands.forEach( function( setCommand ){ - - circuit.set$.apply( circuit, setCommand ) - }) - - - // Are we capable of making controls? Swaps? - - Q.Circuit.Editor.onSelectionChanged( circuitEl ) - Q.Circuit.Editor.onCircuitChanged( circuitEl ) - - - // If the original circuit and destination circuit - // are not the same thing - // then we need to also eval the original circuit. - - if( Q.Circuit.Editor.dragEl.circuitEl && - Q.Circuit.Editor.dragEl.circuitEl !== circuitEl ){ - - const originCircuitEl = Q.Circuit.Editor.dragEl.circuitEl - Q.Circuit.Editor.onSelectionChanged( originCircuitEl ) - Q.Circuit.Editor.onCircuitChanged( originCircuitEl ) - } - - - // We’re finally done here. - // Clean up and go home. - // It’s been a long journey. - // I love you all. - - document.body.removeChild( Q.Circuit.Editor.dragEl ) - Q.Circuit.Editor.dragEl = null -} - - - ///////////////////////// - // // - // Pointer DOUBLECLICK // - // // -///////////////////////// -//ltnln: my trying out an idea for parameterized gates... -Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) { - // assumption for the following 3 lines is that we've already decided that we are on-top of a valid gate operation in - // the circuit - const operation = Q.Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' )) - const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ) - - const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container") - if( boardContainerAll.length === 0 ) return - let boardContainerEl = Array.from(boardContainerAll).find((element) => { - let rect = element.getBoundingClientRect() - let clientX = rect.left - let clientY = rect.top - let height = element.offsetHeight - let width = element.offsetWidth - return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height ) - }) - if( !boardContainerEl ) return; - const parameterEl = boardContainerEl.querySelector('.Q-parameters-box') - const exit = document.createElement( 'button' ) - parameterEl.appendChild( exit ) - exit.classList.add( 'Q-parameter-box-exit' ) - exit.appendChild(document.createTextNode( '⬅' )) - parameterEl.setAttribute( "operation-moment-index", operationEl.getAttribute( 'moment-index' )) - parameterEl.setAttribute( "operation-register-index", operationEl.getAttribute( 'register-index' )) - - - //here we generate queries for each parameter that the gate operation takes! - const parameters = Object.keys(operation.parameters) - parameters.forEach( element => { - if( operation.parameters && operation.parameters[element] !== null ) { - const input_fields = document.createElement( 'div' ) - parameterEl.appendChild( input_fields ) - input_fields.classList.add( 'Q-parameter-box-input-container' ) - const label = document.createElement( "span" ) - input_fields.appendChild( label ) - label.classList.add( 'Q-parameter-input-label' ) - label.appendChild(document.createTextNode( element )) - - const textbox = document.createElement( "input" ) - input_fields.appendChild( textbox ) - textbox.classList.add( 'Q-parameter-box-input' ) - textbox.setAttribute( 'type', 'text' ) - textbox.setAttribute( 'placeholder', element ) - textbox.setAttribute( 'value', operationEl.getAttribute(element) ? operationEl.getAttribute(element) : operation.parameters[element] ) - //set textbox to update the operation instance (cellEl)'s parameters on value change - textbox.addEventListener( "change", () => { - let parameterValue - let oldValue = operationEl.getAttribute( element ) - if( !oldValue ) oldValue = operation.parameters[ element ] - try { - //TODO: figure out how to properly import the mathjs library... - parameterValue = +(textbox.value.toLowerCase()); - } - catch( err ) { - parameterValue = oldValue - } - - if( !parameterValue || parameterValue === Infinity ) textbox.value = oldValue.toString() - else { - operationEl.setAttribute( element, parameterValue ) - textbox.value = parameterValue.toString() - } - }) - - - } - }) - - parameterEl.classList.toggle('overlay') - parameterEl.style.display = 'block' -} - - - /////////////////// - // // - // Listeners // - // // -/////////////////// - - -// These listeners must be applied -// to the entire WINDOW (and not just document.body!) - -window.addEventListener( 'mousemove', Q.Circuit.Editor.onPointerMove ) -window.addEventListener( 'touchmove', Q.Circuit.Editor.onPointerMove ) -window.addEventListener( 'mouseup', Q.Circuit.Editor.onPointerRelease ) -window.addEventListener( 'touchend', Q.Circuit.Editor.onPointerRelease ) - - - - - - - -/* - - -%%HTML - - - - - -%%javascript -Q.braket( element ) - - - - -*/ - - - -//%%javascript - - - -Q.braket = function(){ - - - // Create the HTML bits we need, - // contain them all together, - // and output them to Jupyter. - if( arguments.length === 0 || arguments.length > 3 ) return; - const element = arguments[0]; - const args = (Array.from(arguments)).slice(1); - let circuit - if(args.length === 0) { - circuit = new Q( 4, 8 ) - } - else if(args.length === 1) { - circuit = new Q( args[0] ) - } - else { - circuit = new Q( args[0], args[1] ) - } - container = document.createElement( 'div' ) - container.appendChild( Q.Circuit.Editor.createPalette() ) - container.appendChild( circuit.toDom() ) - element.html( container ) - - - // We’re going to take this SLOOOOOOOOWLY - // because there are many potential things to debug. - - const thisCell = Jupyter.notebook.get_selected_cell() - // console.log( 'thisCell', thisCell ) - - const thisCellIndex = Jupyter.notebook.get_cells().indexOf( thisCell ) - // console.log( 'thisCellIndex', thisCellIndex ) - - const nextCell = Jupyter.notebook.insert_cell_below( 'code', thisCellIndex - 1 ) - const nextNextCell = Jupyter.notebook.insert_cell_below( 'markdown', Jupyter.notebook.get_cells().indexOf( thisCell ) - 1 ) - // console.log( 'nextCell', nextCell ) - - nextCell.set_text( circuit.toAmazonBraket() ) - nextNextCell.set_text( circuit.report$() ) - - - - - - - window.addEventListener( 'Q gui altered circuit', function( event ){ - - // updatePlaygroundFromDom( event.detail.circuit ) - if( event.detail.circuit === circuit ){ - - console.log( 'Updating circuit from GUI', circuit ) - circuit.evaluate$() - nextCell.set_text( circuit.toAmazonBraket() ) - - } - }) - - window.addEventListener( 'Q.Circuit.evaluate completed', function( event ) { - if( event.detail.circuit === circuit ) { - nextNextCell.set_text( circuit.report$() ) - } - }) - - - // nextCell.render() - - // console.log( 'thisCell', thisCell ) - // console.log( 'nextCell', nextCell ) - // console.log( 'thisCellIndex', thisCellIndex ) - - // code = Jupyter.notebook.insert_cell_{0}('code'); - // code.set_text(atob("{1}")) - - // var t_cell = Jupyter.notebook.get_selected_cell() - // t_cell.set_text(' \\n{}') - // var t_index = Jupyter.notebook.get_cells().indexOf(t_cell) - // Jupyter.notebook.to_markdown(t_index) - // Jupyter.notebook.get_cell(t_index).render() -} - -module.exports = Q \ No newline at end of file diff --git a/build/q.css b/build/q.css index de56074..e4a6620 100644 --- a/build/q.css +++ b/build/q.css @@ -1,6 +1,6 @@ /* - Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. */ @charset "utf-8"; @@ -11,8 +11,8 @@ /* This file is in the process of being separated - in to “essential global Q.js styles” which will - remain here in Q.css, and “documentation-specific” + in to “essential global Q.js styles” which will + remain here in Q.css, and “documentation-specific” styles which will be removed from here and placed within the /other/documentation.css file instead. @@ -319,7 +319,7 @@ svg, :root { /* - The below still need to be prefaced with “Q-” + The below still need to be prefaced with “Q-” and for the HTML pages to be updated accordingly. */ @@ -341,6 +341,8 @@ svg, :root { max-width: 100%; overflow-x: auto; font-family: var( --Q-font-family-sans ); + /*letter-spacing: 0.03em;*/ + word-spacing: 0.2em; } dd .maths { @@ -393,22 +395,23 @@ dd .maths { vertical-align: middle; position: relative; align: middle; - margin: 1em; + margin: 1em 0.5em; padding: 1em; - font-family: var( --Q-font-family-mono ); + /*font-family: var( --Q-font-family-mono );*/ font-weight: 300; line-height: 1em; - text-align: right; + /*text-align: right;*/ + text-align: center; } .matrix td { - padding: 5px 10px; + padding: 0.25em 0.5em; } .matrix-bracket-left, .matrix-bracket-right { position: absolute; top: 0; - width: 5px; + width: 0.5em; height: 100%; border: 1px solid #CCC; } @@ -437,7 +440,7 @@ dd .maths { .Q-state-vector.bra::before, .complex-vector.bra::before { - content: '⟨'; + content: '⟨'; color: #BBB; } .Q-state-vector.bra::after, @@ -455,7 +458,7 @@ dd .maths { .Q-state-vector.ket::after, .complex-vector.ket::after { - content: '⟩'; + content: '⟩'; color: #BBB; } .Q-state-vector.bra + .Q-state-vector.ket::before, @@ -472,7 +475,7 @@ dd .maths { /* - Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. */ @charset "utf-8"; @@ -484,7 +487,6 @@ dd .maths { - /* Z indices: @@ -567,11 +569,12 @@ dd .maths { + .Q-circuit, .Q-circuit-palette { position: relative; - width: 100%; + width: 50%; } .Q-circuit-palette { @@ -595,6 +598,7 @@ dd .maths { margin: 1rem 0 2rem 0; /*border-top: 2px solid hsl( 0, 0%, 50% );*/ } +.Q-parameters-box, .Q-circuit-board-foreground { line-height: 3.85rem; @@ -747,6 +751,19 @@ dd .maths { grid-auto-columns: 4rem; grid-auto-flow: column; } + +.Q-parameters-box { + + position: absolute; + display: none; + z-index: 100; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: whitesmoke; +} + /*.Q-circuit-palette,*/ .Q-circuit-board-foreground, .Q-circuit-board-background { @@ -836,8 +853,17 @@ dd .maths { ); } +.Q-parameter-box-exit { + position: relative; + right: 0; + left: 0; + width: 5rem; + height: 2.5rem; + background-color: whitesmoke; + border-radius: 0; +} - +.Q-parameters-box > div, .Q-circuit-palette > div, .Q-circuit-clipboard > div, .Q-circuit-board-foreground > div { @@ -1079,7 +1105,7 @@ dd .maths { rgba( 0, 0, 0, 0.05 ) )*/; } -.Q-circuit-palette .Q-circuit-operation:hover { +.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover { /*background-color: rgba( 255, 255, 255, 0.6 );*/ background-color: white; @@ -1192,6 +1218,25 @@ dd .maths { } +.Q-parameter-box-input-container { + position: relative; + text-align: center; + grid-auto-columns: 4rem; + grid-auto-flow: column; +} + +.Q-parameter-box-input { + position: relative; + border-radius: .2rem; + margin-left: 10px; + font-family: var( --Q-font-family-mono ); +} + +.Q-parameter-input-label { + position: relative; + color: var( --Q-color-blue ); + font-family: var( --Q-font-family-mono ); +} @@ -1329,4 +1374,3 @@ dd .maths { } - diff --git a/package.json b/package.json index 0573d1b..b89ee85 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,11 @@ "description": "![Quantum JavaScript (Q.js)](./assets/Q-mark.svg \"Quantum JavaScript (Q.js)\")", "main": "Q/Q.js", "scripts": { - "test": "npm run test -ws && exit 0", + "test": "npm run test -ws", "lint": "eslint", - "prettier": "prettier --write" + "prettier": "prettier --write", + "dev": "vite", + "build": "npx browserify index.js > build/bundle.js && npx concat -o build/bundle.css packages/quantum-js-vis/Q.css packages/quantum-js-vis/Q-Circuit-Editor.css" }, "repository": { "type": "git", @@ -16,6 +18,8 @@ "author": "", "license": "ISC", "devDependencies": { + "browserify": "^17.0.0", + "concat": "^1.0.3", "eslint": "^7.31.0", "jest": "^27.0.6", "jsdom": "^16.6.0", @@ -23,7 +27,9 @@ "n": "^7.3.1", "prettier": "2.3.2" }, - "dependencies": {}, + "dependencies": { + "requirejs": "^2.3.6" + }, "workspaces": [ "./packages/*" ] From e7de83d0074f26039f856489ed12aa597dca87ca Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 19:13:18 +0000 Subject: [PATCH 05/22] Update dispatchCustomEventToGlobal method and references in other files --- packages/quantum-js-util/Misc.js | 30 +++++++++++++++------------ packages/quantum-js-util/Q-Circuit.js | 20 +++++++++--------- packages/quantum-js-util/Q-History.js | 22 ++++++++------------ 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/quantum-js-util/Misc.js b/packages/quantum-js-util/Misc.js index 4eb824d..6c7974b 100644 --- a/packages/quantum-js-util/Misc.js +++ b/packages/quantum-js-util/Misc.js @@ -1,18 +1,23 @@ const logger = require('./Logging'); -const COLORS = []; -const ANIMALS = []; const constants = {}; - -function dispatchEventToGlobal(event) { - if(typeof window != undefined) { - window.dispatchEvent(event); - } - else { - //if window does exist, global == window is true. So maybe we can just do global.dispatchEvent instead of this wrapper? - global.dispatchEvent(event); - console.log(event); +function dispatchCustomEventToGlobal(event_name, detail, terminate_on_error=false, silent=true) { + try { + const event = new CustomEvent(event_name, detail); + if(typeof window != undefined) { + window.dispatchEvent(event); + } + else { + //if window does exist, global == window is true. So maybe we can just do global.dispatchEvent instead of this wrapper? + global.dispatchEvent(event); + if(!silent) console.log(event); + } + } catch(e) { + //When running in node, CustomEvent and documents don't exist. We can emulate using a JSDOM package + if(!silent) logger.error("Could not dispatch custom event."); + if(terminate_on_error) process.exit(); } + } function createConstant(key, value) { @@ -60,7 +65,6 @@ function shuffleNames$() { function getRandomName$() { if (shuffledNames.length === 0) shuffleNames$(); - const pair = shuffledNames[namesIndex], name = COLORS[pair[0]] + " " + ANIMALS[pair[1]]; @@ -397,4 +401,4 @@ createConstants( ] ); -module.exports = { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchEventToGlobal, constants }; +module.exports = { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchCustomEventToGlobal, constants }; diff --git a/packages/quantum-js-util/Q-Circuit.js b/packages/quantum-js-util/Q-Circuit.js index 9cba24c..8d1c4b6 100644 --- a/packages/quantum-js-util/Q-Circuit.js +++ b/packages/quantum-js-util/Q-Circuit.js @@ -425,13 +425,13 @@ Object.assign( Circuit, { // console.log( circuit.toDiagram() ) - misc.dispatchEventToGlobal(new CustomEvent( + misc.dispatchCustomEventToGlobal( 'Circuit.evaluate began', { detail: { circuit } } - )) + ); // Our circuit’s operations must be in the correct order @@ -541,7 +541,7 @@ Object.assign( Circuit, { const progress = operationsCompleted / operationsTotal - misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate progressed', { detail: { + misc.dispatchCustomEventToGlobal('Circuit.evaluate progressed', { detail: { circuit, progress, @@ -552,7 +552,7 @@ Object.assign( Circuit, { gate: operation.gate.name, state - }})) + }}) // console.log( `\n\nProgress ... ${ Math.round( operationsCompleted / operationsTotal * 100 )}%`) @@ -591,13 +591,13 @@ Object.assign( Circuit, { - misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate completed', { detail: { + misc.dispatchCustomEventToGlobal('Circuit.evaluate completed', { detail: { // circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: { circuit, results: outcomes - }})) + }}) @@ -1388,7 +1388,7 @@ print(task.result().measurement_counts)` foundOperations.forEach( function( operation ){ - misc.dispatchEventToGlobal(new CustomEvent( + misc.dispatchCustomEventToGlobal( 'Circuit.clear$', { detail: { @@ -1396,7 +1396,7 @@ print(task.result().measurement_counts)` momentIndex, registerIndices: operation.registerIndices }} - )) + ) }) } @@ -1545,14 +1545,14 @@ print(task.result().measurement_counts)` // Emit an event that we have set an operation // on this circuit. - misc.dispatchEventToGlobal(new CustomEvent( + misc.dispatchCustomEventToGlobal( 'Circuit.set$', { detail: { circuit, operation }} - )) + ) } return circuit }, diff --git a/packages/quantum-js-util/Q-History.js b/packages/quantum-js-util/Q-History.js index 1d38945..a41cfaf 100644 --- a/packages/quantum-js-util/Q-History.js +++ b/packages/quantum-js-util/Q-History.js @@ -1,7 +1,7 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const {dispatchEventToGlobal} = require('./Misc'); +const {dispatchCustomEventToGlobal} = require('./Misc'); History = function( instance ){ @@ -26,31 +26,27 @@ Object.assign( History.prototype, { const instance = this.instance if( this.index > 0 ){ - dispatchEventToGlobal(new CustomEvent( - + dispatchCustomEventToGlobal( 'History undo is capable', { detail: { instance }} - )); + ); } else { - dispatchEventToGlobal(new CustomEvent( - + dispatchCustomEventToGlobal( 'History undo is depleted', { detail: { instance }} - )) + ) } if( this.index + 1 < this.entries.length ){ - dispatchEventToGlobal(new CustomEvent( - + dispatchCustomEventToGlobal( 'History redo is capable', { detail: { instance }} - )) + ) } else { - dispatchEventToGlobal(new CustomEvent( - + dispatchCustomEventToGlobal( 'History redo is depleted', { detail: { instance }} - )) + ) } return this }, From 105614d2a001270faab1ad8f720fa0d7389120b9 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 19:16:13 +0000 Subject: [PATCH 06/22] Change entry point for quantum-js-util package --- index.js | 17 +++++------------ packages/quantum-js-util/Q.js | 12 +++--------- packages/quantum-js-util/index.js | 13 ++++++++++++- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/index.js b/index.js index fc40a83..840985e 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,6 @@ -const {Q} = require('./Q/Q'); -const {Circuit} = require('./Q/Q-Circuit'); -const {Qubit} = require('./Q/Q-Qubit'); -const {Gate} = require('./Q/Q-Gate'); -const {Matrix} = require('./Q/Q-Matrix'); -const {ComplexNumber} = require('./Q/Q-ComplexNumber'); -const mathf = require('./Q/Math-Functions'); -const misc = require('./Q/Misc'); -const logger = require('./Q/Logging'); +let {logger, mathf, misc, ComplexNumber, Matrix, Gate, Qubit, Circuit, History, Q} = require('quantum-js-util'); +let {Editor, BlochSphere, braket} = require('quantum-js-vis'); +global.misc = misc; +global.logger = logger; +global.mathf = mathf; -console.log("Howdy! Welcome to Q.js!"); - -module.exports = {Q, Circuit, Qubit, Gate, Matrix, ComplexNumber, mathf, misc, logger}; \ No newline at end of file diff --git a/packages/quantum-js-util/Q.js b/packages/quantum-js-util/Q.js index a4ca1d5..4749fb2 100644 --- a/packages/quantum-js-util/Q.js +++ b/packages/quantum-js-util/Q.js @@ -1,25 +1,19 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const logger = require('./Logging'); const misc = require('./Misc'); const mathf = require('./Math-Functions'); -const {ComplexNumber} = require('./Q-ComplexNumber'); -const {Gate} = require('./Q-Gate'); -const {Qubit} = require('./Q-Qubit'); -const {Matrix} = require('./Q-Matrix'); -const {History} = require('./Q-History'); const {Circuit} = require('./Q-Circuit'); -const Q = function () { +Q = function () { // Did we send arguments of the form // ( bandwidth, timewidth )? if ( arguments.length === 2 && Array.from(arguments).every(function (argument) { - return isUsefulInteger(argument); + return mathf.isUsefulInteger(argument); }) ) { return new Circuit(arguments[0], arguments[1]); @@ -51,5 +45,5 @@ https://quantumjavascript.app `); -module.exports = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q}; +module.exports = {Q}; diff --git a/packages/quantum-js-util/index.js b/packages/quantum-js-util/index.js index c1cc69f..997c0c9 100644 --- a/packages/quantum-js-util/index.js +++ b/packages/quantum-js-util/index.js @@ -1 +1,12 @@ -const {Q} = require('./Q'); +const logger = require('./Logging'); +const misc = require('./Misc'); +const mathf = require('./Math-Functions'); +const {ComplexNumber} = require('./Q-ComplexNumber'); +const {Gate} = require('./Q-Gate'); +const {Qubit} = require('./Q-Qubit'); +const {Matrix} = require('./Q-Matrix'); +const {History} = require('./Q-History'); +const {Circuit} = require('./Q-Circuit'); +const {Q} = require('./Q.js'); + +module.exports = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q}; From 9692c33d2bb2650c35c9fe3f1d440c49a36dad56 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 19:25:54 +0000 Subject: [PATCH 07/22] Create quantum-js-vis visualization package for the GUI --- packages/quantum-js-vis/Q-BlochSphere.js | 582 +++++ packages/quantum-js-vis/Q-Circuit-Editor.css | 900 +++++++ packages/quantum-js-vis/Q-Circuit-Editor.js | 2281 ++++++++++++++++++ packages/quantum-js-vis/Q.css | 474 ++++ packages/quantum-js-vis/index.js | 89 + packages/quantum-js-vis/package-lock.json | 35 + packages/quantum-js-vis/package.json | 21 + 7 files changed, 4382 insertions(+) create mode 100644 packages/quantum-js-vis/Q-BlochSphere.js create mode 100644 packages/quantum-js-vis/Q-Circuit-Editor.css create mode 100644 packages/quantum-js-vis/Q-Circuit-Editor.js create mode 100644 packages/quantum-js-vis/Q.css create mode 100644 packages/quantum-js-vis/index.js create mode 100644 packages/quantum-js-vis/package-lock.json create mode 100644 packages/quantum-js-vis/package.json diff --git a/packages/quantum-js-vis/Q-BlochSphere.js b/packages/quantum-js-vis/Q-BlochSphere.js new file mode 100644 index 0000000..a1ff2e5 --- /dev/null +++ b/packages/quantum-js-vis/Q-BlochSphere.js @@ -0,0 +1,582 @@ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + + + +const {Qubit} = require('quantum-js-util'); +BlochSphere = function( onValueChange ){ + + Object.assign( this, { + + isRotating: false, + radius: 1, + radiusSafe: 1.01, + axesLineWidth: 0.01, + arcLineWidth: 0.015, + state: Qubit.LEFT_HAND_CIRCULAR_POLARIZED.toBlochSphere(), + target: Qubit.HORIZONTAL.toBlochSphere(), + group: new THREE.Group(), + onValueChange + }) + + + // Create the surface of the Bloch sphere. + + const surface = new THREE.Mesh( + + new THREE.SphereGeometry( this.radius, 64, 64 ), + new THREE.MeshPhongMaterial({ + + side: THREE.FrontSide, + map: BlochSphere.makeSurface(), + transparent: true, + opacity: 0.97 + }) + ) + surface.receiveShadow = true + this.group.add( surface ) + + + + + // Create the X, Y, and Z axis lines. + + const + xAxis = new THREE.Mesh( + + new THREE.BoxGeometry( this.axesLineWidth, this.axesLineWidth, this.radius * 2.5 ), + new THREE.MeshBasicMaterial({ color: BlochSphere.xAxisColor }) + ), + yAxis = new THREE.Mesh( + + new THREE.BoxGeometry( this.radius * 2.5, this.axesLineWidth, this.axesLineWidth ), + new THREE.MeshBasicMaterial({ color: BlochSphere.yAxisColor }) + ), + zAxis = new THREE.Mesh( + + new THREE.BoxGeometry( this.axesLineWidth, this.radius * 2.5, this.axesLineWidth ), + new THREE.MeshBasicMaterial({ color: BlochSphere.zAxisColor }) + ) + + this.group.add( xAxis, yAxis, zAxis ) + + + // Create X, Y, and Z arrow heads, + // indicating positive directions for all three. + + const + arrowLength = 0.101,// I know, weird, right? + arrowHeadLength = 0.1, + arrowHeadWidth = 0.1 + + this.group.add( new THREE.ArrowHelper( + + new THREE.Vector3( 0, 0, 1.00 ), + new THREE.Vector3( 0, 0, 1.25 ), + arrowLength, + BlochSphere.xAxisColor,// Red + arrowHeadLength, + arrowHeadWidth + )) + this.group.add( new THREE.ArrowHelper( + + new THREE.Vector3( 1.00, 0, 0 ), + new THREE.Vector3( 1.25, 0, 0 ), + arrowLength, + BlochSphere.yAxisColor,// Green + arrowHeadLength, + arrowHeadWidth + )) + this.group.add( new THREE.ArrowHelper( + + new THREE.Vector3( 0, 1.00, 0 ), + new THREE.Vector3( 0, 1.25, 0 ), + arrowLength, + BlochSphere.zAxisColor,// Blue + arrowHeadLength, + arrowHeadWidth + )) + + + // Create the X, Y, and Z axis labels. + + const + axesLabelStyle = { + + width: 128, + height: 128, + fillStyle: BlochSphere.vectorColor,//'#505962', + font: 'bold italic 64px Georgia, "Times New Roman", serif' + }, + xAxisLabel = new THREE.Sprite( + + new THREE.SpriteMaterial({ + + map: Object.assign( SurfaceText( axesLabelStyle )) + }) + ), + yAxisLabel = new THREE.Sprite( + + new THREE.SpriteMaterial({ + + map: Object.assign( SurfaceText( axesLabelStyle )) + }) + ), + zAxisLabel = new THREE.Sprite( + + new THREE.SpriteMaterial({ + + map: Object.assign( SurfaceText( axesLabelStyle )) + }) + ) + + xAxisLabel.material.map.print( 'x' ) + xAxisLabel.position.set( 0, 0, 1.45 ) + xAxisLabel.scale.set( 0.25, 0.25, 0.25 ) + xAxis.add( xAxisLabel ) + + yAxisLabel.material.map.print( 'y' ) + yAxisLabel.position.set( 1.45, 0, 0 ) + yAxisLabel.scale.set( 0.25, 0.25, 0.25 ) + yAxis.add( yAxisLabel ) + + zAxisLabel.material.map.print( 'z' ) + zAxisLabel.position.set( 0, 1.45, 0 ) + zAxisLabel.scale.set( 0.25, 0.25, 0.25 ) + zAxis.add( zAxisLabel ) + + + this.blochColor = new THREE.Color() + + + // Create the line from the sphere’s origin + // out to where the Bloch vector intersects + // with the sphere’s surface. + + this.blochVector = new THREE.Mesh( + + new THREE.BoxGeometry( 0.04, 0.04, this.radius ), + new THREE.MeshBasicMaterial({ color: BlochSphere.vectorColor }) + ) + this.blochVector.geometry.translate( 0, 0, 0.5 ) + this.group.add( this.blochVector ) + + + // Create the cone that indicates the Bloch vector + // and points to where that vectors + // intersects with the surface of the sphere. + + this.blochPointer = new THREE.Mesh( + + new THREE.CylinderBufferGeometry( 0, 0.5, 1, 32, 1 ), + new THREE.MeshPhongMaterial({ color: BlochSphere.vectorColor }) + ) + this.blochPointer.geometry.translate( 0, -0.5, 0 ) + this.blochPointer.geometry.rotateX( Math.PI / 2 ) + this.blochPointer.geometry.scale( 0.2, 0.2, 0.2 ) + this.blochPointer.lookAt( new THREE.Vector3() ) + this.blochPointer.receiveShadow = true + this.blochPointer.castShadow = true + this.group.add( this.blochPointer ) + + + // Create the Theta ring that will belt the sphere. + + const + arcR = this.radiusSafe * Math.sin( Math.PI / 2 ), + arcH = this.radiusSafe * Math.cos( Math.PI / 2 ), + thetaGeometry = BlochSphere.createLatitudeArc( arcR, 128, Math.PI / 2, Math.PI * 2 ), + thetaLine = new MeshLine(), + thetaPhiMaterial = new MeshLineMaterial({ + + color: BlochSphere.thetaPhiColor,//0x505962, + lineWidth: this.arcLineWidth * 3, + sizeAttenuation: true + }) + + thetaGeometry.rotateX( Math.PI / 2 ) + thetaGeometry.rotateY( Math.PI / 2 ) + thetaGeometry.translate( 0, arcH, 0 ) + thetaLine.setGeometry( thetaGeometry ) + + this.thetaMesh = new THREE.Mesh( + + thetaLine.geometry, + thetaPhiMaterial + ) + this.group.add( this.thetaMesh ) + + + // Create the Phi arc that will draw from the north pole + // down to wherever the Theta arc rests. + + this.phiGeometry = BlochSphere.createLongitudeArc( this.radiusSafe, 64, 0, Math.PI * 2 ), + this.phiLine = new MeshLine() + this.phiLine.setGeometry( this.phiGeometry ) + this.phiMesh = new THREE.Mesh( + + this.phiLine.geometry, + thetaPhiMaterial + ) + this.group.add( this.phiMesh ) + + + + + // Time to put plans to action. + + BlochSphere.prototype.setTargetState.call( this ) +} + + + + + + + //////////////// + // // + // Static // + // // +//////////////// + + +Object.assign( BlochSphere, { + + xAxisColor: 0x333333,// Was 0xCF1717 (red) + yAxisColor: 0x333333,// Was 0x59A112 (green) + zAxisColor: 0x333333,// Was 0x0F66BD (blue) + vectorColor: 0xFFFFFF,// Was 0xF2B90D (yellow) + thetaPhiColor: 0x333333,// Was 0xF2B90D (yellow) + + + // It’s important that we build the texture + // right here and now, rather than load an image. + // Why? Because if we load a pre-existing image + // we run into CORS problems using file:/// ! + + makeSurface: function(){ + + const + width = 2048, + height = width / 2 + + const canvas = document.createElement( 'canvas' ) + canvas.width = width + canvas.height = height + + const context = canvas.getContext( '2d' ) + context.fillStyle = 'hsl( 210, 20%, 100% )' + context.fillRect( 0, 0, width, height ) + + + // Create the base hue gradient for our texture. + + const + hueGradient = context.createLinearGradient( 0, height / 2, width, height / 2 ), + hueSteps = 180, + huesPerStep = 360 / hueSteps + + for( let i = 0; i <= hueSteps; i ++ ){ + + hueGradient.addColorStop( i / hueSteps, 'hsl( '+ ( i * huesPerStep - 90 ) +', 100%, 50% )' ) + } + context.fillStyle = hueGradient + context.fillRect( 0, 0, width, height ) + + + // For both the northern gradient (to white) + // and the southern gradient (to black) + // we’ll leave a thin band of full saturation + // near the equator. + + const whiteGradient = context.createLinearGradient( width / 2, 0, width / 2, height / 2 ) + whiteGradient.addColorStop( 0.000, 'hsla( 0, 0%, 100%, 1 )' ) + whiteGradient.addColorStop( 0.125, 'hsla( 0, 0%, 100%, 1 )' ) + whiteGradient.addColorStop( 0.875, 'hsla( 0, 0%, 100%, 0 )' ) + context.fillStyle = whiteGradient + context.fillRect( 0, 0, width, height / 2 ) + + const blackGradient = context.createLinearGradient( width / 2, height / 2, width / 2, height ) + blackGradient.addColorStop( 0.125, 'hsla( 0, 0%, 0%, 0 )' ) + blackGradient.addColorStop( 0.875, 'hsla( 0, 0%, 0%, 1 )' ) + blackGradient.addColorStop( 1.000, 'hsla( 0, 0%, 0%, 1 )' ) + context.fillStyle = blackGradient + context.fillRect( 0, height / 2, width, height ) + + + // Create lines of latitude and longitude. + // Note this is an inverse Mercatur projection ;) + + context.fillStyle = 'hsla( 0, 0%, 0%, 0.2 )' + const yStep = height / 16 + for( let y = 0; y <= height; y += yStep ){ + + context.fillRect( 0, y, width, 1 ) + } + const xStep = width / 16 + for( let x = 0; x <= width; x += xStep ){ + + context.fillRect( x, 0, 1, height ) + } + + + // Prepare the THREE texture and return it + // so we can use it as a material map. + + const texture = new THREE.CanvasTexture( canvas ) + texture.needsUpdate = true + return texture + }, + + + + + createLongitudeArc: function( radius, segments, thetaStart, thetaLength ){ + + const geometry = new THREE.CircleGeometry( radius, segments, thetaStart, thetaLength ) + geometry.vertices.shift() + + + // This is NOT NORMALLY necessary + // because we expect this to only be + // between PI/2 and PI*2 + // (so the length is only Math.PI instead of PI*2). + + if( thetaLength >= Math.PI * 2 ){ + + geometry.vertices.push( geometry.vertices[ 0 ].clone() ) + } + return geometry + }, + createLatitudeArc: function( radius, segments, phiStart, phiLength ){ + + const geometry = new THREE.CircleGeometry( radius, segments, phiStart, phiLength ) + geometry.vertices.shift() + if( phiLength >= 2 * Math.PI ){ + + geometry.vertices.push( geometry.vertices[ 0 ].clone() ) + } + return geometry + }, + createQuadSphere: function( options ){ + + let { + + radius, + phiStart, + phiLength, + thetaStart, + thetaLength, + latitudeLinesTotal, + longitudeLinesTotal, + latitudeLineSegments, + longitudeLineSegments, + latitudeLinesAttributes, + longitudeLinesAttributes + + } = options + + if( typeof radius !== 'number' ) radius = 1 + if( typeof phiStart !== 'number' ) phiStart = Math.PI / 2 + if( typeof phiLength !== 'number' ) phiLength = Math.PI * 2 + if( typeof thetaStart !== 'number' ) thetaStart = 0 + if( typeof thetaLength !== 'number' ) thetaLength = Math.PI + if( typeof latitudeLinesTotal !== 'number' ) latitudeLinesTotal = 16 + if( typeof longitudeLinesTotal !== 'number' ) longitudeLinesTotal = 16 + if( typeof latitudeLineSegments !== 'number' ) latitudeLineSegments = 64 + if( typeof longitudeLineSegments !== 'number' ) longitudeLineSegments = 64 + if( typeof latitudeLinesAttributes === 'undefined' ) latitudeLinesAttributes = { color: 0xCCCCCC } + if( typeof longitudeLinesAttributes === 'undefined' ) longitudeLinesAttributes = { color: 0xCCCCCC } + + const + sphere = new THREE.Group(), + latitudeLinesMaterial = new THREE.LineBasicMaterial( latitudeLinesAttributes ), + longitudeLinesMaterial = new THREE.LineBasicMaterial( longitudeLinesAttributes ) + + + // Lines of longitude. + // https://en.wikipedia.org/wiki/Longitude + + for( + + let + phiDelta = phiLength / longitudeLinesTotal, + phi = phiStart, + arc = BlochSphere.createLongitudeArc( radius, longitudeLineSegments, thetaStart + Math.PI / 2, thetaLength ); + phi < phiStart + phiLength + phiDelta; + phi += phiDelta ){ + + const geometry = arc.clone() + geometry.rotateY( phi ) + sphere.add( new THREE.Line( geometry, longitudeLinesMaterial )) + } + + + // Lines of latitude. + // https://en.wikipedia.org/wiki/Latitude + + for ( + + let + thetaDelta = thetaLength / latitudeLinesTotal, + theta = thetaStart; + theta < thetaStart + thetaLength; + theta += thetaDelta ){ + + if( theta === 0 ) continue + + const + arcR = radius * Math.sin( theta ), + arcH = radius * Math.cos( theta ), + geometry = BlochSphere.createLatitudeArc( arcR, latitudeLineSegments, phiStart, phiLength ) + + geometry.rotateX( Math.PI / 2 ) + geometry.rotateY( Math.PI / 2 ) + geometry.translate( 0, arcH, 0 ) + sphere.add( new THREE.Line( geometry, latitudeLinesMaterial )) + } + + + return sphere + } +}) + + + + + + + /////////////// + // // + // Proto // + // // +/////////////// + + +Object.assign( BlochSphere.prototype, { + + update: function(){ + + if( this.isRotating ) this.group.rotation.y += Math.PI / 4096 + }, + setTargetState: function( target ){ + + if( target === undefined ) target = Qubit.HORIZONTAL.toBlochSphere() + + + // Always take the shortest path around + // even if it crosses the 0˚ / 360˚ boundary, + // ie. between Anti-Diagonal (-90˚) and + // Right0-and circular polarized (180˚). + + const + rangeHalf = Math.PI, + distance = this.state.phi - target.phi + + if( Math.abs( distance ) > rangeHalf ){ + + this.state.phi += Math.sign( distance ) * rangeHalf * -2 + } + + + // Cheap hack to test if we need to update values + // from within the updateBlochVector method. + + Object.assign( this.target, target ) + + + // Create the tween. + + window.tween = new TWEEN.Tween( this.state ) + .to( target, 1000 ) + .easing( TWEEN.Easing.Quadratic.InOut ) + .onUpdate( this.updateBlochVector.bind( this )) + .start() + }, + updateBlochVector: function( state ){ + + + // Move the big-ass surface pointer. + + if( state.theta !== this.target.theta || + state.phi !== this.target.phi ){ + + this.blochPointer.position.set( + + Math.sin( state.theta ) * Math.sin( state.phi ), + Math.cos( state.theta ), + Math.sin( state.theta ) * Math.cos( state.phi ) + ) + this.blochPointer.lookAt( new THREE.Vector3() ) + this.blochVector.lookAt( this.blochPointer.getWorldPosition( new THREE.Vector3() )) + + + // Determine the correct HSL color + // based on Phi and Theta. + + let hue = state.phi * THREE.Math.RAD2DEG + if( hue < 0 ) hue = 360 + hue + this.blochColor.setHSL( + + hue / 360, + 1, + 1 - ( state.theta / Math.PI ) + ) + this.blochPointer.material.color = this.blochColor + this.blochVector.material.color = this.blochColor + + if( state.theta !== this.target.theta ){ + + + // Slide the Theta ring from the north pole + // down as far south as it needs to go + // and scale its radius so it belts the sphere. + + const thetaScaleSafe = Math.max( state.theta, 0.01 ) + this.thetaMesh.scale.set( + + Math.sin( thetaScaleSafe ), + 1, + Math.sin( thetaScaleSafe ) + ) + this.thetaMesh.position.y = Math.cos( state.theta ) + + + // Redraw the Phi arc to extend from the north pole + // down to only as far as the Theta ring sits. + // Then rotate the whole Phi arc about the poles. + + for( + + let + i = 0, + limit = this.phiGeometry.vertices.length; + + i < limit; + i ++ ){ + + const gain = i / ( limit - 1 ) + this.phiGeometry.vertices[ i ].set( + + Math.sin( state.theta * gain ) * this.radiusSafe, + Math.cos( state.theta * gain ) * this.radiusSafe, + 0 + ) + } + this.phiLine.setGeometry( this.phiGeometry ) + } + if( state.phi !== this.target.phi ){ + + this.phiMesh.rotation.y = state.phi - Math.PI / 2 + } + if( typeof this.onValueChange === 'function' ) this.onValueChange.call( this ) + } + } +}) + + + +module.exports = {BlochSphere} + + + diff --git a/packages/quantum-js-vis/Q-Circuit-Editor.css b/packages/quantum-js-vis/Q-Circuit-Editor.css new file mode 100644 index 0000000..6a6a261 --- /dev/null +++ b/packages/quantum-js-vis/Q-Circuit-Editor.css @@ -0,0 +1,900 @@ +/* + + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +*/ +@charset "utf-8"; + + + + + + + + + +/* + + Z indices: + + Clipboard =100 + Selected op 10 + Operation 0 + Shadow -10 + Background -20 + + + + + + Circuit + + Menu Moments + ╭───────┬───┬───┬───┬───╮ + │ ≡ ↘ │ 1 │ 2 │ 3 │ + │ Add moment + ├───┬───┼───┼───┼───┼───╯ + R │ 0 │|0⟩│ H │ C0│ X │ - + e ├───┼───┼───┼───┼───┤ + g │ 1 │|0⟩│ I │ C1│ X │ - + s ├───┼───┴───┴───┴───┘ + │ + │ - - - - + ╰───╯ + Add + register + + + Circuit Palette + + ╭───────────────────┬───╮ + │ H X Y Z S T π M … │ @ │ + ╰───────────────────┴───╯ + + + Circuit clipboard + + ┌───────────────┐ + ▟│ ┌───┬───────┐ │ + █│ │ H │ X#0.0 │ │ + █│ ├───┼───────┤ │ + █│ │ I │ X#0.1 │ │ + █│ └───┴───────┘ │ + █└───────────────┘ + ███████████████▛ + + + + ◢◣ + ◢■■■■◣ +◢■■■■■■■■◣ +◥■■■■■■■■◤ + ◥■■■■◤ + ◥◤ + + + ◢■■■■■■◤ + ◢◤ ◢◤ +◢■■■■■■◤ + + + ─────────── + ╲ ╱ ╱ ╱ + ╳ ╱ ╱ + ╱ ╲╱ ╱ + ─────── + + + ─────⦢ + ╱ ╱ +⦣───── + + +*/ + + + + + +.Q-circuit, +.Q-circuit-palette { + + position: relative; + width: 100%; +} +.Q-circuit-palette { + + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + line-height: 0; +} +.Q-circuit-palette > div { + + display: inline-block; + position: relative; + width: 4rem; + height: 4rem; +} + + +.Q-circuit { + + margin: 1rem 0 2rem 0; + /*border-top: 2px solid hsl( 0, 0%, 50% );*/ +} +.Q-parameters-box, +.Q-circuit-board-foreground { + line-height: 3.85rem; + width: auto; +} + + + + + + + /***************/ + /* */ + /* Toolbar */ + /* */ +/***************/ + + +.Q-circuit-toolbar { + + position: relative; + display: block; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + margin-bottom: 0.5rem; + + box-sizing: border-box; + display: grid; + grid-auto-columns: 3.6rem; + grid-auto-rows: 3.0rem; + grid-auto-flow: column; + +} +.Q-circuit-button { + + position: relative; + display: inline-block; + /*margin: 0 0.5rem 0.5rem 0;*/ + width: 3.6rem; + height: 3rem; +/* box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 0.8 ), + 0.1rem 0.1rem 0.1rem rgba( 0, 0, 0, 0.35 );*/ + + border-top: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 100% + ); + border-right: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 90% + ); + border-bottom: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 85% + ); + border-left: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 97% + ); + background: var( --Q-color-background ); +/* background: + var( --Q-color-background ) + linear-gradient( + + 0.4turn, + + rgba( 0, 0, 0, 0.02 ), + rgba( 255, 255, 255, 0.1 ) + );*/ + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 30% + ); + text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 ); + /*border-radius: 0.5rem;*/ + /*border-radius: 100%;*/ + line-height: 2.9rem; + text-align: center; + cursor: pointer; + overflow: hidden; + font-weight: 900; +} +.Q-circuit-toolbar .Q-circuit-button:first-child { + + border-top-left-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; +} +.Q-circuit-toolbar .Q-circuit-button:last-child { + + border-top-right-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} +.Q-circuit-locked .Q-circuit-button, +.Q-circuit-button[Q-disabled] { + + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 85% + ); + cursor: not-allowed; +} +.Q-circuit-locked .Q-circuit-toggle-lock { + + color: inherit; + cursor: pointer; +} + + + + +.Q-circuit-board-container { + + position: relative; + margin: 0 0 2rem 0; + margin: 0; + width: 100%; + max-height: 60vh; + overflow: scroll; +} +.Q-circuit-board { + + position: relative; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +/*.Q-circuit-palette,*/ +.Q-circuit-board-foreground, +.Q-circuit-board-background, +.Q-circuit-clipboard { + + box-sizing: border-box; + display: grid; + grid-auto-rows: 4rem; + grid-auto-columns: 4rem; + grid-auto-flow: column; +} + +.Q-parameters-box { + + position: absolute; + display: none; + z-index: 100; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: whitesmoke; +} + +/*.Q-circuit-palette,*/ +.Q-circuit-board-foreground, +.Q-circuit-board-background { + + position: relative; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.Q-circuit-clipboard { + + position: absolute; + z-index: 100; + min-width: 4rem; + min-height: 4rem; + transform: scale( 1.05 ); +} +.Q-circuit-clipboard, .Q-circuit-clipboard > div { + + cursor: grabbing; +} +.Q-circuit-clipboard-danger .Q-circuit-operation { + + background-color: var( --Q-color-yellow ); +} +.Q-circuit-clipboard-destroy { + + animation-name: Q-circuit-clipboard-poof; + animation-fill-mode: forwards; + animation-duration: 0.3s; + animation-iteration-count: 1; +} +@keyframes Q-circuit-clipboard-poof { + + 100% { + + transform: scale( 1.5 ); + opacity: 0; + } +} +.Q-circuit-board-background { + + /* + + Clipboard: 100 + Operation: 0 + Shadow: -10 + Background: -20 + + */ + position: absolute; + z-index: -20; + color: rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-board-background > div { + +/* transition: + background-color 0.2s, + color 0.2s;*/ +} +.Q-circuit-board-background .Q-circuit-cell-highlighted { + + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); + /*transition: none;*/ +} + + + + +.Q-circuit-register-wire { + + position: absolute; + top: calc( 50% - 0.5px ); + width: 100%; + height: 1px; + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 50% + ); +} + +.Q-parameter-box-exit { + position: relative; + right: 0; + left: 0; + width: 5rem; + height: 2.5rem; + background-color: whitesmoke; +} + +.Q-parameters-box > div, +.Q-circuit-palette > div, +.Q-circuit-clipboard > div, +.Q-circuit-board-foreground > div { + + text-align: center; +} + + + + + + + /***************/ + /* */ + /* Headers */ + /* */ +/***************/ + + +.Q-circuit-header { + + position: sticky; + z-index: 2; + margin: 0; + /*background-color: var( --Q-color-background );*/ + background-color: white; + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 75% + ); + font-family: var( --Q-font-family-mono ); +} +.Q-circuit-input.Q-circuit-cell-highlighted, +.Q-circuit-header.Q-circuit-cell-highlighted { + + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); + color: black; +} +.Q-circuit-selectall { + + z-index: 3; + margin: 0; + top: 0; + /*left: 4rem;*/ + /*grid-column: 2;*/ + left: 0; + grid-column-start: 1; + grid-column-end: 3; + grid-row: 1; + cursor: se-resize; +} +.Q-circuit-moment-label, +.Q-circuit-moment-add { + + grid-row: 1; + top: 0; + cursor: s-resize; +} +.Q-circuit-register-label, +.Q-circuit-register-add { + + grid-column: 2; + left: 4rem; + cursor: e-resize; +} +.Q-circuit-moment-add, +.Q-circuit-register-add { + + cursor: pointer; +} +.Q-circuit-moment-add, +.Q-circuit-register-add { + + display: none; +} +.Q-circuit-selectall, +.Q-circuit-moment-label, +.Q-circuit-moment-add { + + border-bottom: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); +} +.Q-circuit-selectall, +.Q-circuit-register-label, +.Q-circuit-register-add { + + border-right: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); +} +.Q-circuit-input { + + position: sticky; + z-index: 2; + grid-column: 1; + left: 0; + /*background-color: var( --Q-color-background );*/ + background-color: white; + font-size: 1.5rem; + font-weight: 900; + font-family: var( --Q-font-family-mono ); +} + + + + + + +.Q-circuit-operation-link-container { + + --Q-link-stroke: 3px; + --Q-link-radius: 100%; + + display: block; + position: relative; + left: calc( 50% - ( var( --Q-link-stroke ) / 2 )); + width: 50%; + height: 100%; + overflow: hidden; +} +.Q-circuit-operation-link-container.Q-circuit-cell-highlighted { + + background-color: transparent; +} +.Q-circuit-operation-link { + + display: block; + position: absolute; + width: calc( var( --Q-link-stroke ) * 2 ); + height: calc( 100% - 4rem + var( --Q-link-stroke )); + /*border: var( --Q-link-stroke ) solid hsl( 0, 0%, 50% );*/ + border: var( --Q-link-stroke ) solid hsl( + + var( --Q-color-background-hue ), + 10%, + 30% + ); + + /*border: var( --Q-link-stroke ) solid var( --Q-color-orange );*/ + + transform: translate( -50%, calc( 2rem - ( var( --Q-link-stroke ) / 2 ))); + transform-origin: center; +} +.Q-circuit-operation-link.Q-circuit-operation-link-curved { + + width: calc( var( --Q-link-radius ) - var( --Q-link-stroke )); + width: 200%; + border-radius: 100%; +} + + + + + + + /******************/ + /* */ + /* Operations */ + /* */ +/******************/ + +.Q-circuit-operation { + + position: relative; + /*--Q-operation-color-hue: var( --Q-color-green-hue ); + --Q-operation-color-main: var( --Q-color-green );*/ + + --Q-operation-color-hue: var( --Q-color-blue-hue ); + --Q-operation-color-main: hsl( + + var( --Q-operation-color-hue ), + 10%, + 35% + ); + + --Q-operation-color-light: hsl( + + var( --Q-operation-color-hue ), + 10%, + 50% + ); + --Q-operation-color-dark: hsl( + + var( --Q-operation-color-hue ), + 10%, + 25% + ); + color: white; + text-shadow: -0.05rem -0.05rem 0 rgba( 0, 0, 0, 0.1 ); + font-size: 1.5rem; + line-height: 2.9rem; + font-weight: 900; + cursor: grab; +} +.Q-circuit-locked .Q-circuit-operation { + + cursor: not-allowed; +} +.Q-circuit-operation-tile { + + position: absolute; + top: 0.5rem; + left: 0.5rem; + right: 0.5rem; + bottom: 0.5rem; + + /*margin: 0.5rem;*/ + /*padding: 0.5rem;*/ + + /*box-shadow: 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.2 );*/ + border-radius: 0.2rem; + /* + border-top: 0.1rem solid var( --Q-operation-color-light ); + border-left: 0.1rem solid var( --Q-operation-color-light ); + border-right: 0.1rem solid var( --Q-operation-color-dark ); + border-bottom: 0.1rem solid var( --Q-operation-color-dark ); + */ + background: + var( --Q-operation-color-main ) + /*linear-gradient( + + 0.45turn, + rgba( 255, 255, 255, 0.1 ), + rgba( 0, 0, 0, 0.05 ) + )*/; +} +.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover { + + /*background-color: rgba( 255, 255, 255, 0.6 );*/ + background-color: white; +} +.Q-circuit-palette .Q-circuit-operation-tile { + + --Q-before-rotation: 12deg; + --Q-before-x: 1px; + --Q-before-y: -2px; + + --Q-after-rotation: -7deg; + --Q-after-x: -2px; + --Q-after-y: 3px; + + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-palette .Q-circuit-operation-tile:before, +.Q-circuit-palette .Q-circuit-operation-tile:after { + + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: 0.2rem; + /*background-color: hsl( 0, 0%, 60% );*/ + + background-color: var( --Q-operation-color-dark ); + transform: + translate( var( --Q-before-x ), var( --Q-before-y )) + rotate( var( --Q-before-rotation )); + z-index: -10; + /*z-index: 10;*/ + display: block; + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-palette .Q-circuit-operation-tile:after { + + transform: + translate( var( --Q-after-x ), var( --Q-after-y )) + rotate( var( --Q-after-rotation )); + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-operation:hover .Q-circuit-operation-tile { + + color: white; +} + + + + +.Q-circuit-operation-hadamard .Q-circuit-operation-tile { + + /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ + /*--Q-operation-color-main: var( --Q-color-red );*/ + + /*--Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 10% );*/ + + +/* background: + linear-gradient( + + -33deg, + var( --Q-color-blue ) 20%, + #6f3c69 50%, + var( --Q-color-red ) 80% + );*/ +} +.Q-circuit-operation-identity .Q-circuit-operation-tile, +.Q-circuit-operation-control .Q-circuit-operation-tile, +.Q-circuit-operation-target .Q-circuit-operation-tile { + + /*--Q-operation-color-hue: var( --Q-color-orange-hue );*/ + /*--Q-operation-color-main: var( --Q-color-orange );*/ + border-radius: 100%; +} +.Q-circuit-operation-identity .Q-circuit-operation-tile, +.Q-circuit-operation-control .Q-circuit-operation-tile { + + top: calc( 50% - 0.7rem ); + left: calc( 50% - 0.7rem ); + width: 1.4rem; + height: 1.4rem; + overflow: hidden; +/* --Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 10% );*/ +} +.Q-circuit-operation-pauli-x, +.Q-circuit-operation-pauli-y, +.Q-circuit-operation-pauli-z { + + /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ + /*--Q-operation-color-main: var( --Q-color-red );*/ + +/* --Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 30% );*/ +} +.Q-circuit-operation-swap .Q-circuit-operation-tile { + + top: calc( 50% - 0.55rem ); + left: calc( 50% - 0.55rem ); + width: 1.2rem; + height: 1.2rem; + border-radius: 0; + transform-origin: center; + transform: rotate( 45deg ); + font-size: 0; +} + +.Q-parameter-box-input-container { + position: relative; + text-align: center; + grid-auto-columns: 4rem; + grid-auto-flow: column; +} + +.Q-parameter-box-input { + position: relative; + border-radius: .2rem; + margin-left: 10px; + font-family: var( --Q-font-family-mono ); +} + +.Q-parameter-input-label { + position: relative; + color: var( --Q-color-blue ); + font-family: var( --Q-font-family-mono ); +} + + + + + /********************/ + /* */ + /* Other states */ + /* */ +/********************/ + + +.Q-circuit-palette > div:hover, +.Q-circuit-board-foreground > div:hover { + + outline: 2px solid var( --Q-hyperlink-internal-color ); + outline-offset: -2px; +} +.Q-circuit-palette > div:hover .Q-circuit-operation-tile { + + box-shadow: none; +} +/*.Q-circuit-palette > div:hover,*/ +.Q-circuit-board-foreground > div:hover { + + background-color: white; + color: black; +} + + + + + + +.Q-circuit-clipboard > div, +.Q-circuit-cell-selected { + + background-color: white; +} +.Q-circuit-clipboard > div:before, +.Q-circuit-cell-selected:before { + + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: block; + z-index: -10; + box-shadow: + 0 0 1rem rgba( 0, 0, 0, 0.2 ), + 0.4rem 0.4rem 0.2rem rgba( 0, 0, 0, 0.2 ); + outline: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 50% + ); + /*outline-offset: -1px;*/ +} + + + + +.Q-circuit-clipboard > div { + + background-color: white; +} +.Q-circuit-clipboard > div:before { + + /* + + This was very helpful! + https://blog.dudak.me/2014/css-shadows-under-adjacent-elements/ + + */ + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -10; + display: block; + box-shadow: 0.4rem 0.4rem 0.3rem rgba( 0, 0, 0, 0.2 ); +} + + + + + + + + /***************/ + /* */ + /* Buttons */ + /* */ +/***************/ + + +.Q-circuit-locked .Q-circuit-toggle-lock, +.Q-circuit-locked .Q-circuit-toggle-lock:hover { + + background-color: var( --Q-color-red ); +} +.Q-circuit-toggle-lock { + + z-index: 3; + left: 0; + top: 0; + grid-column: 1; + grid-row: 1; + cursor: pointer; + font-size: 1.1rem; + text-shadow: none; + font-weight: normal; +} +.Q-circuit-button-undo, +.Q-circuit-button-redo { + + font-size: 1.2rem; + line-height: 2.6rem; + font-weight: normal; +} + + + +.Q-circuit p { + + padding: 1rem; + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 66% + ); +} + + + diff --git a/packages/quantum-js-vis/Q-Circuit-Editor.js b/packages/quantum-js-vis/Q-Circuit-Editor.js new file mode 100644 index 0000000..7c225a6 --- /dev/null +++ b/packages/quantum-js-vis/Q-Circuit-Editor.js @@ -0,0 +1,2281 @@ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +const {Q, Circuit, Gate, logger, misc, mathf } = require('quantum-js-util'); +Editor = function( circuit, targetEl ){ + // First order of business, + // we require a valid circuit. + + if( circuit instanceof Circuit !== true ) circuit = new Circuit() + this.circuit = circuit + this.index = Editor.index ++ + + + // Editor is all about the DOM + // so we’re going to get some use out of this + // stupid (but convenient) shorthand here. + + const createDiv = function(){ + + return document.createElement( 'div' ) + } + + + + + + + // We want to “name” our circuit editor instance + // but more importantly we want to give it a unique DOM ID. + // Keep in mind we can have MULTIPLE editors + // for the SAME circuit! + // This is a verbose way to do it, + // but each step is clear and I needed clarity today! ;) + + this.name = typeof circuit.name === 'string' ? + circuit.name : + 'Q Editor '+ this.index + + + // If we’ve been passed a target DOM element + // we should use that as our circuit element. + + if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl ) + const circuitEl = targetEl instanceof HTMLElement ? targetEl : createDiv() + circuitEl.classList.add( 'Q-circuit' ) + + + // If the target element already has an ID + // then we want to use that as our domID. + + if( typeof circuitEl.getAttribute( 'id' ) === 'string' ){ + + this.domId = circuitEl.getAttribute( 'id' ) + } + + + // Otherwise let’s transform our name value + // into a usable domId. + + else { + + let domIdBase = this.name + .replace( /^[^a-z]+|[^\w:.-]+/gi, '-' ), + domId = domIdBase, + domIdAttempt = 1 + + while( document.getElementById( domId ) !== null ){ + + domIdAttempt ++ + domId = domIdBase +'-'+ domIdAttempt + } + this.domId = domId + circuitEl.setAttribute( 'id', this.domId ) + } + + + + + // We want a way to easily get to the circuit + // from this interface’s DOM element. + // (But we don’t need a way to reference this DOM element + // from the circuit. A circuit can have many DOM elements!) + // And we also want an easy way to reference this DOM element + // from this Editor instance. + + circuitEl.circuit = circuit + this.domElement = circuitEl + + + // Create a toolbar for containing buttons. + + const toolbarEl = createDiv() + circuitEl.appendChild( toolbarEl ) + toolbarEl.classList.add( 'Q-circuit-toolbar' ) + + + // Create a toggle switch for locking the circuit. + + const lockToggle = createDiv() + toolbarEl.appendChild( lockToggle ) + lockToggle.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-lock' ) + lockToggle.setAttribute( 'title', 'Lock / unlock' ) + lockToggle.innerText = '🔓' + + + // Create an “Undo” button + // that enables and disables + // based on available undo history. + + const undoButton = createDiv() + toolbarEl.appendChild( undoButton ) + undoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-undo' ) + undoButton.setAttribute( 'title', 'Undo' ) + undoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + undoButton.innerHTML = '⟲' + window.addEventListener( 'History undo is depleted', function( event ){ + + if( event.detail.instance === circuit ) + undoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + }) + window.addEventListener( 'History undo is capable', function( event ){ + + if( event.detail.instance === circuit ) + undoButton.removeAttribute( 'Q-disabled' ) + }) + + + // Create an “Redo” button + // that enables and disables + // based on available redo history. + + const redoButton = createDiv() + toolbarEl.appendChild( redoButton ) + redoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-redo' ) + redoButton.setAttribute( 'title', 'Redo' ) + redoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + redoButton.innerHTML = '⟳' + window.addEventListener( 'History redo is depleted', function( event ){ + + if( event.detail.instance === circuit ) + redoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + }) + window.addEventListener( 'History redo is capable', function( event ){ + + if( event.detail.instance === circuit ) + redoButton.removeAttribute( 'Q-disabled' ) + }) + + + // Create a button for joining + // an “identity cursor” + // and one or more same-gate operations + // into a controlled operation. + // (Will be enabled / disabled from elsewhere.) + + const controlButton = createDiv() + toolbarEl.appendChild( controlButton ) + controlButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-control' ) + controlButton.setAttribute( 'title', 'Create controlled operation' ) + controlButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + controlButton.innerText = 'C' + + + // Create a button for joining + // two “identity cursors” + // into a swap operation. + // (Will be enabled / disabled from elsewhere.) + + const swapButton = createDiv() + toolbarEl.appendChild( swapButton ) + swapButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle-swap' ) + swapButton.setAttribute( 'title', 'Create swap operation' ) + swapButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + swapButton.innerText = 'S' + + + // Create a circuit board container + // so we can house a scrollable circuit board. + + const boardContainerEl = createDiv() + circuitEl.appendChild( boardContainerEl ) + boardContainerEl.classList.add( 'Q-circuit-board-container' ) + //boardContainerEl.addEventListener( 'touchstart', Editor.onPointerPress ) + boardContainerEl.addEventListener( 'mouseleave', function(){ + Editor.unhighlightAll( circuitEl ) + }) + + const boardEl = createDiv() + boardContainerEl.appendChild( boardEl ) + boardEl.classList.add( 'Q-circuit-board' ) + + const backgroundEl = createDiv() + boardEl.appendChild( backgroundEl ) + backgroundEl.classList.add( 'Q-circuit-board-background' ) + + const parameterEl = createDiv() + boardEl.appendChild( parameterEl ) + parameterEl.classList.add( 'Q-parameters-box' ) + // Create background highlight bars + // for each row. + + for( let i = 0; i < circuit.bandwidth; i ++ ){ + + const rowEl = createDiv() + backgroundEl.appendChild( rowEl ) + rowEl.style.position = 'relative' + rowEl.style.gridRowStart = i + 2 + rowEl.style.gridColumnStart = 1 + rowEl.style.gridColumnEnd = Editor.momentIndexToGridColumn( circuit.timewidth ) + 1 + rowEl.setAttribute( 'register-index', i + 1 ) + + const wireEl = createDiv() + rowEl.appendChild( wireEl ) + wireEl.classList.add( 'Q-circuit-register-wire' ) + } + + + // Create background highlight bars + // for each column. + + for( let i = 0; i < circuit.timewidth; i ++ ){ + + const columnEl = createDiv() + backgroundEl.appendChild( columnEl ) + columnEl.style.gridRowStart = 2 + columnEl.style.gridRowEnd = Editor.registerIndexToGridRow( circuit.bandwidth ) + 1 + columnEl.style.gridColumnStart = i + 3 + columnEl.setAttribute( 'moment-index', i + 1 ) + } + + + // Create the circuit board foreground + // for all interactive elements. + + const foregroundEl = createDiv() + boardEl.appendChild( foregroundEl ) + foregroundEl.classList.add( 'Q-circuit-board-foreground' ) + + + // Add “Select All” toggle button to upper-left corner. + + const selectallEl = createDiv() + foregroundEl.appendChild( selectallEl ) + selectallEl.classList.add( 'Q-circuit-header', 'Q-circuit-selectall' ) + selectallEl.setAttribute( 'title', 'Select all' ) + selectallEl.setAttribute( 'moment-index', '0' ) + selectallEl.setAttribute( 'register-index', '0' ) + selectallEl.innerHTML = '↘' + + + // Add register index symbols to left-hand column. + + for( let i = 0; i < circuit.bandwidth; i ++ ){ + + const + registerIndex = i + 1, + registersymbolEl = createDiv() + + foregroundEl.appendChild( registersymbolEl ) + registersymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-label' ) + registersymbolEl.setAttribute( 'title', 'Register '+ registerIndex +' of '+ circuit.bandwidth ) + registersymbolEl.setAttribute( 'register-index', registerIndex ) + registersymbolEl.style.gridRowStart = Editor.registerIndexToGridRow( registerIndex ) + registersymbolEl.innerText = registerIndex + } + + + // Add “Add register” button.q + + const addRegisterEl = createDiv() + foregroundEl.appendChild( addRegisterEl ) + addRegisterEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-add' ) + addRegisterEl.setAttribute( 'title', 'Add register' ) + addRegisterEl.style.gridRowStart = Editor.registerIndexToGridRow( circuit.bandwidth + 1 ) + addRegisterEl.innerText = '+' + + + // Add moment index symbols to top row. + + for( let i = 0; i < circuit.timewidth; i ++ ){ + + const + momentIndex = i + 1, + momentsymbolEl = createDiv() + + foregroundEl.appendChild( momentsymbolEl ) + momentsymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-label' ) + momentsymbolEl.setAttribute( 'title', 'Moment '+ momentIndex +' of '+ circuit.timewidth ) + momentsymbolEl.setAttribute( 'moment-index', momentIndex ) + momentsymbolEl.style.gridColumnStart = Editor.momentIndexToGridColumn( momentIndex ) + momentsymbolEl.innerText = momentIndex + } + + + // Add “Add moment” button. + + const addMomentEl = createDiv() + foregroundEl.appendChild( addMomentEl ) + addMomentEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-add' ) + addMomentEl.setAttribute( 'title', 'Add moment' ) + addMomentEl.style.gridColumnStart = Editor.momentIndexToGridColumn( circuit.timewidth + 1 ) + addMomentEl.innerText = '+' + + + // Add input values. + + circuit.qubits.forEach( function( qubit, i ){ + + const + rowIndex = i + 1, + inputEl = createDiv() + + inputEl.classList.add( 'Q-circuit-header', 'Q-circuit-input' ) + inputEl.setAttribute( 'title', `Qubit #${ rowIndex } starting value` ) + inputEl.setAttribute( 'register-index', rowIndex ) + inputEl.style.gridRowStart = Editor.registerIndexToGridRow( rowIndex ) + inputEl.innerText = qubit.beta.toText() + foregroundEl.appendChild( inputEl ) + }) + + + // Add operations. + + circuit.operations.forEach( function( operation ){ + Editor.set( circuitEl, operation ) + }) + + + // Add event listeners. + + circuitEl.addEventListener( 'mousedown', Editor.onPointerPress ) + circuitEl.addEventListener( 'touchstart', Editor.onPointerPress ) + window.addEventListener( + + 'Circuit.set$', + Editor.prototype.onExternalSet.bind( this ) + ) + window.addEventListener( + + 'Circuit.clear$', + Editor.prototype.onExternalClear.bind( this ) + ) + + + // How can we interact with this circuit + // through code? (How cool is this?!) + + const referenceEl = document.createElement( 'p' ) + circuitEl.appendChild( referenceEl ) + referenceEl.innerHTML = ` + This circuit is accessible in your + JavaScript console + as document.getElementById('${ this.domId }').circuit` + //document.getElementById('Q-Editor-0').circuit + //$('#${ this.domId }') + + + // Put a note in the JavaScript console + // that includes how to reference the circuit via code + // and an ASCII diagram for reference. + + logger.warn( 0.5, + `\n\nCreated a DOM interface for $('#${ this.domId }').circuit\n\n`, + circuit.toDiagram(), + '\n\n\n' + ) +} + + +// Augment Circuit to have this functionality. + +Circuit.toDom = function( circuit, targetEl ){ + + return new Editor( circuit, targetEl ).domElement +} +Circuit.prototype.toDom = function( targetEl ){ + + return new Editor( this, targetEl ).domElement +} + + + + + + + + +Object.assign( Editor, { + + index: 0, + help: function(){ return logger.help( this )}, + dragEl: null, + gridColumnToMomentIndex: function( gridColumn ){ return +gridColumn - 2 }, + momentIndexToGridColumn: function( momentIndex ){ return momentIndex + 2 }, + gridRowToRegisterIndex: function( gridRow ){ return +gridRow - 1 }, + registerIndexToGridRow: function( registerIndex ){ return registerIndex + 1 }, + gridSize: 4,// CSS: grid-auto-columns = grid-auto-rows = 4rem. + pointToGrid: function( p ){ + + + // Take a 1-dimensional point value + // (so either an X or a Y but not both) + // and return what CSS grid cell contains it + // based on our 4rem × 4rem grid setup. + + const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize ) + return 1 + Math.floor( p / ( rem * Editor.gridSize )) + }, + gridToPoint: function( g ){ + + + // Take a 1-dimensional grid cell value + // (so either a row or a column but not both) + // and return the minimum point value it contains. + + const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize ) + return rem * Editor.gridSize * ( g - 1 ) + }, + getInteractionCoordinates: function( event, pageOrClient ){ + + if( typeof pageOrClient !== 'string' ) pageOrClient = 'client'//page + if( event.changedTouches && + event.changedTouches.length ) return { + + x: event.changedTouches[ 0 ][ pageOrClient +'X' ], + y: event.changedTouches[ 0 ][ pageOrClient +'Y' ] + } + return { + x: event[ pageOrClient +'X' ], + y: event[ pageOrClient +'Y' ] + } + }, + createNewElement :function(element_type, element_parent, element_css) { + element = document.createElement(element_type) + if(element_css) element.classList.add(element_css) + if(element_parent) element_parent.appendChild( element ) + return element + }, + createPalette: function( targetEl ){ + + if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl ) + + const + paletteEl = targetEl instanceof HTMLElement ? targetEl : document.createElement( 'div' ), + randomRangeAndSign = function( min, max ){ + + const r = min + Math.random() * ( max - min ) + return Math.floor( Math.random() * 2 ) ? r : -r + } + + //ltnln: added missing Braket operations. + paletteEl.classList.add( 'Q-circuit-palette' ); + 'H,X,Y,Z,P,Rx,Ry,Rz,U,V,V†,S*,S†,T,T†,00,01,10,√S,iS,XX,XY,YY,ZZ,*' + .split( ',' ) + .forEach( function( symbol ){ + + const gate = Gate.findBySymbol( symbol ) + + const operationEl = document.createElement( 'div' ) + paletteEl.appendChild( operationEl ) + operationEl.classList.add( 'Q-circuit-operation' ) + operationEl.classList.add( 'Q-circuit-operation-'+ gate.nameCss ) + operationEl.setAttribute( 'gate-symbol', symbol ) + operationEl.setAttribute( 'title', gate.name ) + + const tileEl = document.createElement( 'div' ) + operationEl.appendChild( tileEl ) + tileEl.classList.add( 'Q-circuit-operation-tile' ) + if( symbol !== Gate.CURSOR.symbol ) tileEl.innerText = symbol + + ;[ 'before', 'after' ].forEach( function( layer ){ + + tileEl.style.setProperty( '--Q-'+ layer +'-rotation', randomRangeAndSign( 0.5, 4 ) +'deg' ) + tileEl.style.setProperty( '--Q-'+ layer +'-x', randomRangeAndSign( 1, 4 ) +'px' ) + tileEl.style.setProperty( '--Q-'+ layer +'-y', randomRangeAndSign( 1, 3 ) +'px' ) + }) + }) + + paletteEl.addEventListener( 'mousedown', Editor.onPointerPress ) + paletteEl.addEventListener( 'touchstart', Editor.onPointerPress ) + return paletteEl + }, + toDom: function( circuit, targetEl ){ + + return new Editor( circuit, targetEl ).domElement + } +}) + + + + + + + ///////////////////////// + // // + // Operation CLEAR // + // // +///////////////////////// + + +Editor.prototype.onExternalClear = function( event ){ + + if( event.detail.circuit === this.circuit ){ + + Editor.clear( this.domElement, { + + momentIndex: event.detail.momentIndex, + registerIndices: event.detail.registerIndices + }) + } +} +Editor.clear = function( circuitEl, operation ){ + + const momentIndex = operation.momentIndex + operation.registerIndices.forEach( function( registerIndex ){ + + Array + .from( circuitEl.querySelectorAll( + + `[moment-index="${ momentIndex }"]`+ + `[register-index="${ registerIndex }"]` + + )) + .forEach( function( op ){ + + op.parentNode.removeChild( op ) + }) + }) +} + + + + + + + /////////////////////// + // // + // Operation SET // + // // +/////////////////////// + + +Editor.prototype.onExternalSet = function( event ){ + + if( event.detail.circuit === this.circuit ){ + + Editor.set( this.domElement, event.detail.operation ) + } +} +Editor.set = function( circuitEl, operation ){ + const + backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ), + foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ), + circuit = circuitEl.circuit, + operationIndex = circuitEl.circuit.operations.indexOf( operation ) + + operation.registerIndices.forEach( function( registerIndex, i ){ + const operationEl = document.createElement( 'div' ) + foregroundEl.appendChild( operationEl ) + operationEl.classList.add( 'Q-circuit-operation', 'Q-circuit-operation-'+ operation.gate.nameCss ) + // operationEl.setAttribute( 'operation-index', operationIndex ) + operationEl.setAttribute( 'gate-symbol', operation.gate.symbol ) + operationEl.setAttribute( 'gate-index', operation.gate.index )// Used as an application-wide unique ID! + operationEl.setAttribute( 'moment-index', operation.momentIndex ) + operationEl.setAttribute( 'register-index', registerIndex ) + operationEl.setAttribute( 'register-array-index', i )// Where within the registerIndices array is this operations fragment located? + operationEl.setAttribute( 'is-controlled', operation.isControlled ) + operationEl.setAttribute( 'title', operation.gate.name ) + operationEl.style.gridColumnStart = Editor.momentIndexToGridColumn( operation.momentIndex ) + operationEl.style.gridRowStart = Editor.registerIndexToGridRow( registerIndex ) + if( operation.gate.has_parameters ) Object.keys(operation.gate.parameters).forEach( element => { + operationEl.setAttribute( element, operation.gate.parameters[element] ) //adds a parameter attribute to the operation! + }) + const tileEl = document.createElement( 'div' ) + operationEl.appendChild( tileEl ) + tileEl.classList.add( 'Q-circuit-operation-tile' ) + if( operation.gate.symbol !== Gate.CURSOR.symbol ) tileEl.innerText = operation.gate.symbol + + + // Add operation link wires + // for multi-qubit operations. + + if( operation.registerIndices.length > 1 ){ + + operationEl.setAttribute( 'register-indices', operation.registerIndices ) + operationEl.setAttribute( 'register-indices-index', i ) + operationEl.setAttribute( + + 'sibling-indices', + operation.registerIndices + .filter( function( siblingRegisterIndex ){ + + return registerIndex !== siblingRegisterIndex + }) + ) + operation.registerIndices.forEach( function( registerIndex, i ){ + + if( i < operation.registerIndices.length - 1 ){ + + const + siblingRegisterIndex = operation.registerIndices[ i + 1 ], + registerDelta = Math.abs( siblingRegisterIndex - registerIndex ), + start = Math.min( registerIndex, siblingRegisterIndex ), + end = Math.max( registerIndex, siblingRegisterIndex ), + containerEl = document.createElement( 'div' ), + linkEl = document.createElement( 'div' ) + + backgroundEl.appendChild( containerEl ) + containerEl.setAttribute( 'moment-index', operation.momentIndex ) + containerEl.setAttribute( 'register-index', registerIndex ) + containerEl.classList.add( 'Q-circuit-operation-link-container' ) + containerEl.style.gridRowStart = Editor.registerIndexToGridRow( start ) + containerEl.style.gridRowEnd = Editor.registerIndexToGridRow( end + 1 ) + containerEl.style.gridColumn = Editor.momentIndexToGridColumn( operation.momentIndex ) + + containerEl.appendChild( linkEl ) + linkEl.classList.add( 'Q-circuit-operation-link' ) + if( registerDelta > 1 ) linkEl.classList.add( 'Q-circuit-operation-link-curved' ) + } + }) + if( operation.isControlled && i === 0 ){ + operationEl.classList.add( 'Q-circuit-operation-control' ) + operationEl.setAttribute( 'title', 'Control' ) + tileEl.innerText = '' + } + else operationEl.classList.add( 'Q-circuit-operation-target' ) + } + }) +} + + + + +Editor.isValidControlCandidate = function( circuitEl ){ + + const + selectedOperations = Array + .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) + + + // We must have at least two operations selected, + // hopefully a control and something else, + // in order to attempt a join. + + if( selectedOperations.length < 2 ) return false + + + // Note the different moment indices present + // among the selected operations. + + const moments = selectedOperations.reduce( function( moments, operationEl ){ + + moments[ operationEl.getAttribute( 'moment-index' )] = true + return moments + + }, {} ) + + + // All selected operations must be in the same moment. + + if( Object.keys( moments ).length > 1 ) return false + + + // If there are multi-register operations present, + // regardless of whether those are controls or swaps, + // all siblings must be present + // in order to join a new gate to this selection. + + // I’m sure we can make this whole routine much more efficient + // but its results are correct and boy am I tired ;) + + const allSiblingsPresent = selectedOperations + .reduce( function( status, operationEl ){ + + const registerIndicesString = operationEl.getAttribute( 'register-indices' ) + + + // If it’s a single-register operation + // there’s no need to search further. + + if( !registerIndicesString ) return status + + + // How many registers are in use + // by this operation? + + const + registerIndicesLength = registerIndicesString + .split( ',' ) + .map( function( registerIndex ){ + + return +registerIndex + }) + .length, + + + // How many of this operation’s siblings + // (including itself) can we find? + + allSiblingsLength = selectedOperations + .reduce( function( siblings, operationEl ){ + + if( operationEl.getAttribute( 'register-indices' ) === registerIndicesString ){ + + siblings.push( operationEl ) + } + return siblings + + }, []) + .length + + + // Did we find all of the siblings for this operation? + // Square that with previous searches. + + return status && allSiblingsLength === registerIndicesLength + + }, true ) + + + // If we’re missing some siblings + // then we cannot modify whatever we have selected here. + + if( allSiblingsPresent !== true ) return false + + // Note the different gate types present + // among the selected operations. + + const gates = selectedOperations.reduce( function( gates, operationEl ){ + const gateSymbol = operationEl.getAttribute( 'gate-symbol' ) + if( !mathf.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1 + else gates[ gateSymbol ] ++ + return gates + + }, {} ) + + + // Note if each operation is already controlled or not. + + const { + + totalControlled, + totalNotControlled + + } = selectedOperations + .reduce( function( stats, operationEl ){ + + if( operationEl.getAttribute( 'is-controlled' ) === 'true' ) + stats.totalControlled ++ + else stats.totalNotControlled ++ + return stats + + }, { + + totalControlled: 0, + totalNotControlled: 0 + }) + + // This could be ONE “identity cursor” + // and one or more of a regular single gate + // that is NOT already controlled. + + if( gates[ Gate.CURSOR.symbol ] === 1 && + Object.keys( gates ).length === 2 && + totalNotControlled === selectedOperations.length ){ + + return true + } + + + // There’s NO “identity cursor” + // but there is one or more of specific gate type + // and at least one of those is already controlled. + + if( gates[ Gate.CURSOR.symbol ] === undefined && + Object.keys( gates ).length === 1 && + totalControlled > 0 && + totalNotControlled > 0 ){ + + return true + } + + + // Any other combination allowed? Nope! + + return false +} +Editor.createControl = function( circuitEl ){ + + if( Editor.isValidControlCandidate( circuitEl ) !== true ) return this + + + const + circuit = circuitEl.circuit, + selectedOperations = Array + .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )), + + + // Are any of these controlled operations?? + // If so, we need to find its control component + // and re-use it. + + existingControlEl = selectedOperations.find( function( operationEl ){ + + return ( + + operationEl.getAttribute( 'is-controlled' ) === 'true' && + operationEl.getAttribute( 'register-array-index' ) === '0' + ) + }), + + + // One control. One or more targets. + + control = existingControlEl || selectedOperations + .find( function( el ){ + + return el.getAttribute( 'gate-symbol' ) === Gate.CURSOR.symbol + }), + targets = selectedOperations + .reduce( function( targets, el ){ + + //if( el.getAttribute( 'gate-symbol' ) !== '!' ) targets.push( el ) + if( el !== control ) targets.push( el ) + return targets + + }, [] ) + + + // Ready to roll. + + circuit.history.createEntry$() + selectedOperations.forEach( function( operationEl ){ + + circuit.clear$( + + +operationEl.getAttribute( 'moment-index' ), + +operationEl.getAttribute( 'register-index' ) + ) + }) + circuit.set$( + targets[ 0 ].getAttribute( 'gate-symbol' ), + +control.getAttribute( 'moment-index' ), + [ +control.getAttribute( 'register-index' )].concat( + + targets.reduce( function( registers, operationEl ){ + + registers.push( +operationEl.getAttribute( 'register-index' )) + return registers + + }, [] ) + ) + ) + + + // Update our toolbar button states. + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + + return this +} + + + + +Editor.isValidSwapCandidate = function( circuitEl ){ + + const + selectedOperations = Array + .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) + + + // We can only swap between two registers. + // No crazy rotation-swap bullshit. (Yet.) + if( selectedOperations.length !== 2 ) return false + + + // Both operations must be “identity cursors.” + // If so, we are good to go. + + areBothCursors = selectedOperations.every( function( operationEl ){ + + return operationEl.getAttribute( 'gate-symbol' ) === Gate.CURSOR.symbol + }) + if( areBothCursors ) return true + + + // Otherwise this is not a valid swap candidate. + + return false +} +Editor.createSwap = function( circuitEl ){ + + if( Editor.isValidSwapCandidate( circuitEl ) !== true ) return this + + const + selectedOperations = Array + .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )), + momentIndex = +selectedOperations[ 0 ].getAttribute( 'moment-index' ) + registerIndices = selectedOperations + .reduce( function( registerIndices, operationEl ){ + + registerIndices.push( +operationEl.getAttribute( 'register-index' )) + return registerIndices + + }, [] ), + circuit = circuitEl.circuit + + + // Create the swap operation. + + circuit.history.createEntry$() + selectedOperations.forEach( function( operation ){ + + circuit.clear$( + + +operation.getAttribute( 'moment-index' ), + +operation.getAttribute( 'register-index' ) + ) + }) + circuit.set$( + + Gate.SWAP, + momentIndex, + registerIndices + ) + + + // Update our toolbar button states. + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + + return this +} + + + + +Editor.onSelectionChanged = function( circuitEl ){ + + const controlButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-control' ) + if( Editor.isValidControlCandidate( circuitEl )){ + + controlButtonEl.removeAttribute( 'Q-disabled' ) + } + else controlButtonEl.setAttribute( 'Q-disabled', true ) + + const swapButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-swap' ) + if( Editor.isValidSwapCandidate( circuitEl )){ + + swapButtonEl.removeAttribute( 'Q-disabled' ) + } + else swapButtonEl.setAttribute( 'Q-disabled', true ) +} +Editor.onCircuitChanged = function( circuitEl ){ + + const circuit = circuitEl.circuit + window.dispatchEvent( new CustomEvent( + + 'Q gui altered circuit', + { detail: { circuit: circuit }} + )) + + // Should we trigger a circuit.evaluate$() here? + // Particularly when we move all that to a new thread?? + // console.log( originCircuit.report$() ) ?? +} + + + + + +Editor.unhighlightAll = function( circuitEl ){ + + Array.from( circuitEl.querySelectorAll( + + '.Q-circuit-board-background > div,'+ + '.Q-circuit-board-foreground > div' + )) + .forEach( function( el ){ + + el.classList.remove( 'Q-circuit-cell-highlighted' ) + }) +} + + + + + + + ////////////////////// + // // + // Pointer MOVE // + // // +////////////////////// + + +Editor.onPointerMove = function( event ){ + + + // We need our cursor coordinates straight away. + // We’ll use that both for dragging (immediately below) + // and for hover highlighting (further below). + // Let’s also hold on to a list of all DOM elements + // that contain this X, Y point + // and also see if one of those is a circuit board container. + + const + { x, y } = Editor.getInteractionCoordinates( event ), + foundEls = document.elementsFromPoint( x, y ), + boardContainerEl = foundEls.find( function( el ){ + + return el.classList.contains( 'Q-circuit-board-container' ) + }) + + + // Are we in the middle of a circuit clipboard drag? + // If so we need to move that thing! + + if( Editor.dragEl !== null ){ + + + // ex. Don’t scroll on touch devices! + + event.preventDefault() + + + // This was a very useful resource + // for a reality check on DOM coordinates: + // https://javascript.info/coordinates + + Editor.dragEl.style.left = ( x + window.pageXOffset + Editor.dragEl.offsetX ) +'px' + Editor.dragEl.style.top = ( y + window.pageYOffset + Editor.dragEl.offsetY ) +'px' + + if( !boardContainerEl && Editor.dragEl.circuitEl ) Editor.dragEl.classList.add( 'Q-circuit-clipboard-danger' ) + else Editor.dragEl.classList.remove( 'Q-circuit-clipboard-danger' ) + } + + + // If we’re not over a circuit board container + // then there’s no highlighting work to do + // so let’s bail now. + + if( !boardContainerEl ) return + + + // Now we know we have a circuit board + // so we must have a circuit + // and if that’s locked then highlighting changes allowed! + + const circuitEl = boardContainerEl.closest( '.Q-circuit' ) + if( circuitEl.classList.contains( 'Q-circuit-locked' )) return + + + // Ok, we’ve found a circuit board. + // First, un-highlight everything. + + Array.from( boardContainerEl.querySelectorAll(` + + .Q-circuit-board-background > div, + .Q-circuit-board-foreground > div + + `)).forEach( function( el ){ + + el.classList.remove( 'Q-circuit-cell-highlighted' ) + }) + + + // Let’s prioritize any element that is “sticky” + // which means it can appear OVER another grid cell. + + + const + cellEl = foundEls.find( function( el ){ + + const style = window.getComputedStyle( el ) + return ( + + style.position === 'sticky' && ( + + el.getAttribute( 'moment-index' ) !== null || + el.getAttribute( 'register-index' ) !== null + ) + ) + }), + highlightByQuery = function( query ){ + + Array.from( boardContainerEl.querySelectorAll( query )) + .forEach( function( el ){ + + el.classList.add( 'Q-circuit-cell-highlighted' ) + }) + } + + + // If we’ve found one of these “sticky” cells + // let’s use its moment and/or register data + // to highlight moments or registers (or all). + + if( cellEl ){ + + const + momentIndex = cellEl.getAttribute( 'moment-index' ), + registerIndex = cellEl.getAttribute( 'register-index' ) + + if( momentIndex === null ){ + + highlightByQuery( `div[register-index="${ registerIndex }"]` ) + return + } + if( registerIndex === null ){ + + highlightByQuery( `div[moment-index="${ momentIndex }"]` ) + return + } + highlightByQuery(` + + .Q-circuit-board-background > div[moment-index], + .Q-circuit-board-foreground > .Q-circuit-operation + + `) + return + } + + + // Ok, we know we’re hovering over the circuit board + // but we’re not on a “sticky” cell. + // We might be over an operation, but we might not. + // No matter -- we’ll infer the moment and register indices + // from the cursor position. + + const + boardElBounds = boardContainerEl.getBoundingClientRect(), + xLocal = x - boardElBounds.left + boardContainerEl.scrollLeft + 1, + yLocal = y - boardElBounds.top + boardContainerEl.scrollTop + 1, + columnIndex = Editor.pointToGrid( xLocal ), + rowIndex = Editor.pointToGrid( yLocal ), + momentIndex = Editor.gridColumnToMomentIndex( columnIndex ), + registerIndex = Editor.gridRowToRegisterIndex( rowIndex ) + + + // If this hover is “out of bounds” + // ie. on the same row or column as an “Add register” or “Add moment” button + // then let’s not highlight anything. + + if( momentIndex > circuitEl.circuit.timewidth || + registerIndex > circuitEl.circuit.bandwidth ) return + + + // If we’re at 0, 0 or below that either means + // we’re over the “Select all” button (already taken care of above) + // or over the lock toggle button. + // Either way, it’s time to bail. + + if( momentIndex < 1 || registerIndex < 1 ) return + + + // If we’ve made it this far that means + // we have valid moment and register indices. + // Highlight them! + + highlightByQuery(` + + div[moment-index="${ momentIndex }"], + div[register-index="${ registerIndex }"] + `) + return +} + + + + /////////////////////// + // // + // Pointer PRESS // + // // +/////////////////////// + + +Editor.onPointerPress = function( event ){ + // This is just a safety net + // in case something terrible has ocurred. + // (ex. Did the user click and then their mouse ran + // outside the window but browser didn’t catch it?) + + if( Editor.dragEl !== null ){ + + Editor.onPointerRelease( event ) + return + } + const + targetEl = event.target, + circuitEl = targetEl.closest( '.Q-circuit' ), + paletteEl = targetEl.closest( '.Q-circuit-palette' ) + parameterEl = targetEl.closest( '.Q-parameters-box' ) + + // If we can’t find a circuit that’s a really bad sign + // considering this event should be fired when a circuit + // is clicked on. So... bail! + + if( !circuitEl && !paletteEl ) return + + // This is a bit of a gamble. + // There’s a possibility we’re not going to drag anything, + // but we’ll prep these variables here anyway + // because both branches of if( circuitEl ) and if( paletteEl ) + // below will have access to this scope. + + dragEl = document.createElement( 'div' ) + dragEl.classList.add( 'Q-circuit-clipboard' ) + const { x, y } = Editor.getInteractionCoordinates( event ) + + + // Are we dealing with a circuit interface? + // ie. NOT a palette interface. + + if( circuitEl && !parameterEl ){ + + // Shall we toggle the circuit lock? + + const + circuit = circuitEl.circuit, + circuitIsLocked = circuitEl.classList.contains( 'Q-circuit-locked' ), + lockEl = targetEl.closest( '.Q-circuit-toggle-lock' ) + + if( lockEl ){ + + // const toolbarEl = Array.from( circuitEl.querySelectorAll( '.Q-circuit-button' )) + if( circuitIsLocked ){ + + circuitEl.classList.remove( 'Q-circuit-locked' ) + lockEl.innerText = '🔓' + } + else { + + circuitEl.classList.add( 'Q-circuit-locked' ) + lockEl.innerText = '🔒' + Editor.unhighlightAll( circuitEl ) + } + + + // We’ve toggled the circuit lock button + // so we should prevent further propagation + // before proceeding further. + // That includes running all this code again + // if it was originally fired by a mouse event + // and about to be fired by a touch event! + + event.preventDefault() + event.stopPropagation() + return + } + + + // If our circuit is already “locked” + // then there’s nothing more to do here. + + if( circuitIsLocked ) { + + logger.warn( `User attempted to interact with a circuit editor but it was locked.` ) + return + } + + + const + cellEl = targetEl.closest(` + + .Q-circuit-board-foreground > div, + .Q-circuit-palette > div + `), + undoEl = targetEl.closest( '.Q-circuit-button-undo' ), + redoEl = targetEl.closest( '.Q-circuit-button-redo' ), + controlEl = targetEl.closest( '.Q-circuit-toggle-control' ), + swapEl = targetEl.closest( '.Q-circuit-toggle-swap' ), + addMomentEl = targetEl.closest( '.Q-circuit-moment-add' ), + addRegisterEl = targetEl.closest( '.Q-circuit-register-add' ) + + if( !cellEl && + !undoEl && + !redoEl && + !controlEl && + !swapEl && + !addMomentEl && + !addRegisterEl ) return + + + // By this point we know that the circuit is unlocked + // and that we’ll activate a button / drag event / etc. + // So we need to hault futher event propagation + // including running this exact code again if this was + // fired by a touch event and about to again by mouse. + // This may SEEM redundant because we did this above + // within the lock-toggle button code + // but we needed to NOT stop propagation if the circuit + // was already locked -- for scrolling and such. + + event.preventDefault() + event.stopPropagation() + + + if( undoEl && circuit.history.undo$() ){ + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + } + if( redoEl && circuit.history.redo$() ){ + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + } + if( controlEl ) Editor.createControl( circuitEl ) + if( swapEl ) Editor.createSwap( circuitEl ) + if( addMomentEl ) console.log( '→ Add moment' ) + if( addRegisterEl ) console.log( '→ Add register' ) + + + // We’re done dealing with external buttons. + // So if we can’t find a circuit CELL + // then there’s nothing more to do here. + + if( !cellEl ) return + + // Once we know what cell we’ve pressed on + // we can get the momentIndex and registerIndex + // from its pre-defined attributes. + // NOTE that we are getting CSS grid column and row + // from our own conversion function and NOT from + // asking its styles. Why? Because browsers convert + // grid commands to a shorthand less easily parsable + // and therefore makes our code and reasoning + // more prone to quirks / errors. Trust me! + + const + momentIndex = +cellEl.getAttribute( 'moment-index' ), + registerIndex = +cellEl.getAttribute( 'register-index' ), + columnIndex = Editor.momentIndexToGridColumn( momentIndex ), + rowIndex = Editor.registerIndexToGridRow( registerIndex ) + + + // Looks like our circuit is NOT locked + // and we have a valid circuit CELL + // so let’s find everything else we could need. + + const + selectallEl = targetEl.closest( '.Q-circuit-selectall' ), + registersymbolEl = targetEl.closest( '.Q-circuit-register-label' ), + momentsymbolEl = targetEl.closest( '.Q-circuit-moment-label' ), + inputEl = targetEl.closest( '.Q-circuit-input' ), + operationEl = targetEl.closest( '.Q-circuit-operation' ) + + // +++++++++++++++ + // We’ll have to add some input editing capability later... + // Of course you can already do this in code! + // For now though most quantum code assumes all qubits + // begin with a value of zero so this is mostly ok ;) + + if( inputEl ){ + + console.log( '→ Edit input Qubit value at', registerIndex ) + return + } + + + // Let’s inspect a group of items via a CSS query. + // If any of them are NOT “selected” (highlighted) + // then select them all. + // But if ALL of them are already selected + // then UNSELECT them all. + + function toggleSelection( query ){ + + const + operations = Array.from( circuitEl.querySelectorAll( query )), + operationsSelectedLength = operations.reduce( function( sum, element ){ + + sum += +element.classList.contains( 'Q-circuit-cell-selected' ) + return sum + + }, 0 ) + + if( operationsSelectedLength === operations.length ){ + + operations.forEach( function( el ){ + el.classList.remove( 'Q-circuit-cell-selected' ) + }) + } + else { + + operations.forEach( function( el ){ + + el.classList.add( 'Q-circuit-cell-selected' ) + }) + } + Editor.onSelectionChanged( circuitEl ) + } + + + // Clicking on the “selectAll” button + // or any of the Moment symbols / Register symbols + // causes a selection toggle. + // In the future we may want to add + // dragging of entire Moment columns / Register rows + // to splice them out / insert them elsewhere + // when a user clicks and drags them. + + if( selectallEl ){ + + toggleSelection( '.Q-circuit-operation' ) + return + } + if( momentsymbolEl ){ + + toggleSelection( `.Q-circuit-operation[moment-index="${ momentIndex }"]` ) + return + } + if( registersymbolEl ){ + + toggleSelection( `.Q-circuit-operation[register-index="${ registerIndex }"]` ) + return + } + + + // Right here we can made a big decision: + // If you’re not pressing on an operation + // then GO HOME. + + if( !operationEl ) return + // If we've doubleclicked on an operation and the operation has parameters, we should be able + // to edit those parameters regardless of whether or not the circuit is locked. + if( event.detail == 2) { + const operation = Gate.findBySymbol(operationEl.getAttribute( 'gate-symbol' )) + if( operation.has_parameters ) { + Editor.onDoubleclick( event, operationEl ) + return + } + } + + // Ok now we know we are dealing with an operation. + // This preserved selection state information + // will be useful for when onPointerRelease is fired. + + if( operationEl.classList.contains( 'Q-circuit-cell-selected' )){ + operationEl.wasSelected = true + } + else operationEl.wasSelected = false + + + // And now we can proceed knowing that + // we need to select this operation + // and possibly drag it + // as well as any other selected operations. + + operationEl.classList.add( 'Q-circuit-cell-selected' ) + const selectedOperations = Array.from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) + dragEl.circuitEl = circuitEl + dragEl.originEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) + + + // These are the default values; + // will be used if we’re only dragging one operation around. + // But if dragging more than one operation + // and we’re dragging the clipboard by an operation + // that is NOT in the upper-left corner of the clipboard + // then we need to know what the offset is. + // (Will be calculated below.) + + dragEl.columnIndexOffset = 1 + dragEl.rowIndexOffset = 1 + + + // Now collect all of the selected operations, + // rip them from the circuit board’s foreground layer + // and place them on the clipboard. + + let + columnIndexMin = Infinity, + rowIndexMin = Infinity + + selectedOperations.forEach( function( el ){ + + + // WORTH REPEATING: + // Once we know what cell we’ve pressed on + // we can get the momentIndex and registerIndex + // from its pre-defined attributes. + // NOTE that we are getting CSS grid column and row + // from our own conversion function and NOT from + // asking its styles. Why? Because browsers convert + // grid commands to a shorthand less easily parsable + // and therefore makes our code and reasoning + // more prone to quirks / errors. Trust me! + + const + momentIndex = +el.getAttribute( 'moment-index' ), + registerIndex = +el.getAttribute( 'register-index' ), + columnIndex = Editor.momentIndexToGridColumn( momentIndex ), + rowIndex = Editor.registerIndexToGridRow( registerIndex ) + + columnIndexMin = Math.min( columnIndexMin, columnIndex ) + rowIndexMin = Math.min( rowIndexMin, rowIndex ) + el.classList.remove( 'Q-circuit-cell-selected' ) + el.origin = { momentIndex, registerIndex, columnIndex, rowIndex } + dragEl.appendChild( el ) + }) + selectedOperations.forEach( function( el ){ + + const + columnIndexForClipboard = 1 + el.origin.columnIndex - columnIndexMin, + rowIndexForClipboard = 1 + el.origin.rowIndex - rowIndexMin + + el.style.gridColumn = columnIndexForClipboard + el.style.gridRow = rowIndexForClipboard + + + // If this operation element is the one we grabbed + // (mostly relevant if we’re moving multiple operations at once) + // we need to know what the “offset” so everything can be + // placed correctly relative to this drag-and-dropped item. + + if( el.origin.columnIndex === columnIndex && + el.origin.rowIndex === rowIndex ){ + + dragEl.columnIndexOffset = columnIndexForClipboard + dragEl.rowIndexOffset = rowIndexForClipboard + } + }) + + + // We need an XY offset that describes the difference + // between the mouse / finger press position + // and the clipboard’s intended upper-left position. + // To do that we need to know the press position (obviously!), + // the upper-left bounds of the circuit board’s foreground, + // and the intended upper-left bound of clipboard. + + const + boardEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ), + bounds = boardEl.getBoundingClientRect(), + minX = Editor.gridToPoint( columnIndexMin ), + minY = Editor.gridToPoint( rowIndexMin ) + + dragEl.offsetX = bounds.left + minX - x + dragEl.offsetY = bounds.top + minY - y + dragEl.momentIndex = momentIndex + dragEl.registerIndex = registerIndex + } + else if( paletteEl ){ + const operationEl = targetEl.closest( '.Q-circuit-operation' ) + + if( !operationEl ) return + + const + bounds = operationEl.getBoundingClientRect(), + { x, y } = Editor.getInteractionCoordinates( event ) + + dragEl.appendChild( operationEl.cloneNode( true )) + dragEl.originEl = paletteEl + dragEl.offsetX = bounds.left - x + dragEl.offsetY = bounds.top - y + } + else if( parameterEl ){ + const exitEl = targetEl.closest( '.Q-parameter-box-exit' ) + if( !exitEl ) return + parameterEl.style.display = 'none' + const foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) + operationEl = foregroundEl.querySelector( `[moment-index="${ parameterEl.getAttribute( 'operation-moment-index' )}"]` + + `[register-index="${ parameterEl.getAttribute( 'operation-register-index' )}"]` ) + parameters = {} + operationSkeleton = Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' )) + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = operationEl.getAttribute( key ) ? operationEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + //upon exiting, we should update the circuit!!! + circuitEl.circuit.set$( + operationEl.getAttribute( 'gate-symbol' ), + +operationEl.getAttribute( 'moment-index' ), + operationEl.getAttribute( 'register-indices' ) ? operationEl.getAttribute( 'register-indices' ).split(',').map( i => +i ) : + [ +operationEl.getAttribute( 'register-index' )], + parameters + ) + //on exiting the parameter-input-box, we should update the circuit!! + parameterEl.innerHTML = "" + return + } + dragEl.timestamp = Date.now() + + + // Append the clipboard to the document, + // establish a global reference to it, + // and trigger a draw of it in the correct spot. + + document.body.appendChild( dragEl ) + Editor.dragEl = dragEl + Editor.onPointerMove( event ) +} + + + + + + + ///////////////////////// + // // + // Pointer RELEASE // + // // +///////////////////////// + + +Editor.onPointerRelease = function( event ){ + + + // If there’s no dragEl then bail immediately. + if( Editor.dragEl === null ) return + // Looks like we’re moving forward with this plan, + // so we’ll take control of the input now. + event.preventDefault() + event.stopPropagation() + + + // We can’t get the drop target from the event. + // Think about it: What was under the mouse / finger + // when this drop event was fired? THE CLIPBOARD ! + // So instead we need to peek at what elements are + // under the mouse / finger, skipping element [0] + // because that will be the clipboard. + + const { x, y } = Editor.getInteractionCoordinates( event ) + const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container") + if( boardContainerAll.length === 0 ) return + let boardContainerEl = Array.from(boardContainerAll).find((element) => { + let rect = element.getBoundingClientRect() + let clientX = rect.left + let clientY = rect.top + let height = element.offsetHeight + let width = element.offsetWidth + return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height ) + }) + returnToOrigin = function(){ + + + // We can only do a “true” return to origin + // if we were dragging from a circuit. + // If we were dragging from a palette + // we can just stop dragging. + + if( Editor.dragEl.circuitEl ){ + + Array.from( Editor.dragEl.children ).forEach( function( el ){ + + Editor.dragEl.originEl.appendChild( el ) + el.style.gridColumn = el.origin.columnIndex + el.style.gridRow = el.origin.rowIndex + if( el.wasSelected === true ) el.classList.remove( 'Q-circuit-cell-selected' ) + else el.classList.add( 'Q-circuit-cell-selected' ) + }) + Editor.onSelectionChanged( Editor.dragEl.circuitEl ) + } + document.body.removeChild( Editor.dragEl ) + Editor.dragEl = null + } + + + // If we have not dragged on to a circuit board + // then we’re throwing away this operation. + + if( !boardContainerEl ){ + + if( Editor.dragEl.circuitEl ){ + + const + originCircuitEl = Editor.dragEl.circuitEl + originCircuit = originCircuitEl.circuit + + originCircuit.history.createEntry$() + Array + .from( Editor.dragEl.children ) + .forEach( function( child ){ + + originCircuit.clear$( + + child.origin.momentIndex, + child.origin.registerIndex + ) + }) + Editor.onSelectionChanged( originCircuitEl ) + Editor.onCircuitChanged( originCircuitEl ) + } + + + // TIME TO DIE. + // Let’s keep a private reference to + // the current clipboard. + + let clipboardToDestroy = Editor.dragEl + + + // Now we can remove our dragging reference. + + Editor.dragEl = null + + + // Add our CSS animation routine + // which will run for 1 second. + // If we were SUPER AWESOME + // we would have also calculated drag momentum + // and we’d let this glide away! + + clipboardToDestroy.classList.add( 'Q-circuit-clipboard-destroy' ) + + + // And around the time that animation is completing + // we can go ahead and remove our clipboard from the DOM + // and kill the reference. + + setTimeout( function(){ + + document.body.removeChild( clipboardToDestroy ) + clipboardToDestroy = null + + }, 500 ) + + + // No more to do here. Goodbye. + + return + } + + + // If we couldn’t determine a circuitEl + // from the drop target, + // or if there is a target circuit but it’s locked, + // then we need to return these dragged items + // to their original circuit. + + const circuitEl = boardContainerEl.closest( '.Q-circuit' ) + if( circuitEl.classList.contains( 'Q-circuit-locked' )){ + + returnToOrigin() + return + } + + + // Time to get serious. + // Where exactly are we dropping on to this circuit?? + + const + circuit = circuitEl.circuit, + bounds = boardContainerEl.getBoundingClientRect(), + droppedAtX = x - bounds.left + boardContainerEl.scrollLeft, + droppedAtY = y - bounds.top + boardContainerEl.scrollTop, + droppedAtMomentIndex = Editor.gridColumnToMomentIndex( + + Editor.pointToGrid( droppedAtX ) + ), + droppedAtRegisterIndex = Editor.gridRowToRegisterIndex( + + Editor.pointToGrid( droppedAtY ) + ), + foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) + + + // If this is a self-drop + // we can also just return to origin and bail. + + if( Editor.dragEl.circuitEl === circuitEl && + Editor.dragEl.momentIndex === droppedAtMomentIndex && + Editor.dragEl.registerIndex === droppedAtRegisterIndex ){ + + returnToOrigin() + return + } + + + // Is this a valid drop target within this circuit? + + if( + droppedAtMomentIndex < 1 || + droppedAtMomentIndex > circuit.timewidth || + droppedAtRegisterIndex < 1 || + droppedAtRegisterIndex > circuit.bandwidth + ){ + returnToOrigin() + return + } + + + // Finally! Work is about to be done! + // All we need to do is tell the circuit itself + // where we need to place these dragged items. + // It will do all the validation for us + // and then fire events that will place new elements + // where they need to go! + + const + draggedOperations = Array.from( Editor.dragEl.children ), + draggedMomentDelta = droppedAtMomentIndex - Editor.dragEl.momentIndex, + draggedRegisterDelta = droppedAtRegisterIndex - Editor.dragEl.registerIndex, + setCommands = [] + + + // Whatever the next action is that we perform on the circuit, + // this was user-initiated via the graphic user interface (GUI). + + circuit.history.createEntry$() + + + // Now let’s work our way through each of the dragged operations. + // If some of these are components of a multi-register operation + // the sibling components will get spliced out of the array + // to avoid processing any specific operation more than once. + + draggedOperations.forEach( function( childEl, i ){ + + let + momentIndexTarget = droppedAtMomentIndex, + registerIndexTarget = droppedAtRegisterIndex + + if( Editor.dragEl.circuitEl ){ + + momentIndexTarget += childEl.origin.momentIndex - Editor.dragEl.momentIndex + registerIndexTarget += childEl.origin.registerIndex - Editor.dragEl.registerIndex + } + + + // Is this a multi-register operation? + // If so, this is also a from-circuit drop + // rather than a from-palette drop. + + const registerIndicesString = childEl.getAttribute( 'register-indices' ) + if( registerIndicesString ){ + + // What are ALL of the registerIndices + // associated with this multi-register operation? + // (We may use them later as a checklist.) + + const + registerIndices = registerIndicesString + .split( ',' ) + .map( function( str ){ return +str }), + + + // Lets look for ALL of the sibling components of this operation. + // Later we’ll check and see if the length of this array + // is equal to the total number of components for this operation. + // If they’re equal then we know we’re dragging the WHOLE thing. + // Otherwise we need to determine if it needs to break apart + // and if so, what that nature of that break might be. + + foundComponents = Array.from( + + Editor.dragEl.querySelectorAll( + + `[moment-index="${ childEl.origin.momentIndex }"]`+ + `[register-indices="${ registerIndicesString }"]` + ) + ) + .sort( function( a, b ){ + + const + aRegisterIndicesIndex = +a.getAttribute( 'register-indices-index' ), + bRegisterIndicesIndex = +b.getAttribute( 'register-indices-index' ) + + return aRegisterIndicesIndex - bRegisterIndicesIndex + }), + allComponents = Array.from( Editor.dragEl.circuitEl.querySelectorAll( + + `[moment-index="${ childEl.origin.momentIndex }"]`+ + `[register-indices="${ registerIndicesString }"]` + )), + remainingComponents = allComponents.filter( function( componentEl, i ){ + + return !foundComponents.includes( componentEl ) + }), + + + // We can’t pick the gate symbol + // off the 0th gate in the register indices array + // because that will be an identity / control / null gate. + // We need to look at slot 1. + + component1 = Editor.dragEl.querySelector( + + `[moment-index="${ childEl.origin.momentIndex }"]`+ + `[register-index="${ registerIndices[ 1 ] }"]` + ), + gatesymbol = component1 ? + component1.getAttribute( 'gate-symbol' ) : + childEl.getAttribute( 'gate-symbol' ) + + + // We needed to grab the above gatesymbol information + // before we sent any clear$ commands + // which would in turn delete those componentEls. + // We’ve just completed that, + // so now’s the time to send a clear$ command + // before we do any set$ commands. + + draggedOperations.forEach( function( childEl ){ + + Editor.dragEl.circuitEl.circuit.clear$( + + childEl.origin.momentIndex, + childEl.origin.registerIndex + ) + }) + + + // FULL MULTI-REGISTER DRAG (TO ANY POSITION ON ANY CIRCUIT). + // If we are dragging all of the components + // of a multi-register operation + // then we are good to go. + + if( registerIndices.length === foundComponents.length ){ + + const operationSkeleton = Gate.findBySymbol( gatesymbol ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + //circuit.set$( + setCommands.push([ + + gatesymbol, + momentIndexTarget, + + + // We need to remap EACH register index here + // according to the drop position. + // Let’s let set$ do all the validation on this. + + registerIndices.map( function( registerIndex ){ + + const siblingDelta = registerIndex - childEl.origin.registerIndex + registerIndexTarget = droppedAtRegisterIndex + if( Editor.dragEl.circuitEl ){ + + registerIndexTarget += childEl.origin.registerIndex - Editor.dragEl.registerIndex + siblingDelta + } + return registerIndexTarget + }), + parameters + // ) + ]) + } + + + // IN-MOMENT (IN-CIRCUIT) PARTIAL MULTI-REGISTER DRAG. + // It appears we are NOT dragging all components + // of a multi-register operation. + // But if we’re dragging within the same circuit + // and we’re staying within the same moment index + // that might be ok! + + else if( Editor.dragEl.circuitEl === circuitEl && + momentIndexTarget === childEl.origin.momentIndex ){ + + + // We must ensure that only one component + // can sit at each register index. + // This copies registerIndices, + // but inverts the key : property relationship. + const registerMap = registerIndices + .reduce( function( registerMap, registerIndex, r ){ + + registerMap[ registerIndex ] = r + return registerMap + + }, {} ) + + + // First, we must remove each dragged component + // from the register it was sitting at. + + foundComponents.forEach( function( component ){ + + const componentRegisterIndex = +component.getAttribute( 'register-index' ) + + + // Remove this component from + // where this component used to be. + + delete registerMap[ componentRegisterIndex ] + }) + + + // Now we can seat it at its new position. + // Note: This may OVERWRITE one of its siblings! + // And that’s ok. + foundComponents.forEach( function( component ){ + + const + componentRegisterIndex = +component.getAttribute( 'register-index' ), + registerGrabDelta = componentRegisterIndex - Editor.dragEl.registerIndex + + + // Now put it where it wants to go, + // possibly overwriting a sibling component! + //ltnln: if a multiqubit operation component that requires a sibling, overwrites its sibling, both/all components should be destroyed + registerMap[ + + componentRegisterIndex + draggedRegisterDelta + + ] = +component.getAttribute( 'register-indices-index' ) + }) + + + // Now let’s flip that registerMap + // back into an array of register indices. + + const fixedRegistersIndices = Object.entries( registerMap ) + .reduce( function( registers, entry, i ){ + + registers[ +entry[ 1 ]] = +entry[ 0 ] + return registers + + }, [] ) + + + // This will remove any blank entries in the array + // ie. if a dragged sibling overwrote a seated one. + + .filter( function( entry ){ + return mathf.isUsefulInteger( entry ) + }) + + const operationSkeleton = Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + // Finally, we’re ready to set. + // circuit.set$( + setCommands.push([ + //ltnln: if a component of an operation that requires a sibling pair overwrites its sibling, we want it removed entirely. + fixedRegistersIndices.length < 2 && Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? + Gate.NOOP : + childEl.getAttribute( 'gate-symbol' ), + momentIndexTarget, + fixedRegistersIndices, + parameters + // ) + ]) + } + else { + remainingComponents.forEach( function( componentEl, i ){ + //circuit.set$( + const operationSkeleton = Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + setCommands.push([ + + +componentEl.getAttribute( 'register-indices-index' ) && !Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? + gatesymbol : + Gate.NOOP, + +componentEl.getAttribute( 'moment-index' ), + +componentEl.getAttribute( 'register-index' ), + parameters + // ) + ]) + }) + + + // Finally, let’s separate and update + // all the components that were part of the drag. + + foundComponents.forEach( function( componentEl ){ + const operationSkeleton = Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + setCommands.push([ + //ltnln: temporary fix: certain multiqubit operations should only be represented in pairs of registers. If one is removed (i.e. a single component of the pair) + //then the entire operation should be removed. + +componentEl.getAttribute( 'register-indices-index' ) && !Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? + componentEl.getAttribute( 'gate-symbol' ) : + Gate.NOOP, + +componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta, + +componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta, + parameters + // ) + ]) + }) + } + + + // We’ve just completed the movement + // of a multi-register operation. + // But all of the sibling components + // will also trigger this process + // unless we remove them + // from the draggd operations array. + + let j = i + 1 + while( j < draggedOperations.length ){ + + const possibleSibling = draggedOperations[ j ] + if( possibleSibling.getAttribute( 'gate-symbol' ) === gatesymbol && + possibleSibling.getAttribute( 'register-indices' ) === registerIndicesString ){ + + draggedOperations.splice( j, 1 ) + } + else j ++ + } + } + + + // This is just a single-register operation. + // How simple this looks + // compared to all the gibberish above. + + else { + + + // First, if this operation comes from a circuit + // (and not a circuit palette) + // make sure the old positions are cleared away. + + if( Editor.dragEl.circuitEl ){ + + draggedOperations.forEach( function( childEl ){ + + Editor.dragEl.circuitEl.circuit.clear$( + + childEl.origin.momentIndex, + childEl.origin.registerIndex + ) + }) + } + + + // And now set$ the operation + // in its new home. + + // circuit.set$( + let registerIndices = [ registerIndexTarget ] + //ltnln: By default, multiqubit gates appear in pairs on the circuit rather than + // requiring the user to have to pair them like with Swap/CNot. + const operationSkeleton = Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' )) + if(operationSkeleton.is_multi_qubit ) { + registerIndices.push( registerIndexTarget == circuit.bandwidth ? registerIndexTarget - 1 : registerIndexTarget + 1) + } + let parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + setCommands.push([ + childEl.getAttribute( 'gate-symbol' ), + momentIndexTarget, + registerIndices, + parameters + // ) + ]) + } + }) + + + // DO IT DO IT DO IT + + setCommands.forEach( function( setCommand ){ + + circuit.set$.apply( circuit, setCommand ) + }) + + + // Are we capable of making controls? Swaps? + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + + + // If the original circuit and destination circuit + // are not the same thing + // then we need to also eval the original circuit. + + if( Editor.dragEl.circuitEl && + Editor.dragEl.circuitEl !== circuitEl ){ + + const originCircuitEl = Editor.dragEl.circuitEl + Editor.onSelectionChanged( originCircuitEl ) + Editor.onCircuitChanged( originCircuitEl ) + } + + + // We’re finally done here. + // Clean up and go home. + // It’s been a long journey. + // I love you all. + + document.body.removeChild( Editor.dragEl ) + Editor.dragEl = null +} + + + ///////////////////////// + // // + // Pointer DOUBLECLICK // + // // +///////////////////////// +//ltnln: my trying out an idea for parameterized gates... +Editor.onDoubleclick = function( event, operationEl ) { + const operation = Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' )) + const { x, y } = Editor.getInteractionCoordinates( event ) + const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container") + if( boardContainerAll.length === 0 ) return + let boardContainerEl = Array.from(boardContainerAll).find((element) => { + let rect = element.getBoundingClientRect() + let clientX = rect.left + let clientY = rect.top + let height = element.offsetHeight + let width = element.offsetWidth + return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height ) + }) + if( !boardContainerEl ) return; + const parameterEl = boardContainerEl.querySelector('.Q-parameters-box') + const exit = Editor.createNewElement( 'button', parameterEl, 'Q-parameter-box-exit') + exit.appendChild(document.createTextNode( '⬅' )) + parameterEl.setAttribute( "operation-moment-index", operationEl.getAttribute( 'moment-index' )) + parameterEl.setAttribute( "operation-register-index", operationEl.getAttribute( 'register-index' )) + //here we generate queries for each parameter that the gate operation takes! + const parameters = Object.keys(operation.parameters) + parameters.forEach( element => { + if( operation.parameters && operation.parameters[element] !== null ) { + const input_fields = document.createElement( 'div' ) + parameterEl.appendChild( input_fields ) + input_fields.classList.add( 'Q-parameter-box-input-container' ) + + const label = Editor.createNewElement( "span", input_fields, 'Q-parameter-input-label' ) + label.appendChild(document.createTextNode( element )) + + const textbox = Editor.createNewElement( "input", input_fields, 'Q-parameter-box-input') + textbox.setAttribute( 'type', 'text' ) + textbox.setAttribute( 'placeholder', element ) + textbox.setAttribute( 'value', operationEl.getAttribute(element) ? operationEl.getAttribute(element) : operation.parameters[element] ) + //set textbox to update the operation instance (cellEl)'s parameters on value change + textbox.addEventListener( "change", () => { + let parameterValue = +textbox.value; + let oldValue = operationEl.getAttribute( element ) + if( !oldValue ) oldValue = operation.parameters[ element ] + if( parameterValue === null || parameterValue === Infinity ) textbox.value = oldValue.toString() + else { + operationEl.setAttribute( element, parameterValue ) + textbox.value = parameterValue + } + }) + + + } + }) + parameterEl.classList.toggle('overlay') + parameterEl.style.display = 'block' +} + + + + /////////////////// + // // + // Listeners // + // // +/////////////////// + + +// These listeners must be appliedm +// to the entire WINDOW (and not just document.body!) + +window.addEventListener( 'mousemove', Editor.onPointerMove ) +window.addEventListener( 'touchmove', Editor.onPointerMove ) +window.addEventListener( 'mouseup', Editor.onPointerRelease ) +window.addEventListener( 'touchend', Editor.onPointerRelease ) +module.exports = {Editor} \ No newline at end of file diff --git a/packages/quantum-js-vis/Q.css b/packages/quantum-js-vis/Q.css new file mode 100644 index 0000000..cb10c3c --- /dev/null +++ b/packages/quantum-js-vis/Q.css @@ -0,0 +1,474 @@ +/* + + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +*/ +@charset "utf-8"; + + + + +/* + + This file is in the process of being separated + in to “essential global Q.js styles” which will + remain here in Q.css, and “documentation-specific” + styles which will be removed from here and placed + within the /other/documentation.css file instead. + + The goal is for a developer to be able to place + this Q.css file into their own web app with little + to no interference with their app. All variables + and styles here will ultimately be prefaced with + a capital Q, eg. --Q-color-base, or .Q-text-input + and so on. + + +*/ + + + + +svg, :root { + + + + /**************/ + /* */ + /* Colors */ + /* */ + /**************/ + + + /* Base color (blue) */ + + --Q-color-base-hue: 210; + --Q-color-base-saturation: 85%; + --Q-color-base-lightness: 40%; + + + /* Red */ + + --Q-color-red-hue: calc( var( --Q-color-base-hue ) + 180 - 30 ); + --Q-color-red-saturation: 85%; + --Q-color-red-lightness: 45%; + --Q-color-red: hsl( + + var( --Q-color-red-hue ), + var( --Q-color-red-saturation ), + var( --Q-color-red-lightness ) + ); + + + /* Orange */ + + --Q-color-orange-hue: calc( var( --Q-color-base-hue ) + 180 - 15 ); + --Q-color-orange-saturation: 85%; + --Q-color-orange-lightness: 50%; + --Q-color-orange: hsl( + + var( --Q-color-orange-hue ), + var( --Q-color-orange-saturation ), + var( --Q-color-orange-lightness ) + ); + + + /* Yellow */ + + --Q-color-yellow-hue: calc( var( --Q-color-base-hue ) + 180 + 15 ); + --Q-color-yellow-saturation: 90%; + --Q-color-yellow-lightness: 50%; + --Q-color-yellow: hsl( + + var( --Q-color-yellow-hue ), + var( --Q-color-yellow-saturation ), + var( --Q-color-yellow-lightness ) + ); + + + /* Green */ + + --Q-color-green-hue: calc( var( --Q-color-base-hue ) + 180 + 60 ); + --Q-color-green-saturation: 80%; + --Q-color-green-lightness: 35%; + --Q-color-green: hsl( + + var( --Q-color-green-hue ), + var( --Q-color-green-saturation ), + var( --Q-color-green-lightness ) + ); + + + /* Blue */ + + --Q-color-blue-hue: var( --Q-color-base-hue ); + --Q-color-blue-saturation: var( --Q-color-base-saturation ); + --Q-color-blue-lightness: var( --Q-color-base-lightness ); + --Q-color-blue: hsl( + + var( --Q-color-blue-hue ), + var( --Q-color-blue-saturation ), + var( --Q-color-blue-lightness ) + ); + + + /* Grayscale */ + + --Q-color-white: #FFFFFF; + --Q-color-chalk: #F9F9F9; + --Q-color-newsprint: #F3F3F3; + --Q-color-titanium: #CCCCCC; + --Q-color-slate: #777777; + --Q-color-charcoal: #333333; + --Q-color-black: #000000; + + + /* Background */ + + --Q-color-background-hue: var( --Q-color-base-hue ); + --Q-color-background-saturation: 15%; + --Q-color-background-lightness: 98%; + --Q-color-background: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + var( --Q-color-background-lightness ) + ); + /*--Q-color-background: white;*/ + + + /* Misc */ + + --Q-text-color: hsl( + + var( --Q-color-base-hue ), + 5%, + 35% + ); + --Q-text-code-comment-color: rgba( 0, 0, 0, 0.4 ); + --Q-text-code-output-color: rgba( 0, 0, 0, 1 ); + + --Q-selection-color: var( --Q-color-black ); + --Q-selection-background-color: var( --Q-color-yellow ); + + --Q-hyperlink-internal-color: var( --Q-color-blue ); + --Q-hyperlink-external-color: var( --Q-text-color ); + + --Q-background-callout-color: var( --Q-color-white ); + + --Q-svg-fill-color: var( --Q-text-color ); + + + + + /* Fonts */ + + --Q-font-family-serif: 'Source Serif Pro', 'Roboto Slab', 'Georgia', serif; + --Q-font-family-sans: 'SF Pro Text', system-ui, -apple-system, 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; + --Q-font-family-mono: 'Roboto Mono', 'Source Code Pro', 'Menlo', 'Courier New', monospace; + --Q-font-family-symbols: 'Georgia', serif; +} + + + + + /*******************/ + /* */ + /* Interactive */ + /* */ +/*******************/ + + +.Q-input, +.Q-circuit-text-input { + + margin: 1.5rem 0 0 0 !important; + outline: none !important; + border: none !important; + border-radius: 1.2rem !important; + box-shadow: + 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.15 ) inset, + -0.2rem -0.2rem 0.2rem rgba( 255, 255, 255, 1 ) inset; + + background: linear-gradient( + + 0.375turn, + rgba( 255, 255, 255, 1.0 ), + rgba( 255, 255, 255, 0.2 ) + ) !important; +} +.Q-input, +.Q-circuit-text-input { + + padding: 1.5rem !important; + color: #555 !important; + font-size: 0.9rem !important; + line-height: 1.2rem !important; +} + + + +.Q-circuit-text-input { + + /*min-width: 18rem;*/ + width: 100%; + min-height: 8rem; + /*margin: 1rem 0 2rem 0;*/ + margin: 1rem 0 0 0; + border: 1px solid var( --Q-color-blue ); + border-radius: 0.5rem; + background-color: var( --Q-color-chalk ); + padding: 1rem 0 0 2rem; + color: var( --Q-color-blue ); + font-family: var( --Q-font-family-mono ); + font-size: 1.0rem; + line-height: 1.2rem; + white-space: pre; + word-wrap: normal;/* OMFG, iOS you make me sad. */ +} + + + + + + +.Q-button { + + position: relative; + text-align: right; + margin: 0.5rem 1rem 0 0; + border-radius: 3rem; + box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ), + 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ); + height: 3rem; + background: + var( --Q-color-blue ) + linear-gradient( + + 0.4turn, + rgba( 255, 255, 255, 0.2 ), + rgba( 0, 0, 0, 0.08 ) + ); + padding: 0.8rem 1.8rem; + color: var( --Q-color-white ); + font-family: var( --Q-font-family-sans ); + font-size: 1rem; + line-height: 1rem; + font-weight: 500; + letter-spacing: 0; + text-shadow: -1px -1px 0 rgba( 0, 0, 0, 0.1 ); + cursor: pointer; +} +.Q-button:hover { + + background: + hsl( + + var( --Q-color-blue-hue ), + var( --Q-color-blue-saturation ), + calc( var( --Q-color-blue-lightness ) * 1.2 ) + ) + linear-gradient( + + 0.4turn, + rgba( 255, 255, 255, 0.2 ), + rgba( 0, 0, 0, 0.08 ) + ); +} +.Q-button:focus { + + margin-top: 0.7rem; + margin-bottom: -0.2rem; + margin-right: 0.9rem; + outline: none; + box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ) inset, + 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ) inset; + background: + var( --Q-color-blue ) + linear-gradient( + + 0.4turn, + rgba( 0, 0, 0, 0.08 ), + rgba( 255, 255, 255, 0.2 ) + ); +} +.Q-button[disabled] { + + box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ), + 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ); + background: + var( --Q-color-background ) + linear-gradient( + + 0.45turn, + rgba( 255, 255, 255, 0.1 ), + rgba( 0, 0, 0, 0.05 ) + ); + color: rgba( 0, 0, 0, 0.3 ); + text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 ); + cursor: default; +} + + + + + + +/* + + The below still need to be prefaced with “Q-” + and for the HTML pages to be updated accordingly. + +*/ + + + + + + + /*************/ + /* */ + /* Maths */ + /* */ +/*************/ + + +.maths { + + max-width: 100%; + overflow-x: auto; + font-family: var( --Q-font-family-sans ); + /*letter-spacing: 0.03em;*/ + word-spacing: 0.2em; +} +dd .maths { + + margin-top: 0; + margin-left: 0; +} + + + + +.symbol { + + font-size: 1.1em; + padding: 0 0.1em; + font-family: var( --Q-font-family-symbols ); + font-style: italic; + font-weight: 900; + letter-spacing: 0.05em; +} + + + + +.division { + + display: inline-block; + vertical-align: middle; + margin: 10px; +} +.division td { + + padding: 5px; +} +.dividend { + + border-bottom: 1px solid #CCC; + text-align: center; +} +.divisor { + + text-align: center; +} + + + + +.matrix { + + display: inline-block; + vertical-align: middle; + position: relative; + align: middle; + margin: 1em 0.5em; + padding: 1em; + /*font-family: var( --Q-font-family-mono );*/ + font-weight: 300; + line-height: 1em; + /*text-align: right;*/ + text-align: center; +} +.matrix td { + + padding: 0.25em 0.5em; +} +.matrix-bracket-left, .matrix-bracket-right { + + position: absolute; + top: 0; + width: 0.5em; + height: 100%; + border: 1px solid #CCC; +} +.matrix-bracket-left { + + left: 0; + border-right: none; +} +.matrix-bracket-right { + + right: 0; + border-left: none; +} +/*.matrix.qubit tr:first-child td { + + color: #BBB; +}*/ + + + +.Q-state-vector, +.complex-vector { + + font-family: var( --Q-font-family-mono ); +} +.Q-state-vector.bra::before, +.complex-vector.bra::before { + + content: '⟨'; + color: #BBB; +} +.Q-state-vector.bra::after, +.complex-vector.bra::after { + + content: '|'; + color: #BBB; +} +.Q-state-vector.ket::before, +.complex-vector.ket::before { + + content: '|'; + color: #BBB; +} +.Q-state-vector.ket::after, +.complex-vector.ket::after { + + content: '⟩'; + color: #BBB; +} +.Q-state-vector.bra + .Q-state-vector.ket::before, +.complex-vector.bra + .complex-vector.ket::before { + + content: ''; +} + + + + + + diff --git a/packages/quantum-js-vis/index.js b/packages/quantum-js-vis/index.js new file mode 100644 index 0000000..6d200e6 --- /dev/null +++ b/packages/quantum-js-vis/index.js @@ -0,0 +1,89 @@ +const {Editor} = require('./Q-Circuit-Editor'); +const {circuit} = require('quantum-js-util'); +const {BlochSphere} = require('./Q-BlochSphere'); +console.log("Welcome to Q.js! The GUI experience!\n"); + +braket = function(){ + + + // Create the HTML bits we need, + // contain them all together, + // and output them to Jupyter. + if( arguments.length === 0 || arguments.length > 3 ) return; + const element = arguments[0]; + const args = (Array.from(arguments)).slice(1); + let circuit + if(args.length === 0) { + circuit = new Q( 4, 8 ) + } + else if(args.length === 1) { + circuit = new Q( args[0] ) + } + else { + circuit = new Q( args[0], args[1] ) + } + container = document.createElement( 'div' ) + container.appendChild( Editor.createPalette() ) + container.appendChild( circuit.toDom() ) + element.html( container ) + document.querySelectorAll('.Q-circuit-palette').forEach( paletteEl => paletteEl.style.width = "50%") + + // We’re going to take this SLOOOOOOOOWLY + // because there are many potential things to debug. + + const thisCell = Jupyter.notebook.get_selected_cell() + // console.log( 'thisCell', thisCell ) + + const thisCellIndex = Jupyter.notebook.get_cells().indexOf( thisCell ) + // console.log( 'thisCellIndex', thisCellIndex ) + + const nextCell = Jupyter.notebook.insert_cell_below( 'code', thisCellIndex - 1 ) + const nextNextCell = Jupyter.notebook.insert_cell_below( 'markdown', Jupyter.notebook.get_cells().indexOf( thisCell ) - 1 ) + // console.log( 'nextCell', nextCell ) + + nextCell.set_text( circuit.toAmazonBraket() ) + nextNextCell.set_text( circuit.report$() ) + + + + + + + window.addEventListener( 'Q gui altered circuit', function( event ){ + + // updatePlaygroundFromDom( event.detail.circuit ) + if( event.detail.circuit === circuit ){ + + console.log( 'Updating circuit from GUI', circuit ) + circuit.evaluate$() + nextCell.set_text( circuit.toAmazonBraket() ) + + } + }) + + window.addEventListener( 'Circuit.evaluate completed', function( event ) { + if( event.detail.circuit === circuit ) { + nextNextCell.set_text( circuit.report$() ) + } + }) + + + + // nextCell.render() + + // console.log( 'thisCell', thisCell ) + // console.log( 'nextCell', nextCell ) + // console.log( 'thisCellIndex', thisCellIndex ) + + // code = Jupyter.notebook.insert_cell_{0}('code'); + // code.set_text(atob("{1}")) + + // var t_cell = Jupyter.notebook.get_selected_cell() + // t_cell.set_text(' \\n{}') + // var t_index = Jupyter.notebook.get_cells().indexOf(t_cell) + // Jupyter.notebook.to_markdown(t_index) + // Jupyter.notebook.get_cell(t_index).render() +} + + +module.exports = {Editor, BlochSphere, braket}; \ No newline at end of file diff --git a/packages/quantum-js-vis/package-lock.json b/packages/quantum-js-vis/package-lock.json new file mode 100644 index 0000000..95e01ab --- /dev/null +++ b/packages/quantum-js-vis/package-lock.json @@ -0,0 +1,35 @@ +{ + "name": "quantum-js-vis", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "prettier": "^2.3.2" + } + }, + "node_modules/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + } + }, + "dependencies": { + "prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true + } + } +} diff --git a/packages/quantum-js-vis/package.json b/packages/quantum-js-vis/package.json new file mode 100644 index 0000000..e9aea82 --- /dev/null +++ b/packages/quantum-js-vis/package.json @@ -0,0 +1,21 @@ +{ + "name": "quantum-js-vis", + "version": "1.0.0", + "description": "Visualization Components for Q.js", + "main": "index.js", + "scripts": { + "test": "jest", + "prettier": "echo 'I am a prettier vis!'" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "prettier": "^2.3.2", + "window": "^4.2.7" + + }, + "dependencies": { + "requirejs": "^2.3.6" + } +} From 931f168a0abfe931701facc81c7e03add1c7b1ca Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 19:27:29 +0000 Subject: [PATCH 08/22] Adjust help() calls to use logger module --- packages/quantum-js-util/Q-Matrix.js | 2 +- packages/quantum-js-util/Q-Qubit.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/quantum-js-util/Q-Matrix.js b/packages/quantum-js-util/Q-Matrix.js index 6f837c5..544b4d4 100644 --- a/packages/quantum-js-util/Q-Matrix.js +++ b/packages/quantum-js-util/Q-Matrix.js @@ -96,7 +96,7 @@ Matrix = function () { Object.assign(Matrix, { index: 0, help: function () { - return help(this); + return logger.help(this); }, constants: {}, // Only holds references; an easy way to look up what constants exist. createConstant: function (key, value) { diff --git a/packages/quantum-js-util/Q-Qubit.js b/packages/quantum-js-util/Q-Qubit.js index 48c3f1e..99fc7c4 100644 --- a/packages/quantum-js-util/Q-Qubit.js +++ b/packages/quantum-js-util/Q-Qubit.js @@ -95,7 +95,7 @@ Qubit.prototype.constructor = Qubit; Object.assign(Qubit, { index: 0, help: function () { - return help(this); + return logger.help(this); }, constants: {}, createConstant: function (key, value) { From 54dd31f775033108c8add0eeed5af53d87fe8edc Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 19:28:17 +0000 Subject: [PATCH 09/22] Create bundles --- build/bundle.css | 1375 ++++++++ build/bundle.js | 8238 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 9613 insertions(+) create mode 100644 build/bundle.css create mode 100644 build/bundle.js diff --git a/build/bundle.css b/build/bundle.css new file mode 100644 index 0000000..1fb9f31 --- /dev/null +++ b/build/bundle.css @@ -0,0 +1,1375 @@ +/* + + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +*/ +@charset "utf-8"; + + + + +/* + + This file is in the process of being separated + in to “essential global Q.js styles” which will + remain here in Q.css, and “documentation-specific” + styles which will be removed from here and placed + within the /other/documentation.css file instead. + + The goal is for a developer to be able to place + this Q.css file into their own web app with little + to no interference with their app. All variables + and styles here will ultimately be prefaced with + a capital Q, eg. --Q-color-base, or .Q-text-input + and so on. + + +*/ + + + + +svg, :root { + + + + /**************/ + /* */ + /* Colors */ + /* */ + /**************/ + + + /* Base color (blue) */ + + --Q-color-base-hue: 210; + --Q-color-base-saturation: 85%; + --Q-color-base-lightness: 40%; + + + /* Red */ + + --Q-color-red-hue: calc( var( --Q-color-base-hue ) + 180 - 30 ); + --Q-color-red-saturation: 85%; + --Q-color-red-lightness: 45%; + --Q-color-red: hsl( + + var( --Q-color-red-hue ), + var( --Q-color-red-saturation ), + var( --Q-color-red-lightness ) + ); + + + /* Orange */ + + --Q-color-orange-hue: calc( var( --Q-color-base-hue ) + 180 - 15 ); + --Q-color-orange-saturation: 85%; + --Q-color-orange-lightness: 50%; + --Q-color-orange: hsl( + + var( --Q-color-orange-hue ), + var( --Q-color-orange-saturation ), + var( --Q-color-orange-lightness ) + ); + + + /* Yellow */ + + --Q-color-yellow-hue: calc( var( --Q-color-base-hue ) + 180 + 15 ); + --Q-color-yellow-saturation: 90%; + --Q-color-yellow-lightness: 50%; + --Q-color-yellow: hsl( + + var( --Q-color-yellow-hue ), + var( --Q-color-yellow-saturation ), + var( --Q-color-yellow-lightness ) + ); + + + /* Green */ + + --Q-color-green-hue: calc( var( --Q-color-base-hue ) + 180 + 60 ); + --Q-color-green-saturation: 80%; + --Q-color-green-lightness: 35%; + --Q-color-green: hsl( + + var( --Q-color-green-hue ), + var( --Q-color-green-saturation ), + var( --Q-color-green-lightness ) + ); + + + /* Blue */ + + --Q-color-blue-hue: var( --Q-color-base-hue ); + --Q-color-blue-saturation: var( --Q-color-base-saturation ); + --Q-color-blue-lightness: var( --Q-color-base-lightness ); + --Q-color-blue: hsl( + + var( --Q-color-blue-hue ), + var( --Q-color-blue-saturation ), + var( --Q-color-blue-lightness ) + ); + + + /* Grayscale */ + + --Q-color-white: #FFFFFF; + --Q-color-chalk: #F9F9F9; + --Q-color-newsprint: #F3F3F3; + --Q-color-titanium: #CCCCCC; + --Q-color-slate: #777777; + --Q-color-charcoal: #333333; + --Q-color-black: #000000; + + + /* Background */ + + --Q-color-background-hue: var( --Q-color-base-hue ); + --Q-color-background-saturation: 15%; + --Q-color-background-lightness: 98%; + --Q-color-background: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + var( --Q-color-background-lightness ) + ); + /*--Q-color-background: white;*/ + + + /* Misc */ + + --Q-text-color: hsl( + + var( --Q-color-base-hue ), + 5%, + 35% + ); + --Q-text-code-comment-color: rgba( 0, 0, 0, 0.4 ); + --Q-text-code-output-color: rgba( 0, 0, 0, 1 ); + + --Q-selection-color: var( --Q-color-black ); + --Q-selection-background-color: var( --Q-color-yellow ); + + --Q-hyperlink-internal-color: var( --Q-color-blue ); + --Q-hyperlink-external-color: var( --Q-text-color ); + + --Q-background-callout-color: var( --Q-color-white ); + + --Q-svg-fill-color: var( --Q-text-color ); + + + + + /* Fonts */ + + --Q-font-family-serif: 'Source Serif Pro', 'Roboto Slab', 'Georgia', serif; + --Q-font-family-sans: 'SF Pro Text', system-ui, -apple-system, 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; + --Q-font-family-mono: 'Roboto Mono', 'Source Code Pro', 'Menlo', 'Courier New', monospace; + --Q-font-family-symbols: 'Georgia', serif; +} + + + + + /*******************/ + /* */ + /* Interactive */ + /* */ +/*******************/ + + +.Q-input, +.Q-circuit-text-input { + + margin: 1.5rem 0 0 0 !important; + outline: none !important; + border: none !important; + border-radius: 1.2rem !important; + box-shadow: + 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.15 ) inset, + -0.2rem -0.2rem 0.2rem rgba( 255, 255, 255, 1 ) inset; + + background: linear-gradient( + + 0.375turn, + rgba( 255, 255, 255, 1.0 ), + rgba( 255, 255, 255, 0.2 ) + ) !important; +} +.Q-input, +.Q-circuit-text-input { + + padding: 1.5rem !important; + color: #555 !important; + font-size: 0.9rem !important; + line-height: 1.2rem !important; +} + + + +.Q-circuit-text-input { + + /*min-width: 18rem;*/ + width: 100%; + min-height: 8rem; + /*margin: 1rem 0 2rem 0;*/ + margin: 1rem 0 0 0; + border: 1px solid var( --Q-color-blue ); + border-radius: 0.5rem; + background-color: var( --Q-color-chalk ); + padding: 1rem 0 0 2rem; + color: var( --Q-color-blue ); + font-family: var( --Q-font-family-mono ); + font-size: 1.0rem; + line-height: 1.2rem; + white-space: pre; + word-wrap: normal;/* OMFG, iOS you make me sad. */ +} + + + + + + +.Q-button { + + position: relative; + text-align: right; + margin: 0.5rem 1rem 0 0; + border-radius: 3rem; + box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ), + 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ); + height: 3rem; + background: + var( --Q-color-blue ) + linear-gradient( + + 0.4turn, + rgba( 255, 255, 255, 0.2 ), + rgba( 0, 0, 0, 0.08 ) + ); + padding: 0.8rem 1.8rem; + color: var( --Q-color-white ); + font-family: var( --Q-font-family-sans ); + font-size: 1rem; + line-height: 1rem; + font-weight: 500; + letter-spacing: 0; + text-shadow: -1px -1px 0 rgba( 0, 0, 0, 0.1 ); + cursor: pointer; +} +.Q-button:hover { + + background: + hsl( + + var( --Q-color-blue-hue ), + var( --Q-color-blue-saturation ), + calc( var( --Q-color-blue-lightness ) * 1.2 ) + ) + linear-gradient( + + 0.4turn, + rgba( 255, 255, 255, 0.2 ), + rgba( 0, 0, 0, 0.08 ) + ); +} +.Q-button:focus { + + margin-top: 0.7rem; + margin-bottom: -0.2rem; + margin-right: 0.9rem; + outline: none; + box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ) inset, + 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ) inset; + background: + var( --Q-color-blue ) + linear-gradient( + + 0.4turn, + rgba( 0, 0, 0, 0.08 ), + rgba( 255, 255, 255, 0.2 ) + ); +} +.Q-button[disabled] { + + box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 1 ), + 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.3 ); + background: + var( --Q-color-background ) + linear-gradient( + + 0.45turn, + rgba( 255, 255, 255, 0.1 ), + rgba( 0, 0, 0, 0.05 ) + ); + color: rgba( 0, 0, 0, 0.3 ); + text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 ); + cursor: default; +} + + + + + + +/* + + The below still need to be prefaced with “Q-” + and for the HTML pages to be updated accordingly. + +*/ + + + + + + + /*************/ + /* */ + /* Maths */ + /* */ +/*************/ + + +.maths { + + max-width: 100%; + overflow-x: auto; + font-family: var( --Q-font-family-sans ); + /*letter-spacing: 0.03em;*/ + word-spacing: 0.2em; +} +dd .maths { + + margin-top: 0; + margin-left: 0; +} + + + + +.symbol { + + font-size: 1.1em; + padding: 0 0.1em; + font-family: var( --Q-font-family-symbols ); + font-style: italic; + font-weight: 900; + letter-spacing: 0.05em; +} + + + + +.division { + + display: inline-block; + vertical-align: middle; + margin: 10px; +} +.division td { + + padding: 5px; +} +.dividend { + + border-bottom: 1px solid #CCC; + text-align: center; +} +.divisor { + + text-align: center; +} + + + + +.matrix { + + display: inline-block; + vertical-align: middle; + position: relative; + align: middle; + margin: 1em 0.5em; + padding: 1em; + /*font-family: var( --Q-font-family-mono );*/ + font-weight: 300; + line-height: 1em; + /*text-align: right;*/ + text-align: center; +} +.matrix td { + + padding: 0.25em 0.5em; +} +.matrix-bracket-left, .matrix-bracket-right { + + position: absolute; + top: 0; + width: 0.5em; + height: 100%; + border: 1px solid #CCC; +} +.matrix-bracket-left { + + left: 0; + border-right: none; +} +.matrix-bracket-right { + + right: 0; + border-left: none; +} +/*.matrix.qubit tr:first-child td { + + color: #BBB; +}*/ + + + +.Q-state-vector, +.complex-vector { + + font-family: var( --Q-font-family-mono ); +} +.Q-state-vector.bra::before, +.complex-vector.bra::before { + + content: '⟨'; + color: #BBB; +} +.Q-state-vector.bra::after, +.complex-vector.bra::after { + + content: '|'; + color: #BBB; +} +.Q-state-vector.ket::before, +.complex-vector.ket::before { + + content: '|'; + color: #BBB; +} +.Q-state-vector.ket::after, +.complex-vector.ket::after { + + content: '⟩'; + color: #BBB; +} +.Q-state-vector.bra + .Q-state-vector.ket::before, +.complex-vector.bra + .complex-vector.ket::before { + + content: ''; +} + + + + + + + +/* + + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +*/ +@charset "utf-8"; + + + + + + + + + +/* + + Z indices: + + Clipboard =100 + Selected op 10 + Operation 0 + Shadow -10 + Background -20 + + + + + + Circuit + + Menu Moments + ╭───────┬───┬───┬───┬───╮ + │ ≡ ↘ │ 1 │ 2 │ 3 │ + │ Add moment + ├───┬───┼───┼───┼───┼───╯ + R │ 0 │|0⟩│ H │ C0│ X │ - + e ├───┼───┼───┼───┼───┤ + g │ 1 │|0⟩│ I │ C1│ X │ - + s ├───┼───┴───┴───┴───┘ + │ + │ - - - - + ╰───╯ + Add + register + + + Circuit Palette + + ╭───────────────────┬───╮ + │ H X Y Z S T π M … │ @ │ + ╰───────────────────┴───╯ + + + Circuit clipboard + + ┌───────────────┐ + ▟│ ┌───┬───────┐ │ + █│ │ H │ X#0.0 │ │ + █│ ├───┼───────┤ │ + █│ │ I │ X#0.1 │ │ + █│ └───┴───────┘ │ + █└───────────────┘ + ███████████████▛ + + + + ◢◣ + ◢■■■■◣ +◢■■■■■■■■◣ +◥■■■■■■■■◤ + ◥■■■■◤ + ◥◤ + + + ◢■■■■■■◤ + ◢◤ ◢◤ +◢■■■■■■◤ + + + ─────────── + ╲ ╱ ╱ ╱ + ╳ ╱ ╱ + ╱ ╲╱ ╱ + ─────── + + + ─────⦢ + ╱ ╱ +⦣───── + + +*/ + + + + + +.Q-circuit, +.Q-circuit-palette { + + position: relative; + width: 100%; +} +.Q-circuit-palette { + + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + line-height: 0; +} +.Q-circuit-palette > div { + + display: inline-block; + position: relative; + width: 4rem; + height: 4rem; +} + + +.Q-circuit { + + margin: 1rem 0 2rem 0; + /*border-top: 2px solid hsl( 0, 0%, 50% );*/ +} +.Q-parameters-box, +.Q-circuit-board-foreground { + line-height: 3.85rem; + width: auto; +} + + + + + + + /***************/ + /* */ + /* Toolbar */ + /* */ +/***************/ + + +.Q-circuit-toolbar { + + position: relative; + display: block; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + margin-bottom: 0.5rem; + + box-sizing: border-box; + display: grid; + grid-auto-columns: 3.6rem; + grid-auto-rows: 3.0rem; + grid-auto-flow: column; + +} +.Q-circuit-button { + + position: relative; + display: inline-block; + /*margin: 0 0.5rem 0.5rem 0;*/ + width: 3.6rem; + height: 3rem; +/* box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 0.8 ), + 0.1rem 0.1rem 0.1rem rgba( 0, 0, 0, 0.35 );*/ + + border-top: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 100% + ); + border-right: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 90% + ); + border-bottom: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 85% + ); + border-left: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 97% + ); + background: var( --Q-color-background ); +/* background: + var( --Q-color-background ) + linear-gradient( + + 0.4turn, + + rgba( 0, 0, 0, 0.02 ), + rgba( 255, 255, 255, 0.1 ) + );*/ + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 30% + ); + text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 ); + /*border-radius: 0.5rem;*/ + /*border-radius: 100%;*/ + line-height: 2.9rem; + text-align: center; + cursor: pointer; + overflow: hidden; + font-weight: 900; +} +.Q-circuit-toolbar .Q-circuit-button:first-child { + + border-top-left-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; +} +.Q-circuit-toolbar .Q-circuit-button:last-child { + + border-top-right-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} +.Q-circuit-locked .Q-circuit-button, +.Q-circuit-button[Q-disabled] { + + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 85% + ); + cursor: not-allowed; +} +.Q-circuit-locked .Q-circuit-toggle-lock { + + color: inherit; + cursor: pointer; +} + + + + +.Q-circuit-board-container { + + position: relative; + margin: 0 0 2rem 0; + margin: 0; + width: 100%; + max-height: 60vh; + overflow: scroll; +} +.Q-circuit-board { + + position: relative; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +/*.Q-circuit-palette,*/ +.Q-circuit-board-foreground, +.Q-circuit-board-background, +.Q-circuit-clipboard { + + box-sizing: border-box; + display: grid; + grid-auto-rows: 4rem; + grid-auto-columns: 4rem; + grid-auto-flow: column; +} + +.Q-parameters-box { + + position: absolute; + display: none; + z-index: 100; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: whitesmoke; +} + +/*.Q-circuit-palette,*/ +.Q-circuit-board-foreground, +.Q-circuit-board-background { + + position: relative; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.Q-circuit-clipboard { + + position: absolute; + z-index: 100; + min-width: 4rem; + min-height: 4rem; + transform: scale( 1.05 ); +} +.Q-circuit-clipboard, .Q-circuit-clipboard > div { + + cursor: grabbing; +} +.Q-circuit-clipboard-danger .Q-circuit-operation { + + background-color: var( --Q-color-yellow ); +} +.Q-circuit-clipboard-destroy { + + animation-name: Q-circuit-clipboard-poof; + animation-fill-mode: forwards; + animation-duration: 0.3s; + animation-iteration-count: 1; +} +@keyframes Q-circuit-clipboard-poof { + + 100% { + + transform: scale( 1.5 ); + opacity: 0; + } +} +.Q-circuit-board-background { + + /* + + Clipboard: 100 + Operation: 0 + Shadow: -10 + Background: -20 + + */ + position: absolute; + z-index: -20; + color: rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-board-background > div { + +/* transition: + background-color 0.2s, + color 0.2s;*/ +} +.Q-circuit-board-background .Q-circuit-cell-highlighted { + + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); + /*transition: none;*/ +} + + + + +.Q-circuit-register-wire { + + position: absolute; + top: calc( 50% - 0.5px ); + width: 100%; + height: 1px; + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 50% + ); +} + +.Q-parameter-box-exit { + position: relative; + right: 0; + left: 0; + width: 5rem; + height: 2.5rem; + background-color: whitesmoke; +} + +.Q-parameters-box > div, +.Q-circuit-palette > div, +.Q-circuit-clipboard > div, +.Q-circuit-board-foreground > div { + + text-align: center; +} + + + + + + + /***************/ + /* */ + /* Headers */ + /* */ +/***************/ + + +.Q-circuit-header { + + position: sticky; + z-index: 2; + margin: 0; + /*background-color: var( --Q-color-background );*/ + background-color: white; + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 75% + ); + font-family: var( --Q-font-family-mono ); +} +.Q-circuit-input.Q-circuit-cell-highlighted, +.Q-circuit-header.Q-circuit-cell-highlighted { + + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); + color: black; +} +.Q-circuit-selectall { + + z-index: 3; + margin: 0; + top: 0; + /*left: 4rem;*/ + /*grid-column: 2;*/ + left: 0; + grid-column-start: 1; + grid-column-end: 3; + grid-row: 1; + cursor: se-resize; +} +.Q-circuit-moment-label, +.Q-circuit-moment-add { + + grid-row: 1; + top: 0; + cursor: s-resize; +} +.Q-circuit-register-label, +.Q-circuit-register-add { + + grid-column: 2; + left: 4rem; + cursor: e-resize; +} +.Q-circuit-moment-add, +.Q-circuit-register-add { + + cursor: pointer; +} +.Q-circuit-moment-add, +.Q-circuit-register-add { + + display: none; +} +.Q-circuit-selectall, +.Q-circuit-moment-label, +.Q-circuit-moment-add { + + border-bottom: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); +} +.Q-circuit-selectall, +.Q-circuit-register-label, +.Q-circuit-register-add { + + border-right: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); +} +.Q-circuit-input { + + position: sticky; + z-index: 2; + grid-column: 1; + left: 0; + /*background-color: var( --Q-color-background );*/ + background-color: white; + font-size: 1.5rem; + font-weight: 900; + font-family: var( --Q-font-family-mono ); +} + + + + + + +.Q-circuit-operation-link-container { + + --Q-link-stroke: 3px; + --Q-link-radius: 100%; + + display: block; + position: relative; + left: calc( 50% - ( var( --Q-link-stroke ) / 2 )); + width: 50%; + height: 100%; + overflow: hidden; +} +.Q-circuit-operation-link-container.Q-circuit-cell-highlighted { + + background-color: transparent; +} +.Q-circuit-operation-link { + + display: block; + position: absolute; + width: calc( var( --Q-link-stroke ) * 2 ); + height: calc( 100% - 4rem + var( --Q-link-stroke )); + /*border: var( --Q-link-stroke ) solid hsl( 0, 0%, 50% );*/ + border: var( --Q-link-stroke ) solid hsl( + + var( --Q-color-background-hue ), + 10%, + 30% + ); + + /*border: var( --Q-link-stroke ) solid var( --Q-color-orange );*/ + + transform: translate( -50%, calc( 2rem - ( var( --Q-link-stroke ) / 2 ))); + transform-origin: center; +} +.Q-circuit-operation-link.Q-circuit-operation-link-curved { + + width: calc( var( --Q-link-radius ) - var( --Q-link-stroke )); + width: 200%; + border-radius: 100%; +} + + + + + + + /******************/ + /* */ + /* Operations */ + /* */ +/******************/ + +.Q-circuit-operation { + + position: relative; + /*--Q-operation-color-hue: var( --Q-color-green-hue ); + --Q-operation-color-main: var( --Q-color-green );*/ + + --Q-operation-color-hue: var( --Q-color-blue-hue ); + --Q-operation-color-main: hsl( + + var( --Q-operation-color-hue ), + 10%, + 35% + ); + + --Q-operation-color-light: hsl( + + var( --Q-operation-color-hue ), + 10%, + 50% + ); + --Q-operation-color-dark: hsl( + + var( --Q-operation-color-hue ), + 10%, + 25% + ); + color: white; + text-shadow: -0.05rem -0.05rem 0 rgba( 0, 0, 0, 0.1 ); + font-size: 1.5rem; + line-height: 2.9rem; + font-weight: 900; + cursor: grab; +} +.Q-circuit-locked .Q-circuit-operation { + + cursor: not-allowed; +} +.Q-circuit-operation-tile { + + position: absolute; + top: 0.5rem; + left: 0.5rem; + right: 0.5rem; + bottom: 0.5rem; + + /*margin: 0.5rem;*/ + /*padding: 0.5rem;*/ + + /*box-shadow: 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.2 );*/ + border-radius: 0.2rem; + /* + border-top: 0.1rem solid var( --Q-operation-color-light ); + border-left: 0.1rem solid var( --Q-operation-color-light ); + border-right: 0.1rem solid var( --Q-operation-color-dark ); + border-bottom: 0.1rem solid var( --Q-operation-color-dark ); + */ + background: + var( --Q-operation-color-main ) + /*linear-gradient( + + 0.45turn, + rgba( 255, 255, 255, 0.1 ), + rgba( 0, 0, 0, 0.05 ) + )*/; +} +.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover { + + /*background-color: rgba( 255, 255, 255, 0.6 );*/ + background-color: white; +} +.Q-circuit-palette .Q-circuit-operation-tile { + + --Q-before-rotation: 12deg; + --Q-before-x: 1px; + --Q-before-y: -2px; + + --Q-after-rotation: -7deg; + --Q-after-x: -2px; + --Q-after-y: 3px; + + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-palette .Q-circuit-operation-tile:before, +.Q-circuit-palette .Q-circuit-operation-tile:after { + + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: 0.2rem; + /*background-color: hsl( 0, 0%, 60% );*/ + + background-color: var( --Q-operation-color-dark ); + transform: + translate( var( --Q-before-x ), var( --Q-before-y )) + rotate( var( --Q-before-rotation )); + z-index: -10; + /*z-index: 10;*/ + display: block; + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-palette .Q-circuit-operation-tile:after { + + transform: + translate( var( --Q-after-x ), var( --Q-after-y )) + rotate( var( --Q-after-rotation )); + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-operation:hover .Q-circuit-operation-tile { + + color: white; +} + + + + +.Q-circuit-operation-hadamard .Q-circuit-operation-tile { + + /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ + /*--Q-operation-color-main: var( --Q-color-red );*/ + + /*--Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 10% );*/ + + +/* background: + linear-gradient( + + -33deg, + var( --Q-color-blue ) 20%, + #6f3c69 50%, + var( --Q-color-red ) 80% + );*/ +} +.Q-circuit-operation-identity .Q-circuit-operation-tile, +.Q-circuit-operation-control .Q-circuit-operation-tile, +.Q-circuit-operation-target .Q-circuit-operation-tile { + + /*--Q-operation-color-hue: var( --Q-color-orange-hue );*/ + /*--Q-operation-color-main: var( --Q-color-orange );*/ + border-radius: 100%; +} +.Q-circuit-operation-identity .Q-circuit-operation-tile, +.Q-circuit-operation-control .Q-circuit-operation-tile { + + top: calc( 50% - 0.7rem ); + left: calc( 50% - 0.7rem ); + width: 1.4rem; + height: 1.4rem; + overflow: hidden; +/* --Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 10% );*/ +} +.Q-circuit-operation-pauli-x, +.Q-circuit-operation-pauli-y, +.Q-circuit-operation-pauli-z { + + /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ + /*--Q-operation-color-main: var( --Q-color-red );*/ + +/* --Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 30% );*/ +} +.Q-circuit-operation-swap .Q-circuit-operation-tile { + + top: calc( 50% - 0.55rem ); + left: calc( 50% - 0.55rem ); + width: 1.2rem; + height: 1.2rem; + border-radius: 0; + transform-origin: center; + transform: rotate( 45deg ); + font-size: 0; +} + +.Q-parameter-box-input-container { + position: relative; + text-align: center; + grid-auto-columns: 4rem; + grid-auto-flow: column; +} + +.Q-parameter-box-input { + position: relative; + border-radius: .2rem; + margin-left: 10px; + font-family: var( --Q-font-family-mono ); +} + +.Q-parameter-input-label { + position: relative; + color: var( --Q-color-blue ); + font-family: var( --Q-font-family-mono ); +} + + + + + /********************/ + /* */ + /* Other states */ + /* */ +/********************/ + + +.Q-circuit-palette > div:hover, +.Q-circuit-board-foreground > div:hover { + + outline: 2px solid var( --Q-hyperlink-internal-color ); + outline-offset: -2px; +} +.Q-circuit-palette > div:hover .Q-circuit-operation-tile { + + box-shadow: none; +} +/*.Q-circuit-palette > div:hover,*/ +.Q-circuit-board-foreground > div:hover { + + background-color: white; + color: black; +} + + + + + + +.Q-circuit-clipboard > div, +.Q-circuit-cell-selected { + + background-color: white; +} +.Q-circuit-clipboard > div:before, +.Q-circuit-cell-selected:before { + + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: block; + z-index: -10; + box-shadow: + 0 0 1rem rgba( 0, 0, 0, 0.2 ), + 0.4rem 0.4rem 0.2rem rgba( 0, 0, 0, 0.2 ); + outline: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 50% + ); + /*outline-offset: -1px;*/ +} + + + + +.Q-circuit-clipboard > div { + + background-color: white; +} +.Q-circuit-clipboard > div:before { + + /* + + This was very helpful! + https://blog.dudak.me/2014/css-shadows-under-adjacent-elements/ + + */ + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -10; + display: block; + box-shadow: 0.4rem 0.4rem 0.3rem rgba( 0, 0, 0, 0.2 ); +} + + + + + + + + /***************/ + /* */ + /* Buttons */ + /* */ +/***************/ + + +.Q-circuit-locked .Q-circuit-toggle-lock, +.Q-circuit-locked .Q-circuit-toggle-lock:hover { + + background-color: var( --Q-color-red ); +} +.Q-circuit-toggle-lock { + + z-index: 3; + left: 0; + top: 0; + grid-column: 1; + grid-row: 1; + cursor: pointer; + font-size: 1.1rem; + text-shadow: none; + font-weight: normal; +} +.Q-circuit-button-undo, +.Q-circuit-button-redo { + + font-size: 1.2rem; + line-height: 2.6rem; + font-weight: normal; +} + + + +.Q-circuit p { + + padding: 1rem; + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 66% + ); +} + + + diff --git a/build/bundle.js b/build/bundle.js new file mode 100644 index 0000000..5a90412 --- /dev/null +++ b/build/bundle.js @@ -0,0 +1,8238 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],3:[function(require,module,exports){ +//Logging functions + +function log(verbosity = 0.5, verbosityThreshold, ...remainingArguments) { + if (verbosity >= verbosityThreshold) console.log(...remainingArguments); + return "(log)"; +} + +function error() { + console.error(...arguments); + return "(error)"; +} + +function warn() { + console.warn(...arguments); + return "(error)"; +} + +function extractDocumentation(f) { + ` + I wanted a way to document code + that was cleaner, more legible, and more elegant + than the bullshit we put up with today. + Also wanted it to print nicely in the console. + `; + + f = f.toString(); + + const begin = f.indexOf("`") + 1, + end = f.indexOf("`", begin), + lines = f.substring(begin, end).split("\n"); + + function countPrefixTabs(text) { + // Is counting tabs “manually” + // actually more performant than regex? + + let count = (index = 0); + while (text.charAt(index++) === "\t") count++; + return count; + } + + //------------------- TO DO! + // we should check that there is ONLY whitespace between the function opening and the tick mark! + // otherwise it’s not documentation. + + let tabs = Number.MAX_SAFE_INTEGER; + + lines.forEach(function (line) { + if (line) { + const lineTabs = countPrefixTabs(line); + if (tabs > lineTabs) tabs = lineTabs; + } + }); + lines.forEach(function (line, i) { + if (line.trim() === "") line = "\n\n"; + lines[i] = line.substring(tabs).replace(/ {2}$/, "\n"); + }); + return lines.join(""); +} + +function help(f) { + if (f === undefined) f = Q; + return extractDocumentation(f); +} + +function toTitleCase(text) { + text = text.replace(/_/g, " "); + return text + .toLowerCase() + .split(" ") + .map(function (word) { + return word.replace(word[0], word[0].toUpperCase()); + }) + .join(" "); +} + +function centerText(text, length, filler) { + if (length > text.length) { + if (typeof filler !== "string") filler = " "; + + const padLengthLeft = Math.floor((length - text.length) / 2), + padLengthRight = length - text.length - padLengthLeft; + + return text + .padStart(padLengthLeft + text.length, filler) + .padEnd(length, filler); + } else return text; +} + +module.exports = { log, error, help, warn, toTitleCase, centerText }; + +},{}],4:[function(require,module,exports){ +//math functions +function hypotenuse(x, y) { + let a = Math.abs(x), + b = Math.abs(y); + + if (a < 2048 && b < 2048) { + return Math.sqrt(a * a + b * b); + } + if (a < b) { + a = b; + b = x / y; + } else b = y / x; + return a * Math.sqrt(1 + b * b); +} + +function logHypotenuse(x, y) { + const a = Math.abs(x), + b = Math.abs(y); + + if (x === 0) return Math.log(b); + if (y === 0) return Math.log(a); + if (a < 2048 && b < 2048) { + return Math.log(x * x + y * y) / 2; + } + return Math.log(x / Math.cos(Math.atan2(y, x))); +} + +function hyperbolicSine(n) { + return (Math.exp(n) - Math.exp(-n)) / 2; +} + +function hyperbolicCosine(n) { + return (Math.exp(n) + Math.exp(-n)) / 2; +} + +function round(n, d) { + if (typeof d !== "number") d = 0; + const f = Math.pow(10, d); + return Math.round(n * f) / f; +} + +function isUsefulNumber(n) { + return ( + isNaN(n) === false && + (typeof n === "number" || n instanceof Number) && + n !== Infinity && + n !== -Infinity + ); +} + +function isUsefulInteger(n) { + return isUsefulNumber(n) && Number.isInteger(n); +} + + +module.exports = { isUsefulNumber, isUsefulInteger, hypotenuse, logHypotenuse, hyperbolicCosine, hyperbolicSine, round }; + +},{}],5:[function(require,module,exports){ +(function (process,global){(function (){ +const logger = require('./Logging'); + +const constants = {}; +function dispatchCustomEventToGlobal(event_name, detail, terminate_on_error=false, silent=true) { + try { + const event = new CustomEvent(event_name, detail); + if(typeof window != undefined) { + window.dispatchEvent(event); + } + else { + //if window does exist, global == window is true. So maybe we can just do global.dispatchEvent instead of this wrapper? + global.dispatchEvent(event); + if(!silent) console.log(event); + } + } catch(e) { + //When running in node, CustomEvent and documents don't exist. We can emulate using a JSDOM package + if(!silent) logger.error("Could not dispatch custom event."); + if(terminate_on_error) process.exit(); + } + +} + +function createConstant(key, value) { + //Object.freeze( value ) + this[key] = value; + // Object.defineProperty( this, key, { + + // value, + // writable: false + // }) + // Object.defineProperty( this.constants, key, { + + // value, + // writable: false + // }) + constants[key] = this[key]; + Object.freeze(this[key]); +} + +function createConstants() { + if (arguments.length % 2 !== 0) { + return logger.error( + "Q attempted to create constants with invalid (KEY, VALUE) pairs." + ); + } + for (let i = 0; i < arguments.length; i += 2) { + createConstant(arguments[i], arguments[i + 1]); + } +} +// function loop() {} + +let namesIndex = 0; +let shuffledNames = []; +function shuffleNames$() { + let m = []; + for (let c = 0; c < COLORS.length; c++) { + for (let a = 0; a < ANIMALS.length; a++) { + m.push([c, a, Math.random()]); + } + } + shuffledNames = m.sort(function (a, b) { + return a[2] - b[2]; + }); +} + +function getRandomName$() { + if (shuffledNames.length === 0) shuffleNames$(); + const pair = shuffledNames[namesIndex], + name = COLORS[pair[0]] + " " + ANIMALS[pair[1]]; + + namesIndex = (namesIndex + 1) % shuffledNames.length; + return name; +} + +function hueToColorName(hue) { + hue = hue % 360; + hue = Math.floor(hue / 10); + return COLORS[hue]; +} + +function colorIndexToHue(i) { + return i * 10; +} + +createConstants( + "REVISION", + 19, + + // Yeah... F’ing floating point numbers, Man! + // Here’s the issue: + // var a = new Q.ComplexNumber( 1, 2 ) + // a.multiply(a).isEqualTo( a.power( new Q.ComplexNumber( 2, 0 ))) + // That’s only true if Q.EPSILON >= Number.EPSILON * 6 + + "EPSILON", + Number.EPSILON * 6, + + "RADIANS_TO_DEGREES", + 180 / Math.PI, + + + "ANIMALS", + [ + "Aardvark", + "Albatross", + "Alligator", + "Alpaca", + "Ant", + "Anteater", + "Antelope", + "Ape", + "Armadillo", + "Baboon", + "Badger", + "Barracuda", + "Bat", + "Bear", + "Beaver", + "Bee", + "Bison", + "Boar", + "Buffalo", + "Butterfly", + "Camel", + "Caribou", + "Cat", + "Caterpillar", + "Cattle", + "Chamois", + "Cheetah", + "Chicken", + "Chimpanzee", + "Chinchilla", + "Chough", + "Clam", + "Cobra", + "Cod", + "Cormorant", + "Coyote", + "Crab", + "Crane", + "Crocodile", + "Crow", + "Curlew", + "Deer", + "Dinosaur", + "Dog", + "Dogfish", + "Dolphin", + "Donkey", + "Dotterel", + "Dove", + "Dragonfly", + "Duck", + "Dugong", + "Dunlin", + "Eagle", + "Echidna", + "Eel", + "Eland", + "Elephant", + "Elephant seal", + "Elk", + "Emu", + "Falcon", + "Ferret", + "Finch", + "Fish", + "Flamingo", + "Fly", + "Fox", + "Frog", + "Galago", + "Gaur", + "Gazelle", + "Gerbil", + "Giant Panda", + "Giraffe", + "Gnat", + "Gnu", + "Goat", + "Goose", + "Goldfinch", + "Goldfish", + "Gorilla", + "Goshawk", + "Grasshopper", + "Grouse", + "Guanaco", + "Guinea fowl", + "Guinea pig", + "Gull", + "Guppy", + "Hamster", + "Hare", + "Hawk", + "Hedgehog", + "Hen", + "Heron", + "Herring", + "Hippopotamus", + "Hornet", + "Horse", + "Human", + "Hummingbird", + "Hyena", + "Ide", + "Jackal", + "Jaguar", + "Jay", + "Jellyfish", + "Kangaroo", + "Koala", + "Koi", + "Komodo dragon", + "Kouprey", + "Kudu", + "Lapwing", + "Lark", + "Lemur", + "Leopard", + "Lion", + "Llama", + "Lobster", + "Locust", + "Loris", + "Louse", + "Lyrebird", + "Magpie", + "Mallard", + "Manatee", + "Marten", + "Meerkat", + "Mink", + "Mole", + "Monkey", + "Moose", + "Mouse", + "Mosquito", + "Mule", + "Narwhal", + "Newt", + "Nightingale", + "Octopus", + "Okapi", + "Opossum", + "Oryx", + "Ostrich", + "Otter", + "Owl", + "Ox", + "Oyster", + "Panther", + "Parrot", + "Partridge", + "Peafowl", + "Pelican", + "Penguin", + "Pheasant", + "Pig", + "Pigeon", + "Pony", + "Porcupine", + "Porpoise", + "Prairie Dog", + "Quail", + "Quelea", + "Rabbit", + "Raccoon", + "Rail", + "Ram", + "Raven", + "Reindeer", + "Rhinoceros", + "Rook", + "Ruff", + "Salamander", + "Salmon", + "Sand Dollar", + "Sandpiper", + "Sardine", + "Scorpion", + "Sea lion", + "Sea Urchin", + "Seahorse", + "Seal", + "Shark", + "Sheep", + "Shrew", + "Shrimp", + "Skunk", + "Snail", + "Snake", + "Sow", + "Spider", + "Squid", + "Squirrel", + "Starling", + "Stingray", + "Stinkbug", + "Stork", + "Swallow", + "Swan", + "Tapir", + "Tarsier", + "Termite", + "Tiger", + "Toad", + "Trout", + "Tui", + "Turkey", + "Turtle", + // U + "Vicuña", + "Viper", + "Vulture", + "Wallaby", + "Walrus", + "Wasp", + "Water buffalo", + "Weasel", + "Whale", + "Wolf", + "Wolverine", + "Wombat", + "Woodcock", + "Woodpecker", + "Worm", + "Wren", + // X + "Yak", + "Zebra", + ], + "ANIMALS3", + [ + "ape", + "bee", + "cat", + "dog", + "elk", + "fox", + "gup", + "hen", + "ide", + "jay", + "koi", + "leo", + "moo", + "nit", + "owl", + "pig", + // Q ? + "ram", + "sow", + "tui", + // U ? + // V ? + // W ? + // X ? + "yak", + "zeb", + ], + "COLORS", + [ + "Red", // 0 RED + "Scarlet", // 10 + "Tawny", // 20 + "Carrot", // 30 + "Pumpkin", // 40 + "Mustard", // 50 + "Lemon", // 60 Yellow + "Lime", // 70 + "Spring bud", // 80 + "Spring grass", // 90 + "Pear", // 100 + "Kelly", // 110 + "Green", // 120 GREEN + "Malachite", // 130 + "Sea green", // 140 + "Sea foam", // 150 + "Aquamarine", // 160 + "Turquoise", // 170 + "Cyan", // 180 Cyan + "Pacific blue", // 190 + "Baby blue", // 200 + "Ocean blue", // 210 + "Sapphire", // 220 + "Azure", // 230 + "Blue", // 240 BLUE + "Cobalt", // 250 + "Indigo", // 260 + "Violet", // 270 + "Lavender", // 280 + "Purple", // 290 + "Magenta", // 300 Magenta + "Hot pink", // 310 + "Fuschia", // 320 + "Ruby", // 330 + "Crimson", // 340 + "Carmine", // 350 + ] +); + +module.exports = { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchCustomEventToGlobal, constants }; + +}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./Logging":3,"_process":2}],6:[function(require,module,exports){ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + + +// +const logger = require('./Logging'); +const misc = require('./Misc'); +const mathf = require('./Math-Functions'); +const {ComplexNumber} = require('./Q-ComplexNumber'); +const {Gate} = require('./Q-Gate'); +const {Qubit} = require('./Q-Qubit'); +const {Matrix} = require('./Q-Matrix'); +const {History} = require('./Q-History'); + + + + +Circuit = function( bandwidth, timewidth ){ + + // What number Circuit is this + // that we’re attempting to make here? + + this.index = Circuit.index ++ + + + // How many qubits (registers) shall we use? + + if( !mathf.isUsefulInteger( bandwidth )) bandwidth = 3 + this.bandwidth = bandwidth + + + // How many operations can we perform on each qubit? + // Each operation counts as one moment; one clock tick. + + if( !mathf.isUsefulInteger( timewidth )) timewidth = 5 + this.timewidth = timewidth + + + // We’ll start with Horizontal qubits (zeros) as inputs + // but we can of course modify this after initialization. + + this.qubits = new Array( bandwidth ).fill( Qubit.HORIZONTAL ) + + + // What operations will we perform on our qubits? + + this.operations = [] + + + // Does our circuit need evaluation? + // Certainly, yes! + // (And will again each time it is modified.) + + this.needsEvaluation = true + + + // When our circuit is evaluated + // we store those results in this array. + + this.results = [] + this.matrix = null + + + // Undo / Redo history. + this.history = new History( this ) + +} + + + + +Object.assign( Circuit, { + index: 0, + help: function(){ return logger.help( this )}, + constants: {}, + createConstant: misc.createConstant, + createConstants: misc.createConstants, + + + fromText: function( text ){ + + + // This is a quick way to enable `fromText()` + // to return a default new Circuit(). + + if( text === undefined ) return new Circuit() + + // Is this a String Template -- as opposed to a regular String? + // If so, let’s convert it to a regular String. + // Yes, this maintains the line breaks. + + if( text.raw !== undefined ) text = ''+text.raw + return Circuit.fromTableTransposed( + + text + .trim() + .split( /\r?\n/ ) + .filter( function( item ){ return item.length }) + .map( function( item, r ){ + + return item + .trim() + .split( /[-+\s+=+]/ ) + .filter( function( item ){ return item.length }) + .map( function( item, m ){ + + //const matches = item.match( /(^\w+)(#(\w+))*(\.(\d+))*/ ) + const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ ) + return { + + gateSymbol: matches[ 1 ], + operationMomentId: matches[ 3 ], + mappingIndex: +matches[ 5 ] + } + }) + }) + ) + }, + + + + + + + + + + + + + + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Working out a new syntax here... Patience please! + + + fromText2: function( text ){ + + + text = ` + H C C + I C1 C1 + I X1 S1 + I X1 S1` + + + // This is a quick way to enable `fromText()` + // to return a default new Circuit(). + + if( text === undefined ) return new Circuit() + + + // Is this a String Template -- as opposed to a regular String? + // If so, let’s convert it to a regular String. + // Yes, this maintains the line breaks. + + if( text.raw !== undefined ) text = ''+text.raw + + + + text + .trim() + .split( /\r?\n/ ) + .filter( function( item ){ return item.length }) + .map( function( item, r ){ + + return item + .trim() + .split( /[-+\s+=+]/ ) + .filter( function( item ){ return item.length }) + .map( function( item, m ){ + + // +++++++++++++++++++++++ + // need to map LETTER[] optional NUMBER ] + + const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ ) + + //const matches = item.match( /(^\w+)(#(\w+))*(\.(\d+))*/ ) + // const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ ) + // return { + + // gateSymbol: matches[ 1 ], + // operationMomentId: matches[ 3 ], + // mappingIndex: +matches[ 5 ] + // } + }) + }) + + }, + + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + + + + + + + + + + fromTableTransposed: function( table ){ + const + bandwidth = table.length, + timewidth = table.reduce( function( max, moments ){ + + return Math.max( max, moments.length ) + + }, 0 ), + circuit = new Circuit( bandwidth, timewidth ) + + circuit.bandwidth = bandwidth + circuit.timewidth = timewidth + for( let r = 0; r < bandwidth; r ++ ){ + + const registerIndex = r + 1 + for( let m = 0; m < timewidth; m ++ ){ + + const + momentIndex = m + 1, + operation = table[ r ][ m ] + let siblingHasBeenFound = false + for( let s = 0; s < r; s ++ ){ + + const sibling = table[ s ][ m ] + if( operation.gateSymbol === sibling.gateSymbol && + operation.operationMomentId === sibling.operationMomentId && + mathf.isUsefulInteger( operation.mappingIndex ) && + mathf.isUsefulInteger( sibling.mappingIndex ) && + operation.mappingIndex !== sibling.mappingIndex ){ + + + // We’ve found a sibling ! + const operationsIndex = circuit.operations.findIndex( function( operation ){ + + return ( + + operation.momentIndex === momentIndex && + operation.registerIndices.includes( s + 1 ) + ) + }) + // console.log( 'operationsIndex?', operationsIndex ) + circuit.operations[ operationsIndex ].registerIndices[ operation.mappingIndex ] = registerIndex + circuit.operations[ operationsIndex ].isControlled = operation.gateSymbol != '*'// Q.Gate.SWAP. + siblingHasBeenFound = true + } + } + if( siblingHasBeenFound === false && operation.gateSymbol !== 'I' ){ + + const + gate = Gate.findBySymbol( operation.gateSymbol ), + registerIndices = [] + + if( mathf.isUsefulInteger( operation.mappingIndex )){ + + registerIndices[ operation.mappingIndex ] = registerIndex + } + else registerIndices[ 0 ] = registerIndex + circuit.operations.push({ + + gate, + momentIndex, + registerIndices, + isControlled: false, + operationMomentId: operation.operationMomentId + }) + } + } + } + circuit.sort$() + return circuit + }, + + + + + controlled: function( U ){ + + + // we should really just replace this with a nice Matrix.copy({}) command!!!! + + // console.log( 'U?', U ) + + const + size = U.getWidth(), + result = Matrix.createIdentity( size * 2 ) + + // console.log( 'U', U.toTsv() ) + // console.log( 'size', size ) + // console.log( 'result', result.toTsv() ) + + for( let x = 0; x < size; x ++ ){ + + for( let y = 0; y < size; y ++ ){ + const v = U.read( x, y ) + // console.log( `value at ${x}, ${y}`, v ) + result.write$( x + size, y + size, v ) + } + } + return result + }, + + //given an operation, return whether or not it is a valid control operation on the circuit-editor. + isControlledOperation: function( operation ) { + return (!operation.gate.is_multi_qubit || operation.gate.name === 'Swap') //assumption: we won't allow controlled multi-qubit operations + //..except swap or CNOT + && (operation.registerIndices.length >= 2) + && (operation.gate.can_be_controlled) + }, + + + // Return transformation over entire nqubit register that applies U to + // specified qubits (in order given). + // Algorithm from Lee Spector's "Automatic Quantum Computer Programming" + // Page 21 in the 2004 PDF? + // http://148.206.53.84/tesiuami/S_pdfs/AUTOMATIC%20QUANTUM%20COMPUTER%20PROGRAMMING.pdf + + expandMatrix: function( circuitBandwidth, U, qubitIndices ){ + // console.log( 'EXPANDING THE MATRIX...' ) + // console.log( 'this one: U', U.toTsv()) + + const _qubits = [] + const n = Math.pow( 2, circuitBandwidth ) + + + // console.log( 'qubitIndices used by this operation:', qubitIndices ) + // console.log( 'qubits before slice', qubitIndices ) + // qubitIndices = qubitIndices.slice( 0 ) + // console.log( 'qubits AFTER slice', qubitIndices ) + + + + + for( let i = 0; i < qubitIndices.length; i ++ ){ + + //qubitIndices[ i ] = ( circuitBandwidth - 1 ) - qubitIndices[ i ] + qubitIndices[ i ] -= 1 + } + // console.log( 'qubits AFTER manipulation', qubitIndices ) + + + qubitIndices.reverse() + for( let i = 0; i < circuitBandwidth; i ++ ){ + + if( qubitIndices.indexOf( i ) == -1 ){ + + _qubits.push( i ) + } + } + + + // console.log( 'qubitIndices vs _qubits:' ) + // console.log( 'qubitIndices', qubitIndices ) + // console.log( '_qubits', _qubits ) + + + + const result = new Matrix.createZero( n ) + + + // const X = numeric.rep([n, n], 0); + // const Y = numeric.rep([n, n], 0); + + + let i = n + while( i -- ){ + + let j = n + while( j -- ){ + + let + bitsEqual = true, + k = _qubits.length + + while( k -- ){ + + if(( i & ( 1 << _qubits[ k ])) != ( j & ( 1 << _qubits[ k ]))){ + + bitsEqual = false + break + } + } + if( bitsEqual ){ + + // console.log( 'bits ARE equal' ) + let + istar = 0, + jstar = 0, + k = qubitIndices.length + + while( k -- ){ + + const q = qubitIndices[ k ] + istar |= (( i & ( 1 << q )) >> q ) << k + jstar |= (( j & ( 1 << q )) >> q ) << k + } + //console.log( 'U.read( istar, jstar )', U.read( istar, jstar ).toText() ) + + // console.log( 'before write$', result.toTsv()) + + // console.log( 'U.read at ', istar, jstar, '=', U.read( istar, jstar ).toText()) + result.write$( i, j, U.read( istar, jstar )) + + // console.log( 'after write$', result.toTsv()) + + // X[i][j] = U.x[ istar ][ jstar ] + // Y[i][j] = U.y[ istar ][ jstar ] + } + // else console.log('bits NOT equal') + } + } + //return new numeric.T(X, Y); + + // console.log( 'expanded matrix to:', result.toTsv() ) + return result + }, + + + evaluate: function( circuit ){ + + + // console.log( circuit.toDiagram() ) + + misc.dispatchCustomEventToGlobal( + + 'Circuit.evaluate began', { + + detail: { circuit } + } + ); + + + // Our circuit’s operations must be in the correct order + // before we attempt to step through them! + + circuit.sort$() + + + + // Create a new matrix (or more precisely, a vector) + // that is a 1 followed by all zeros. + // + // ┌ ┐ + // │ 1 │ + // │ 0 │ + // │ 0 │ + // │ . │ + // │ . │ + // │ . │ + // └ ┘ + + const state = new Matrix( 1, Math.pow( 2, circuit.bandwidth )) + state.write$( 0, 0, 1 ) + + + + + // Create a state matrix from this circuit’s input qubits. + + // const state2 = circuit.qubits.reduce( function( state, qubit, i ){ + + // if( i > 0 ) return state.multiplyTensor( qubit ) + // else return state + + // }, circuit.qubits[ 0 ]) + // console.log( 'Initial state', state2.toTsv() ) + // console.log( 'multiplied', state2.multiplyTensor( state ).toTsv() ) + + + + + + const operationsTotal = circuit.operations.length + let operationsCompleted = 0 + let matrix = circuit.operations.reduce( function( state, operation, i ){ + + + let U + if( operation.registerIndices.length < Infinity ){ + + if( operation.isControlled ){ + //if( operation.registerIndices.length > 1 ){ + + // operation.gate = Q.Gate.PAULI_X + // why the F was this hardcoded in there?? what was i thinking?! + // OH I KNOW ! + // that was from back when i represented this as "C" -- its own gate + // rather than an X with multiple registers. + // so now no need for this "if" block at all. + // will remove in a few cycles. + } + U = operation.gate.matrix + } + else { + + // This is for Quantum Fourier Transforms (QFT). + // Will have to come back to this at a later date! + } + // console.log( operation.gate.name, U.toTsv() ) + + + + + + // Yikes. May need to separate registerIndices in to controls[] and targets[] ?? + // Works for now tho..... + // Houston we have a problem. Turns out, not every gate with registerIndices.length > 1 is + // controlled. + // This is a nasty fix, leads to a lot of edge cases. But just experimenting. + if( Circuit.isControlledOperation(operation) ) { + const scale = operation.registerIndices.length - ( operation.gate.is_multi_qubit ? 2 : 1) + for( let j = 0; j < scale; j ++ ){ + + U = Circuit.controlled( U ) + // console.log( 'qubitIndex #', j, 'U = Circuit.controlled( U )', U.toTsv() ) + } + } + + + // We need to send a COPY of the registerIndices Array + // to .expandMatrix() + // otherwise it *may* modify the actual registerIndices Array + // and wow -- tracking down that bug was painful! + + const registerIndices = operation.registerIndices.slice() + state = Circuit.expandMatrix( + + circuit.bandwidth, + U, + registerIndices + + ).multiply( state ) + + + + operationsCompleted ++ + const progress = operationsCompleted / operationsTotal + + + misc.dispatchCustomEventToGlobal('Circuit.evaluate progressed', { detail: { + + circuit, + progress, + operationsCompleted, + operationsTotal, + momentIndex: operation.momentIndex, + registerIndices: operation.registerIndices, + gate: operation.gate.name, + state + + }}) + + + // console.log( `\n\nProgress ... ${ Math.round( operationsCompleted / operationsTotal * 100 )}%`) + // console.log( 'Moment .....', operation.momentIndex ) + // console.log( 'Registers ..', JSON.stringify( operation.registerIndices )) + // console.log( 'Gate .......', operation.gate.name ) + // console.log( 'Intermediate result:', state.toTsv() ) + // console.log( '\n' ) + + + return state + + }, state ) + + + + + + + const outcomes = matrix.rows.reduce( function( outcomes, row, i ){ + + outcomes.push({ + + state: '|'+ parseInt( i, 10 ).toString( 2 ).padStart( circuit.bandwidth, '0' ) +'⟩', + probability: Math.pow( row[ 0 ].absolute(), 2 ) + }) + return outcomes + + }, [] ) + + + + circuit.needsEvaluation = false + circuit.matrix = matrix + circuit.results = outcomes + + + + misc.dispatchCustomEventToGlobal('Circuit.evaluate completed', { detail: { + // circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: { + + circuit, + results: outcomes + + }}) + + + + + return matrix + } +}) + + + + + + + +Object.assign( Circuit.prototype, { + + clone: function(){ + + const + original = this, + clone = original.copy() + + clone.qubits = original.qubits.slice() + clone.results = original.results.slice() + clone.needsEvaluation = original.needsEvaluation + + return clone + }, + evaluate$: function(){ + + Circuit.evaluate( this ) + return this + }, + report$: function( length ){ + + if( this.needsEvaluation ) this.evaluate$() + if( !mathf.isUsefulInteger( length )) length = 20 + + const + circuit = this, + text = this.results.reduce( function( text, outcome, i ){ + + const + probabilityPositive = Math.round( outcome.probability * length ), + probabilityNegative = length - probabilityPositive + + return text +'\n' + + ( i + 1 ).toString().padStart( Math.ceil( Math.log10( Math.pow( 2, circuit.qubits.length ))), ' ' ) +' ' + + outcome.state +' ' + + ''.padStart( probabilityPositive, '█' ) + + ''.padStart( probabilityNegative, '░' ) + + mathf.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance' + + }, '' ) + '\n' + return text + }, + try$: function(){ + + if( this.needsEvaluation ) this.evaluate$() + + + // We need to “stack” our probabilities from 0..1. + + const outcomesStacked = new Array( this.results.length ) + this.results.reduce( function( sum, outcome, i ){ + + sum += outcome.probability + outcomesStacked[ i ] = sum + return sum + + }, 0 ) + + + // Now we can pick a random number + // and return the first outcome + // with a probability equal to or greater than + // that random number. + + const + randomNumber = Math.random(), + randomIndex = outcomesStacked.findIndex( function( index ){ + + return randomNumber <= index + }) + + + // Output that to the console + // but return the random index + // so we can pipe that to something else + // should we want to :) + + // console.log( this.outcomes[ randomIndex ].state ) + return randomIndex + }, + + + + + //////////////// + // // + // Output // + // // + //////////////// + + + // This is absolutely required by toTable. + + sort$: function(){ + + + // Sort this circuit’s operations + // primarily by momentIndex, + // then by the first registerIndex. + + this.operations.sort( function( a, b ){ + + if( a.momentIndex === b.momentIndex ){ + + + // Note that we are NOT sorting registerIndices here! + // We are merely asking which set of indices contain + // the lowest register index. + // If we instead sorted the registerIndices + // we could confuse which qubit is the controller + // and which is the controlled! + + return Math.min( ...a.registerIndices ) - Math.min( b.registerIndices ) + } + else { + + return a.momentIndex - b.momentIndex + } + }) + return this + }, + + + + + + + /////////////////// + // // + // Exporters // + // // + /////////////////// + + + // Many export functions rely on toTable + // and toTable itself absolutely relies on + // a circuit’s operations to be SORTED correctly. + // We could force circuit.sort$() here, + // but then toTable would become toTable$ + // and every exporter that relies on it would + // also become destructive. + + toTable: function(){ + + const + table = new Array( this.timewidth ), + circuit = this + + + // Sure, this is equal to table.length + // but isn’t legibility and convenience everything? + + table.timewidth = this.timewidth + + + // Similarly, this should be equal to table[ 0 ].length + // or really table[ i >= 0; i < table.length ].length, + // but again, lowest cognitive hurdle is key ;) + + table.bandwidth = this.bandwidth + + + // First, let’s establish a “blank” table + // that contains an identity operation + // for each register during each moment. + + table.fill( 0 ).forEach( function( element, index, array ){ + + const operations = new Array( circuit.bandwidth ) + operations.fill( 0 ).forEach( function( element, index, array ){ + + array[ index ] = { + + symbol: 'I', + symbolDisplay: 'I', + name: 'Identity', + nameCss: 'identity', + gateInputIndex: 0, + bandwidth: 0, + thisGateAmongMultiQubitGatesIndex: 0, + aSiblingIsAbove: false, + aSiblingIsBelow: false + } + }) + array[ index ] = operations + }) + + + // Now iterate through the circuit’s operations list + // and note those operations in our table. + // NOTE: This relies on operations being pre-sorted with .sort$() + // prior to the .toTable() call. + + let + momentIndex = 1, + multiRegisterOperationIndex = 0, + gateTypesUsedThisMoment = {} + + this.operations.forEach( function( operation, operationIndex, operations ){ + + + // We need to keep track of + // how many multi-register operations + // occur during this moment. + + if( momentIndex !== operation.momentIndex ){ + + table[ momentIndex ].gateTypesUsedThisMoment = gateTypesUsedThisMoment + momentIndex = operation.momentIndex + multiRegisterOperationIndex = 0 + gateTypesUsedThisMoment = {} + } + if( operation.registerIndices.length > 1 ){ + + table[ momentIndex - 1 ].multiRegisterOperationIndex = multiRegisterOperationIndex + multiRegisterOperationIndex ++ + } + if( gateTypesUsedThisMoment[ operation.gate.symbol ] === undefined ){ + + gateTypesUsedThisMoment[ operation.gate.symbol ] = 1 + } + else gateTypesUsedThisMoment[ operation.gate.symbol ] ++ + + + // By default, an operation’s CSS name + // is its regular name, all lowercase, + // with all spaces replaced by hyphens. + + let nameCss = operation.gate.name.toLowerCase().replace( /\s+/g, '-' ) + + + operation.registerIndices.forEach( function( registerIndex, indexAmongSiblings ){ + + let isMultiRegisterOperation = false + if( operation.registerIndices.length > 1 ){ + + isMultiRegisterOperation = true + if( indexAmongSiblings === operation.registerIndices.length - 1 ){ + + nameCss = 'target' + } + else { + + nameCss = 'control' + } + + // May need to re-visit the code above in consideration of SWAPs. + + } + table[ operation.momentIndex - 1 ][ registerIndex - 1 ] = { + + symbol: operation.gate.symbol, + symbolDisplay: operation.gate.symbol, + name: operation.gate.name, + nameCss, + operationIndex, + momentIndex: operation.momentIndex, + registerIndex, + isMultiRegisterOperation, + multiRegisterOperationIndex, + gatesOfThisTypeNow: gateTypesUsedThisMoment[ operation.gate.symbol ], + indexAmongSiblings, + siblingExistsAbove: Math.min( ...operation.registerIndices ) < registerIndex, + siblingExistsBelow: Math.max( ...operation.registerIndices ) > registerIndex + } + }) + +/* + + +++++++++++++++++++++++ + +Non-fatal problem to solve here: + +Previously we were concerned with “gates of this type used this moment” +when we were thinking about CNOT as its own special gate. +But now that we treat CNOT as just connected X gates, +we now have situations +where a moment can have one “CNOT” but also a stand-alone X gate +and toTable will symbol the “CNOT” as X.0 +(never X.1, because it’s the only multi-register gate that moment) +but still uses the symbol X.0 instead of just X +because there’s another stand-alone X there tripping the logic!!! + + + + + +*/ + + + // if( operationIndex === operations.length - 1 ){ + + table[ momentIndex - 1 ].gateTypesUsedThisMoment = gateTypesUsedThisMoment + // } + }) + + + + + + + + + + + + table.forEach( function( moment, m ){ + + moment.forEach( function( operation, o ){ + + if( operation.isMultiRegisterOperation ){ + + if( moment.gateTypesUsedThisMoment[ operation.symbol ] > 1 ){ + + operation.symbolDisplay = operation.symbol +'.'+ ( operation.gatesOfThisTypeNow - 1 ) + } + operation.symbolDisplay += '#'+ operation.indexAmongSiblings + } + }) + }) + + + // Now we can easily read down each moment + // and establish the moment’s character width. + // Very useful for text-based diagrams ;) + + table.forEach( function( moment ){ + + const maximumWidth = moment.reduce( function( maximumWidth, operation ){ + + return Math.max( maximumWidth, operation.symbolDisplay.length ) + + }, 1 ) + moment.maximumCharacterWidth = maximumWidth + }) + + + // We can also do this for the table as a whole. + + table.maximumCharacterWidth = table.reduce( function( maximumWidth, moment ){ + + return Math.max( maximumWidth, moment.maximumCharacterWidth ) + + }, 1 ) + + + // I think we’re done here. + + return table + }, + toText: function( makeAllMomentsEqualWidth ){ + + ` + Create a text representation of this circuit + using only common characters, + ie. no fancy box-drawing characters. + This is the complement of Circuit.fromText() + ` + + const + table = this.toTable(), + output = new Array( table.bandwidth ).fill( '' ) + + for( let x = 0; x < table.timewidth; x ++ ){ + + for( let y = 0; y < table.bandwidth; y ++ ){ + + let cellString = table[ x ][ y ].symbolDisplay.padEnd( table[ x ].maximumCharacterWidth, '-' ) + if( makeAllMomentsEqualWidth && x < table.timewidth - 1 ){ + + cellString = table[ x ][ y ].symbolDisplay.padEnd( table.maximumCharacterWidth, '-' ) + } + if( x > 0 ) cellString = '-'+ cellString + output[ y ] += cellString + } + } + return '\n'+ output.join( '\n' ) + // return output.join( '\n' ) + }, + toDiagram: function( makeAllMomentsEqualWidth ){ + + ` + Create a text representation of this circuit + using fancy box-drawing characters. + ` + + const + scope = this, + table = this.toTable(), + output = new Array( table.bandwidth * 3 + 1 ).fill( '' ) + + output[ 0 ] = ' ' + scope.qubits.forEach( function( qubit, q ){ + + const y3 = q * 3 + output[ y3 + 1 ] += ' ' + output[ y3 + 2 ] += 'r'+ ( q + 1 ) +' |'+ qubit.beta.toText().trim() +'⟩─' + output[ y3 + 3 ] += ' ' + }) + for( let x = 0; x < table.timewidth; x ++ ){ + + const padToLength = makeAllMomentsEqualWidth + ? table.maximumCharacterWidth + : table[ x ].maximumCharacterWidth + + output[ 0 ] += logger.centerText( 'm'+ ( x + 1 ), padToLength + 4 ) + for( let y = 0; y < table.bandwidth; y ++ ){ + + let + operation = table[ x ][ y ], + first = '', + second = '', + third = '' + + if( operation.symbol === 'I' ){ + + first += ' ' + second += '──' + third += ' ' + + first += ' '.padEnd( padToLength ) + second += logger.centerText( '○', padToLength, '─' ) + third += ' '.padEnd( padToLength ) + + first += ' ' + if( x < table.timewidth - 1 ) second += '──' + else second += ' ' + third += ' ' + } + else { + + if( operation.isMultiRegisterOperation ){ + + first += '╭─' + third += '╰─' + } + else { + + first += '┌─' + third += '└─' + } + second += '┤ ' + + first += '─'.padEnd( padToLength, '─' ) + second += logger.centerText( operation.symbolDisplay, padToLength ) + third += '─'.padEnd( padToLength, '─' ) + + + if( operation.isMultiRegisterOperation ){ + + first += '─╮' + third += '─╯' + } + else { + + first += '─┐' + third += '─┘' + } + second += x < table.timewidth - 1 ? ' ├' : ' │' + + if( operation.isMultiRegisterOperation ){ + + let n = ( operation.multiRegisterOperationIndex * 2 ) % ( table[ x ].maximumCharacterWidth + 1 ) + 1 + if( operation.siblingExistsAbove ){ + + first = first.substring( 0, n ) +'┴'+ first.substring( n + 1 ) + } + if( operation.siblingExistsBelow ){ + + third = third.substring( 0, n ) +'┬'+ third.substring( n + 1 ) + } + } + } + const y3 = y * 3 + output[ y3 + 1 ] += first + output[ y3 + 2 ] += second + output[ y3 + 3 ] += third + } + } + return '\n'+ output.join( '\n' ) + }, + + + + + // Oh yes my friends... WebGL is coming! + + toShader: function(){ + + }, + toGoogleCirq: function(){ +/* + + +cirq.GridQubit(4,5) + +https://cirq.readthedocs.io/en/stable/tutorial.html + +*/ + const header = `import cirq` + + return headers + }, + toAmazonBraket: function(){ + let isValidBraketCircuit = true + const header = `import boto3 +from braket.aws import AwsDevice +from braket.circuits import Circuit + +my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket +my_prefix = "Your-Folder-Name" # the name of the folder in the bucket +s3_folder = (my_bucket, my_prefix)\n +device = LocalSimulator()\n\n` +//TODO (ltnln): Syntax is different for simulators and actual quantum computers. Should there be a default? Should there be a way to change? +//vs an actual quantum computer? May not be necessary. + let variables = '' + let num_unitaries = 0 + //`qjs_circuit = Circuit().h(0).cnot(0,1)` + //ltnln change: from gate.AmazonBraketName -> gate.symbolAmazonBraket + let circuit = this.operations.reduce( function( string, operation ){ + let awsGate = operation.gate.symbolAmazonBraket + isValidBraketCircuit &= awsGate !== undefined + if( operation.gate.symbolAmazonBraket === undefined ) isValidBraketCircuit = false + if( operation.gate.symbol === 'X' ) { + if( operation.registerIndices.length === 1 ) awsGate = operation.gate.symbolAmazonBraket + else if( operation.registerIndices.length === 2 ) awsGate = 'cnot' + else if( operation.registerIndices.length === 3) awsGate = 'ccnot' + else isValidBraketCircuit = false + } + + else if( operation.gate.symbol === 'S' ) { + if( operation.gate.parameters["phi"] === 0 ) { + awsGate = operation.registerIndices.length == 2 ? awsGate : "cswap" + return string +'.'+ awsGate +'(' + + operation.registerIndices.reduce( function( string, registerIndex, r ){ + + return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) + + }, '' ) + ')' + } + awsGate = 'pswap' + } + //ltnln note: removed the if( operation.gate.symbol == '*') branch as it should be covered by + //the inclusion of the CURSOR gate. + else if( ['Y','Z','P'].includes( operation.gate.symbol) ) { + if( operation.registerIndices.length === 1) awsGate = operation.gate.symbolAmazonBraket + else if( operation.registerIndices.length === 2 ) awsGate = (operation.gate.symbol === 'Y') ? 'cy' : (operation.gate.symbol === 'Z') ? 'cz' : 'cphaseshift' + else isValidBraketCircuit = false + } + //for all unitary gates, there must be a line of code to initialize the matrix for use + //in Braket's .u(matrix=my_unitary, targets[0]) function + else if( operation.gate.symbol === 'U') { + //check that this truly works as a unique id + isValidBraketCircuit &= operation.registerIndices.length === 1 + const new_matrix = `unitary_` + num_unitaries + num_unitaries++ + //https://en.wikipedia.org/wiki/Unitary_matrix; source for the unitary matrix values implemented below. + const a = ComplexNumber.toText(Math.cos(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), + Math.sin(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2)) + .replace('i', 'j') + const b = ComplexNumber.toText(-Math.cos(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), + -Math.sin(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2) + .replace('i', 'j') + const c = ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), + -Math.sin((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2) + .replace('i', 'j') + const d = ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), + Math.sin((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2)) / 2) + .replace('i', 'j') + variables += new_matrix + ` = np.array(` + + `[[` + a + ', ' + b + `],`+ + `[` + c + ', ' + d + `]])\n` + return string +'.'+ awsGate +'(' + new_matrix +','+ + operation.registerIndices.reduce( function( string, registerIndex, r ){ + + return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) + + }, '' ) + ')' + } + // I believe this line should ensure that we don't include any controlled single-qubit gates that aren't allowed in Braket. + // The registerIndices.length > 1 technically shouldn't be necessary, but if changes are made later, it's just for safety. + else isValidBraketCircuit &= (operation.registerIndices.length === 1) || ( operation.registerIndices.length > 1 && operation.gate.is_multi_qubit ) + return string +'.'+ awsGate +'(' + + operation.registerIndices.reduce( function( string, registerIndex, r ){ + + return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) + + }, '' ) + ((operation.gate.has_parameters) ? + Object.values( operation.gate.parameters ).reduce( function( string, parameter ) { + return string + "," + parameter + }, '') + : '') + ')' + + }, 'qjs_circuit = Circuit()' ) + variables += '\n' + if( this.operations.length === 0 ) circuit += '.i(0)'// Quick fix to avoid an error here! + + const footer = `\n\ntask = device.run(qjs_circuit, s3_folder, shots=100) +print(task.result().measurement_counts)` + return isValidBraketCircuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###` + }, + toLatex: function(){ + + /* + + \Qcircuit @C=1em @R=.7em { + & \ctrl{2} & \targ & \gate{U} & \qw \\ + & \qw & \ctrl{-1} & \qw & \qw \\ + & \targ & \ctrl{-1} & \ctrl{-2} & \qw \\ + & \qw & \ctrl{-1} & \qw & \qw + } + + No "&"" means it’s an input. So could also do this: + \Qcircuit @C=1.4em @R=1.2em { + + a & i \\ + 1 & x + } + */ + + return '\\Qcircuit @C=1.0em @R=0.7em {\n' + + this.toTable() + .reduce( function( array, moment, m ){ + + moment.forEach( function( operation, o, operations ){ + + let command = 'qw' + if( operation.symbol !== 'I' ){ + + if( operation.isMultiRegisterOperation ){ + + if( operation.indexAmongSiblings === 0 ){ + + if( operation.symbol === 'X' ) command = 'targ' + else command = operation.symbol.toLowerCase() + } + else if( operation.indexAmongSiblings > 0 ) command = 'ctrl{?}' + } + else command = operation.symbol.toLowerCase() + } + operations[ o ].latexCommand = command + }) + const maximumCharacterWidth = moment.reduce( function( maximumCharacterWidth, operation ){ + + return Math.max( maximumCharacterWidth, operation.latexCommand.length ) + + }, 0 ) + moment.forEach( function( operation, o ){ + + array[ o ] += '& \\'+ operation.latexCommand.padEnd( maximumCharacterWidth ) +' ' + }) + return array + + }, new Array( this.bandwidth ).fill( '\n\t' )) + .join( '\\\\' ) + + '\n}' + }, + + + + + + + ////////////// + // // + // Edit // + // // + ////////////// + + + get: function( momentIndex, registerIndex ){ + + return this.operations.find( function( op ){ + + return op.momentIndex === momentIndex && + op.registerIndices.includes( registerIndex ) + }) + }, + clear$: function( momentIndex, registerIndices ){ + + const circuit = this + + + // Validate our arguments. + + if( arguments.length !== 2 ) + logger.warn( `Circuit.clear$ expected 2 arguments but received ${ arguments.length }.` ) + if( mathf.isUsefulInteger( momentIndex ) !== true ) + return logger.error( `Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid moment index:`, momentIndex ) + if( mathf.isUsefulInteger( registerIndices )) registerIndices = [ registerIndices ] + if( registerIndices instanceof Array !== true ) + return logger.error( `Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid register indices array:`, registerIndices ) + + + // Let’s find any operations + // with a footprint at this moment index and one of these register indices + // and collect not only their content, but their index in the operations array. + // (We’ll need that index to splice the operations array later.) + + const foundOperations = circuit.operations.reduce( function( filtered, operation, o ){ + + if( operation.momentIndex === momentIndex && + operation.registerIndices.some( function( registerIndex ){ + + return registerIndices.includes( registerIndex ) + }) + ) filtered.push({ + + index: o, + momentIndex: operation.momentIndex, + registerIndices: operation.registerIndices, + gate: operation.gate + }) + return filtered + + }, [] ) + + + // Because we held on to each found operation’s index + // within the circuit’s operations array + // we can now easily splice them out of the array. + + foundOperations.reduce( function( deletionsSoFar, operation ){ + + circuit.operations.splice( operation.index - deletionsSoFar, 1 ) + return deletionsSoFar + 1 + + }, 0 ) + + + // IMPORTANT! + // Operations must be sorted properly + // for toTable to work reliably with + // multi-register operations!! + + this.sort$() + + + // Let’s make history. + + if( foundOperations.length ){ + + this.history.record$({ + + redo: { + + name: 'clear$', + func: circuit.clear$, + args: Array.from( arguments ) + }, + undo: foundOperations.reduce( function( undos, operation ){ + + undos.push({ + + name: 'set$', + func: circuit.set$, + args: [ + + operation.gate, + operation.momentIndex, + operation.registerIndices + ] + }) + return undos + + }, [] ) + }) + + + // Let anyone listening, + // including any circuit editor interfaces, + // know about what we’ve just completed here. + + foundOperations.forEach( function( operation ){ + + misc.dispatchCustomEventToGlobal( + + 'Circuit.clear$', { detail: { + + circuit, + momentIndex, + registerIndices: operation.registerIndices + }} + ) + }) + } + + + // Enable that “fluent interface” method chaining :) + + return circuit + }, + + + setProperty$: function( key, value ){ + + this[ key ] = value + return this + }, + setName$: function( name ){ + + if( typeof name === 'function' ) name = name() + return this.setProperty$( 'name', name ) + }, + + + set$: function( gate, momentIndex, registerIndices, parameters = {} ){ + + const circuit = this + + // Is this a valid gate? + // We clone the gate rather than using the constant; this way, if we change it's parameters, we don't change the constant. + if( typeof gate === 'string' ) gate = Gate.prototype.clone( Gate.findBySymbol( gate ) ) + if( gate instanceof Gate !== true ) return logger.error( `Circuit attempted to add a gate (${ gate }) to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate ) + + + // Is this a valid moment index? + + if( mathf.isUsefulNumber( momentIndex ) !== true || + Number.isInteger( momentIndex ) !== true || + momentIndex < 1 || momentIndex > this.timewidth ){ + + return logger.error( `Circuit attempted to add a gate to circuit #${ this.index } at a moment index that is not valid:`, momentIndex ) + } + + + // Are these valid register indices? + + if( typeof registerIndices === 'number' ) registerIndices = [ registerIndices ] + if( registerIndices instanceof Array !== true ) return logger.error( `Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an invalid register indices array:`, registerIndices ) + if( registerIndices.length === 0 ) return logger.error( `Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with an empty register indices array:`, registerIndices ) + if( registerIndices.reduce( function( accumulator, registerIndex ){ + + // console.log(accumulator && + // registerIndex > 0 && + // registerIndex <= circuit.bandwidth) + return ( + + accumulator && + registerIndex > 0 && + registerIndex <= circuit.bandwidth + ) + + }, false )){ + + return logger.warn( `Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with some out of range qubit indices:`, registerIndices ) + } + + + // Ok, now we can check if this set$ command + // is redundant. + + const + isRedundant = !!circuit.operations.find( function( operation ){ + + return ( + + momentIndex === operation.momentIndex && + gate === operation.gate && + registerIndices.length === operation.registerIndices.length && + registerIndices.every( val => operation.registerIndices.includes( val )) + ) + }) + + + // If it’s NOT redundant + // then we’re clear to proceed. + + if( isRedundant !== true ){ + + + // If there’s already an operation here, + // we’d better get rid of it! + // This will also entirely remove any multi-register operations + // that happen to have a component at this moment / register. + + this.clear$( momentIndex, registerIndices ) + + + // Finally. + // Finally we can actually set this operation. + // Aren’t you glad we handle all this for you? + + const + //TODO: For ltnln (have to fix) + // a) allow users to control whatever they want! Just because it's not allowed in Braket + // doesn't mean they shouldn't be allowed to do it in Q! (Probably fixable by adjusting toAmazonBraket) + // b) Controlling a multi_qubit gate will not treat the control icon like a control gate! + isControlled = registerIndices.length > 1 && gate !== Gate.SWAP && gate.can_be_controlled !== undefined + operation = { + + gate, + momentIndex, + registerIndices, + isControlled + } + //perform parameter update here!!! + if(gate.has_parameters) gate.updateMatrix$.apply( gate, Object.values(parameters) ) + this.operations.push( operation ) + + + // IMPORTANT! + // Operations must be sorted properly + // for toTable to work reliably with + // multi-register operations!! + + this.sort$() + + + // Let’s make history. + const redo_args = Array.from( arguments ) + Object.assign( redo_args[ redo_args.length - 1 ], parameters ) + this.history.record$({ + + redo: { + + name: 'set$', + func: circuit.set$, + args: redo_args + }, + undo: [{ + + name: 'clear$', + func: circuit.clear$, + args: [ momentIndex, registerIndices ] + }] + }) + + + // Emit an event that we have set an operation + // on this circuit. + + misc.dispatchCustomEventToGlobal( + + 'Circuit.set$', { detail: { + + circuit, + operation + }} + ) + } + return circuit + }, + + + + + determineRanges: function( options ){ + + if( options === undefined ) options = {} + let { + + qubitFirstIndex, + qubitRange, + qubitLastIndex, + momentFirstIndex, + momentRange, + momentLastIndex + + } = options + + if( typeof qubitFirstIndex !== 'number' ) qubitFirstIndex = 0 + if( typeof qubitLastIndex !== 'number' && typeof qubitRange !== 'number' ) qubitLastIndex = this.bandwidth + if( typeof qubitLastIndex !== 'number' && typeof qubitRange === 'number' ) qubitLastIndex = qubitFirstIndex + qubitRange + else if( typeof qubitLastIndex === 'number' && typeof qubitRange !== 'number' ) qubitRange = qubitLastIndex - qubitFirstIndex + else return logger.error( `Circuit attempted to copy a circuit but could not understand what qubits to copy.` ) + + if( typeof momentFirstIndex !== 'number' ) momentFirstIndex = 0 + if( typeof momentLastIndex !== 'number' && typeof momentRange !== 'number' ) momentLastIndex = this.timewidth + if( typeof momentLastIndex !== 'number' && typeof momentRange === 'number' ) momentLastIndex = momentFirstIndex + momentRange + else if( typeof momentLastIndex === 'number' && typeof momentRange !== 'number' ) momentRange = momentLastIndex - momentFirstIndex + else return logger.error( `Circuit attempted to copy a circuit but could not understand what moments to copy.` ) + + logger.log( 0.8, + + '\nCircuit copy operation:', + '\n\n qubitFirstIndex', qubitFirstIndex, + '\n qubitLastIndex ', qubitLastIndex, + '\n qubitRange ', qubitRange, + '\n\n momentFirstIndex', momentFirstIndex, + '\n momentLastIndex ', momentLastIndex, + '\n momentRange ', momentRange, + '\n\n' + ) + + return { + + qubitFirstIndex, + qubitRange, + qubitLastIndex, + momentFirstIndex, + momentRange, + momentLastIndex + } + }, + + + copy: function( options, isACutOperation ){ + + const original = this + let { + + registerFirstIndex, + registerRange, + registerLastIndex, + momentFirstIndex, + momentRange, + momentLastIndex + + } = this.determineRanges( options ) + + const copy = new Circuit( registerRange, momentRange ) + + original.operations + .filter( function( operation ){ + + return ( operation.registerIndices.every( function( registerIndex ){ + + return ( + + operation.momentIndex >= momentFirstIndex && + operation.momentIndex < momentLastIndex && + operation.registerIndex >= registerFirstIndex && + operation.registerIndex < registerLastIndex + ) + })) + }) + .forEach( function( operation ){ + + const adjustedRegisterIndices = operation.registerIndices.map( function( registerIndex ){ + + return registerIndex - registerFirstIndex + }) + copy.set$( + + operation.gate, + 1 + m - momentFirstIndex, + adjustedRegisterIndices + ) + }) + + + // The cut$() operation just calls copy() + // with the following boolean set to true. + // If this is a cut we need to + // replace all gates in this area with identity gates. + + // UPDATE !!!! + // will come back to fix!! + // with new style it's now just a matter of + // splicing out these out of circuit.operations + + + + if( isACutOperation === true ){ + + /* + for( let m = momentFirstIndex; m < momentLastIndex; m ++ ){ + + original.moments[ m ] = new Array( original.bandwidth ) + .fill( 0 ) + .map( function( qubit, q ){ + + return { + + gate: Q.Gate.IDENTITY, + registerIndices: [ q ] + } + }) + }*/ + } + return copy + }, + cut$: function( options ){ + + return this.copy( options, true ) + }, + + + + + + + + /* + + + + + If covers all moments for 1 or more qubits then + 1. go through each moment and remove those qubits + 2. remove hanging operations. (right?? don’t want them?) + + + + + */ + + spliceCut$: function( options ){ + + let { + + qubitFirstIndex, + qubitRange, + qubitLastIndex, + momentFirstIndex, + momentRange, + momentLastIndex + + } = this.determineRanges( options ) + + + // Only three options are valid: + // 1. Selection area covers ALL qubits for a series of moments. + // 2. Selection area covers ALL moments for a seriies of qubits. + // 3. Both of the above (splice the entire circuit). + + if( qubitRange !== this.bandwidth && + momentRange !== this.timewidth ){ + + return logger.error( `Circuit attempted to splice circuit #${this.index} by an area that did not include all qubits _or_ all moments.` ) + } + + + // If the selection area covers all qubits for 1 or more moments + // then splice the moments array. + + if( qubitRange === this.bandwidth ){ + + + // We cannot use Array.prototype.splice() for this + // because we need a DEEP copy of the array + // and splice() will only make a shallow copy. + + this.moments = this.moments.reduce( function( accumulator, moment, m ){ + + if( m < momentFirstIndex - 1 || m >= momentLastIndex - 1 ) accumulator.push( moment ) + return accumulator + + }, []) + this.timewidth -= momentRange + + //@@ And how do we implement splicePaste$() here? + } + + + // If the selection area covers all moments for 1 or more qubits + // then iterate over each moment and remove those qubits. + + if( momentRange === this.timewidth ){ + + + // First, let’s splice the inputs array. + + this.inputs.splice( qubitFirstIndex, qubitRange ) + //@@ this.inputs.splice( qubitFirstIndex, qubitRange, qubitsToPaste?? ) + + + // Now we can make the proper adjustments + // to each of our moments. + + this.moments = this.moments.map( function( operations ){ + + + // Remove operations that pertain to the removed qubits. + // Renumber the remaining operations’ qubitIndices. + + return operations.reduce( function( accumulator, operation ){ + + if( operation.qubitIndices.every( function( index ){ + + return index < qubitFirstIndex || index >= qubitLastIndex + + })) accumulator.push( operation ) + return accumulator + + }, []) + .map( function( operation ){ + + operation.qubitIndices = operation.qubitIndices.map( function( index ){ + + return index >= qubitLastIndex ? index - qubitRange : index + }) + return operation + }) + }) + this.bandwidth -= qubitRange + } + + + // Final clean up. + + this.removeHangingOperations$() + this.fillEmptyOperations$() + + + return this// Or should we return the cut area?! + }, + splicePaste$: function(){ + + + }, + + + + + + // This is where “hanging operations” get interesting! + // when you paste one circuit in to another + // and that clipboard circuit has hanging operations + // those can find a home in the circuit its being pasted in to! + + + paste$: function( other, atMoment = 0, atQubit = 0, shouldClean = true ){ + + const scope = this + this.timewidth = Math.max( this.timewidth, atMoment + other.timewidth ) + this.bandwidth = Math.max( this.bandwidth, atQubit + other.bandwidth ) + this.ensureMomentsAreReady$() + this.fillEmptyOperations$() + other.moments.forEach( function( moment, m ){ + + moment.forEach( function( operation ){ + + //console.log( 'past over w this:', m + atMoment, operation ) + + scope.set$( + + operation.gate, + m + atMoment + 1, + operation.qubitIndices.map( function( qubitIndex ){ + + return qubitIndex + atQubit + }) + ) + }) + }) + if( shouldClean ) this.removeHangingOperations$() + this.fillEmptyOperations$() + return this + }, + pasteInsert$: function( other, atMoment, atQubit ){ + + // if( other.alphandwidth !== this.bandwidth && + // other.timewidth !== this.timewidth ) return error( 'Circuit attempted to pasteInsert Circuit A', other, 'in to circuit B', this, 'but neither their bandwidth or timewidth matches.' ) + + + + + if( shouldClean ) this.removeHangingOperations$() + this.fillEmptyOperations$() + return this + + }, + expand$: function(){ + + // expand either bandwidth or timewidth, fill w identity + + + this.fillEmptyOperations$() + return thiis + }, + + + + + + + + trim$: function( options ){ + + ` + Edit this circuit by trimming off moments, qubits, or both. + We could have implemented trim$() as a wrapper around copy$(), + similar to how cut$ is a wrapper around copy$(). + But this operates on the existing circuit + instead of returning a new one and returning that. + ` + + let { + + qubitFirstIndex, + qubitRange, + qubitLastIndex, + momentFirstIndex, + momentRange, + momentLastIndex + + } = this.determineRanges( options ) + + + // First, trim the moments down to desired size. + + this.moments = this.moments.slice( momentFirstIndex, momentLastIndex ) + this.timewidth = momentRange + + + // Then, trim the bandwidth down. + + this.inputs = this.inputs.slice( qubitFirstIndex, qubitLastIndex ) + this.bandwidth = qubitRange + + + // Finally, remove all gates where + // gate’s qubit indices contain an index < qubitFirstIndex, + // gate’s qubit indices contain an index > qubitLastIndex, + // and fill those holes with Identity gates. + + this.removeHangingOperations$() + this.fillEmptyOperations$() + + return this + } +}) + + + + + + + +// Against my predilection for verbose clarity... +// I offer you super short convenience methods +// that do NOT use the $ suffix to delcare they are destructive. +// Don’t shoot your foot off. +Object.entries( Gate.constants ).forEach( function( entry ){ + + const + gateConstantName = entry[ 0 ], + gate = entry[ 1 ], + set$ = function( momentIndex, registerIndexOrIndices ){ + + this.set$( gate, momentIndex, registerIndexOrIndices ) + return this + } + Circuit.prototype[ gateConstantName ] = set$ + Circuit.prototype[ gate.symbol ] = set$ + Circuit.prototype[ gate.symbol.toLowerCase() ] = set$ +}) + + + +/* +const bells = [ + + + // Verbose without shortcuts. + + new Circuit( 2, 2 ) + .set$( Q.Gate.HADAMARD, 1, [ 1 ]) + .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]), + + new Circuit( 2, 2 ) + .set$( Q.Gate.HADAMARD, 1, 1 ) + .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]), + + + // Uses Q.Gate.findBySymbol() to lookup gates. + + new Circuit( 2, 2 ) + .set$( 'H', 1, [ 1 ]) + .set$( 'X', 2, [ 1 , 2 ]), + + new Circuit( 2, 2 ) + .set$( 'H', 1, 1 ) + .set$( 'X', 2, [ 1 , 2 ]), + + + // Convenience gate functions -- constant name. + + new Circuit( 2, 2 ) + .HADAMARD( 1, [ 1 ]) + .PAULI_X( 2, [ 1, 2 ]), + + new Circuit( 2, 2 ) + .HADAMARD( 1, 1 ) + .PAULI_X( 2, [ 1, 2 ]), + + + // Convenience gate functions -- uppercase symbol. + + new Circuit( 2, 2 ) + .H( 1, [ 1 ]) + .X( 2, [ 1, 2 ]), + + new Circuit( 2, 2 ) + .H( 1, 1 ) + .X( 2, [ 1, 2 ]), + + + // Convenience gate functions -- lowercase symbol. + + new Circuit( 2, 2 ) + .h( 1, [ 1 ]) + .x( 2, [ 1, 2 ]), + + new Circuit( 2, 2 )// Perhaps the closest to Braket style. + .h( 1, 1 ) + .x( 2, [ 1, 2 ]), + + + // Q function -- bandwidth / timewidth arguments. + + Q( 2, 2 ) + .h( 1, [ 1 ]) + .x( 2, [ 1, 2 ]), + + Q( 2, 2 ) + .h( 1, 1 ) + .x( 2, [ 1, 2 ]), + + + // Q function -- text block argument + // with operation symbols + // and operation component IDs. + + Q` + H-X.0#0 + I-X.0#1`, + + + // Q function -- text block argument + // using only component IDs + // (ie. no operation symbols) + // because the operation that the + // components should belong to is NOT ambiguous. + + Q` + H-X#0 + I-X#1`, + + + // Q function -- text block argument + // as above, but using only whitespace + // to partition between moments. + + Q` + H X#0 + I X#1` +], +bellsAreEqual = !!bells.reduce( function( a, b ){ + + return a.toText() === b.toText() ? a : NaN + +}) +if( bellsAreEqual ){ + + console.log( `\n\nYES. All of ${ bells.length } our “Bell” circuits are equal.\n\n`, bells ) +} +*/ + + + + + + + +Circuit.createConstants( + + 'BELL', new Circuit.fromText(` + + H X#0 + I X#1 + `), + // 'GROVER', Q` + + // H X *#0 X#0 I X#0 I I I X#0 I I I X#0 I X H X I *#0 + // H X I X#1 *#0 X#1 *#0 X#0 I I I X#0 X I H X I I I I + // H X I I I I I X#1 *#0 X#1 *#0 X#1 *#0 X#1 I *#0 X H X I + // H X *#1 I *#1 I *#1 I *#1 I *#1 I *#1 I I *#1 X H X *#1 + // ` + + //https://docs.microsoft.com/en-us/quantum/concepts/circuits?view=qsharp-preview + // 'TELEPORT', Q.(` + + // I-I--H-M---v + // H-C0-I-M-v-v + // I-C1-I-I-X-Z- + // `) +) + + +module.exports = {Circuit}; + + +},{"./Logging":3,"./Math-Functions":4,"./Misc":5,"./Q-ComplexNumber":7,"./Q-Gate":8,"./Q-History":9,"./Q-Matrix":10,"./Q-Qubit":11}],7:[function(require,module,exports){ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +const { warn, error, help } = require('./Logging'); +const mathf = require('./Math-Functions'); +const misc = require('./Misc'); +const EPSILON = misc.constants.EPSILON; +ComplexNumber = function (real, imaginary) { + ` + The set of “real numbers” (ℝ) contains any number that can be expressed + along an infinite timeline. https://en.wikipedia.org/wiki/Real_number + + … -3 -2 -1 0 +1 +2 +3 … + ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄ + √2 𝒆 π + + + Meanwhile, “imaginary numbers” (𝕀) consist of a real (ℝ) multiplier and + the symbol 𝒊, which is the impossible solution to the equation 𝒙² = −1. + Note that no number when multiplied by itself can ever result in a + negative product, but the concept of 𝒊 gives us a way to reason around + this imaginary scenario nonetheless. + https://en.wikipedia.org/wiki/Imaginary_number + + … -3𝒊 -2𝒊 -1𝒊 0𝒊 +1𝒊 +2𝒊 +3𝒊 … + ┄───┴───┴───┴───┴───┴───┴───┴───┄ + + + A “complex number“ (ℂ) is a number that can be expressed in the form + 𝒂 + 𝒃𝒊, where 𝒂 is the real component (ℝ) and 𝒃𝒊 is the imaginary + component (𝕀). https://en.wikipedia.org/wiki/Complex_number + + + Operation functions on ComplexNumber instances generally accept as + arguments both sibling instances and pure Number instances, though the + value returned is always an instance of ComplexNumber. + + `; + + if (real instanceof ComplexNumber) { + imaginary = real.imaginary; + real = real.real; + warn( + "ComplexNumber tried to create a new instance with an argument that is already a ComplexNumber — and that’s weird!" + ); + } else if (real === undefined) real = 0; + if (imaginary === undefined) imaginary = 0; + if ( + (ComplexNumber.isNumberLike(real) !== true && isNaN(real) !== true) || + (ComplexNumber.isNumberLike(imaginary) !== true && + isNaN(imaginary) !== true) + ) + return error( + "ComplexNumber attempted to create a new instance but the arguments provided were not actual numbers." + ); + + this.real = real; + this.imaginary = imaginary; + this.index = ComplexNumber.index++; +}; + +Object.assign(ComplexNumber, { + index: 0, + help: function () { + return help(this); + }, + constants: {}, + createConstant: function (key, value) { + //Object.freeze( value ) + this[key] = value; + // Object.defineProperty( this, key, { + + // value, + // writable: false + // }) + // Object.defineProperty( this.constants, key, { + + // value, + // writable: false + // }) + this.constants[key] = this[key]; + Object.freeze(this[key]); + }, + createConstants: function () { + if (arguments.length % 2 !== 0) { + return error( + "Q attempted to create constants with invalid (KEY, VALUE) pairs." + ); + } + for (let i = 0; i < arguments.length; i += 2) { + this.createConstant(arguments[i], arguments[i + 1]); + } + }, + + toText: function (rNumber, iNumber, roundToDecimal, padPositive) { + // Should we round these numbers? + // Our default is yes: to 3 digits. + // Otherwise round to specified decimal. + + if (typeof roundToDecimal !== "number") roundToDecimal = 3; + const factor = Math.pow(10, roundToDecimal); + rNumber = Math.round(rNumber * factor) / factor; + iNumber = Math.round(iNumber * factor) / factor; + + // Convert padPositive + // from a potential Boolean + // to a String. + // If we don’t receive a FALSE + // then we’ll pad the positive numbers. + + padPositive = padPositive === false ? "" : " "; + + // We need the absolute values of each. + + let rAbsolute = Math.abs(rNumber), + iAbsolute = Math.abs(iNumber); + + // And an absolute value string. + + let rText = rAbsolute.toString(), + iText = iAbsolute.toString(); + + // Is this an IMAGINARY-ONLY number? + // Don’t worry: -0 === 0. + + if (rNumber === 0) { + if (iNumber === Infinity) return padPositive + "∞i"; + if (iNumber === -Infinity) return "-∞i"; + if (iNumber === 0) return padPositive + "0"; + if (iNumber === -1) return "-i"; + if (iNumber === 1) return padPositive + "i"; + if (iNumber >= 0) return padPositive + iText + "i"; + if (iNumber < 0) return "-" + iText + "i"; + return iText + "i"; // NaN + } + + // This number contains a real component + // and may also contain an imaginary one as well. + + if (rNumber === Infinity) rText = padPositive + "∞"; + else if (rNumber === -Infinity) rText = "-∞"; + else if (rNumber >= 0) rText = padPositive + rText; + else if (rNumber < 0) rText = "-" + rText; + + if (iNumber === Infinity) return rText + " + ∞i"; + if (iNumber === -Infinity) return rText + " - ∞i"; + if (iNumber === 0) return rText; + if (iNumber === -1) return rText + " - i"; + if (iNumber === 1) return rText + " + i"; + if (iNumber > 0) return rText + " + " + iText + "i"; + if (iNumber < 0) return rText + " - " + iText + "i"; + return rText + " + " + iText + "i"; // NaN + }, + + isNumberLike: function (n) { + return isNaN(n) === false && (typeof n === "number" || n instanceof Number); + }, + isNaN: function (n) { + return isNaN(n.real) || isNaN(n.imaginary); + }, + isZero: function (n) { + return ( + (n.real === 0 || n.real === -0) && + (n.imaginary === 0 || n.imaginary === -0) + ); + }, + isFinite: function (n) { + return isFinite(n.real) && isFinite(n.imaginary); + }, + isInfinite: function (n) { + return !(this.isNaN(n) || this.isFinite(n)); + }, + areEqual: function (a, b) { + return ComplexNumber.operate( + "areEqual", + a, + b, + function (a, b) { + return Math.abs(a - b) < EPSILON; + }, + function (a, b) { + return ( + Math.abs(a - b.real) < EPSILON && Math.abs(b.imaginary) < EPSILON + ); + }, + function (a, b) { + return ( + Math.abs(a.real - b) < EPSILON && Math.abs(a.imaginary) < EPSILON + ); + }, + function (a, b) { + return ( + Math.abs(a.real - b.real) < EPSILON && + Math.abs(a.imaginary - b.imaginary) < EPSILON + ); + } + ); + }, + + absolute: function (n) { + return mathf.hypotenuse(n.real, n.imaginary); + }, + conjugate: function (n) { + return new ComplexNumber(n.real, n.imaginary * -1); + }, + operate: function ( + name, + a, + b, + numberAndNumber, + numberAndComplex, + complexAndNumber, + complexAndComplex + ) { + if (ComplexNumber.isNumberLike(a)) { + if (ComplexNumber.isNumberLike(b)) return numberAndNumber(a, b); + else if (b instanceof ComplexNumber) return numberAndComplex(a, b); + else + return error( + "ComplexNumber attempted to", + name, + "with the number", + a, + "and something that is neither a Number or ComplexNumber:", + b + ); + } else if (a instanceof ComplexNumber) { + if (ComplexNumber.isNumberLike(b)) return complexAndNumber(a, b); + else if (b instanceof ComplexNumber) return complexAndComplex(a, b); + else + return error( + "ComplexNumber attempted to", + name, + "with the complex number", + a, + "and something that is neither a Number or ComplexNumber:", + b + ); + } else + return error( + "ComplexNumber attempted to", + name, + "with something that is neither a Number or ComplexNumber:", + a + ); + }, + + sine: function (n) { + const a = n.real, + b = n.imaginary; + + return new ComplexNumber( + Math.sin(a) * mathf.hyperbolicCosine(b), + Math.cos(a) * mathf.hyperbolicSine(b) + ); + }, + cosine: function (n) { + const a = n.real, + b = n.imaginary; + + return new ComplexNumber( + Math.cos(a) * mathf.hyperbolicCosine(b), + -Math.sin(a) * mathf.hyperbolicSine(b) + ); + }, + arcCosine: function (n) { + const a = n.real, + b = n.imaginary, + t1 = ComplexNumber.squareRoot( + new ComplexNumber(b * b - a * a + 1, a * b * -2) + ), + t2 = ComplexNumber.log(new ComplexNumber(t1.real - b, t1.imaginary + a)); + return new ComplexNumber(Math.PI / 2 - t2.imaginary, t2.real); + }, + arcTangent: function (n) { + const a = n.real, + b = n.imaginary; + + if (a === 0) { + if (b === 1) return new ComplexNumber(0, Infinity); + if (b === -1) return new ComplexNumber(0, -Infinity); + } + + const d = a * a + (1 - b) * (1 - b), + t = ComplexNumber.log( + new ComplexNumber((1 - b * b - a * a) / d, (a / d) * -2) + ); + return new ComplexNumber(t.imaginary / 2, t.real / 2); + }, + + power: function (a, b) { + if (ComplexNumber.isNumberLike(a)) a = new ComplexNumber(a); + if (ComplexNumber.isNumberLike(b)) b = new ComplexNumber(b); + + // Anything raised to the Zero power is 1. + + if (b.isZero()) return ComplexNumber.ONE; + + // Zero raised to any power is 0. + // Note: What happens if b.real is zero or negative? + // What happens if b.imaginary is negative? + // Do we really need those conditionals?? + + if (a.isZero() && b.real > 0 && b.imaginary >= 0) { + return ComplexNumber.ZERO; + } + + // If our exponent is Real (has no Imaginary component) + // then we’re really just raising to a power. + + if (b.imaginary === 0) { + if (a.real >= 0 && a.imaginary === 0) { + return new ComplexNumber(Math.pow(a.real, b.real), 0); + } else if (a.real === 0) { + // If our base is Imaginary (has no Real component). + + switch (((b.real % 4) + 4) % 4) { + case 0: + return new ComplexNumber(Math.pow(a.imaginary, b.real), 0); + case 1: + return new ComplexNumber(0, Math.pow(a.imaginary, b.real)); + case 2: + return new ComplexNumber(-Math.pow(a.imaginary, b.real), 0); + case 3: + return new ComplexNumber(0, -Math.pow(a.imaginary, b.real)); + } + } + } + + const arctangent2 = Math.atan2(a.imaginary, a.real), + logHypotenuse = mathf.logHypotenuse(a.real, a.imaginary), + x = Math.exp(b.real * logHypotenuse - b.imaginary * arctangent2), + y = b.imaginary * logHypotenuse + b.real * arctangent2; + + return new ComplexNumber(x * Math.cos(y), x * Math.sin(y)); + }, + squareRoot: function (a) { + const result = new ComplexNumber(0, 0), + absolute = ComplexNumber.absolute(a); + + if (a.real >= 0) { + if (a.imaginary === 0) { + result.real = Math.sqrt(a.real); // and imaginary already equals 0. + } else { + result.real = Math.sqrt(2 * (absolute + a.real)) / 2; + } + } else { + result.real = Math.abs(a.imaginary) / Math.sqrt(2 * (absolute - a.real)); + } + if (a.real <= 0) { + result.imaginary = Math.sqrt(2 * (absolute - a.real)) / 2; + } else { + result.imaginary = + Math.abs(a.imaginary) / Math.sqrt(2 * (absolute + a.real)); + } + if (a.imaginary < 0) result.imaginary *= -1; + return result; + }, + log: function (a) { + return new ComplexNumber( + mathf.logHypotenuse(a.real, a.imaginary), + Math.atan2(a.imaginary, a.real) + ); + }, + multiply: function (a, b) { + return ComplexNumber.operate( + "multiply", + a, + b, + function (a, b) { + return new ComplexNumber(a * b); + }, + function (a, b) { + return new ComplexNumber(a * b.real, a * b.imaginary); + }, + function (a, b) { + return new ComplexNumber(a.real * b, a.imaginary * b); + }, + function (a, b) { + // FOIL Method that shit. + // https://en.wikipedia.org/wiki/FOIL_method + + const firsts = a.real * b.real, + outers = a.real * b.imaginary, + inners = a.imaginary * b.real, + lasts = a.imaginary * b.imaginary * -1; // Because i² = -1. + + return new ComplexNumber(firsts + lasts, outers + inners); + } + ); + }, + divide: function (a, b) { + return ComplexNumber.operate( + "divide", + a, + b, + function (a, b) { + return new ComplexNumber(a / b); + }, + function (a, b) { + return new ComplexNumber(a).divide(b); + }, + function (a, b) { + return new ComplexNumber(a.real / b, a.imaginary / b); + }, + function (a, b) { + // Ermergerd I had to look this up because it’s been so long. + // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/complex-conjugates-and-dividing-complex-numbers/a/dividing-complex-numbers-review + + const conjugate = b.conjugate(), + numerator = a.multiply(conjugate), + // The .imaginary will be ZERO for sure, + // so this forces a ComplexNumber.divide( Number ) ;) + + denominator = b.multiply(conjugate).real; + + return numerator.divide(denominator); + } + ); + }, + add: function (a, b) { + return ComplexNumber.operate( + "add", + a, + b, + function (a, b) { + return new ComplexNumber(a + b); + }, + function (a, b) { + return new ComplexNumber(b.real + a, b.imaginary); + }, + function (a, b) { + return new ComplexNumber(a.real + b, a.imaginary); + }, + function (a, b) { + return new ComplexNumber(a.real + b.real, a.imaginary + b.imaginary); + } + ); + }, + subtract: function (a, b) { + return ComplexNumber.operate( + "subtract", + a, + b, + function (a, b) { + return new ComplexNumber(a - b); + }, + function (a, b) { + return new ComplexNumber(b.real - a, b.imaginary); + }, + function (a, b) { + return new ComplexNumber(a.real - b, a.imaginary); + }, + function (a, b) { + return new ComplexNumber(a.real - b.real, a.imaginary - b.imaginary); + } + ); + }, +}); + +ComplexNumber.createConstants( + "ZERO", + new ComplexNumber(0, 0), + "ONE", + new ComplexNumber(1, 0), + "E", + new ComplexNumber(Math.E, 0), + "PI", + new ComplexNumber(Math.PI, 0), + "I", + new ComplexNumber(0, 1), + "EPSILON", + new ComplexNumber(EPSILON, EPSILON), + "INFINITY", + new ComplexNumber(Infinity, Infinity), + "NAN", + new ComplexNumber(NaN, NaN) +); + +Object.assign(ComplexNumber.prototype, { + // NON-destructive operations. + + clone: function () { + return new ComplexNumber(this.real, this.imaginary); + }, + reduce: function () { + // Note: this *might* kill function chaining. + + if (this.imaginary === 0) return this.real; + return this; + }, + toText: function (roundToDecimal, padPositive) { + // Note: this will kill function chaining. + + return ComplexNumber.toText( + this.real, + this.imaginary, + roundToDecimal, + padPositive + ); + }, + + isNaN: function (n) { + return ComplexNumber.isNaN(this); // Returned boolean will kill function chaining. + }, + isZero: function (n) { + return ComplexNumber.isZero(this); // Returned boolean will kill function chaining. + }, + isFinite: function (n) { + return ComplexNumber.isFinite(this); // Returned boolean will kill function chaining. + }, + isInfinite: function (n) { + return ComplexNumber.isInfinite(this); // Returned boolean will kill function chaining. + }, + isEqualTo: function (b) { + return ComplexNumber.areEqual(this, b); // Returned boolean will kill function chaining. + }, + + absolute: function () { + return ComplexNumber.absolute(this); // Returned number will kill function chaining. + }, + conjugate: function () { + return ComplexNumber.conjugate(this); + }, + + power: function (b) { + return ComplexNumber.power(this, b); + }, + squareRoot: function () { + return ComplexNumber.squareRoot(this); + }, + log: function () { + return ComplexNumber.log(this); + }, + multiply: function (b) { + return ComplexNumber.multiply(this, b); + }, + divide: function (b) { + return ComplexNumber.divide(this, b); + }, + add: function (b) { + return ComplexNumber.add(this, b); + }, + subtract: function (b) { + return ComplexNumber.subtract(this, b); + }, + + // DESTRUCTIVE operations. + + copy$: function (b) { + if (b instanceof ComplexNumber !== true) + return error( + `ComplexNumber attempted to copy something that was not a complex number in to this complex number #${this.index}.`, + this + ); + + this.real = b.real; + this.imaginary = b.imaginary; + return this; + }, + conjugate$: function () { + return this.copy$(this.conjugate()); + }, + power$: function (b) { + return this.copy$(this.power(b)); + }, + squareRoot$: function () { + return this.copy$(this.squareRoot()); + }, + log$: function () { + return this.copy$(this.log()); + }, + multiply$: function (b) { + return this.copy$(this.multiply(b)); + }, + divide$: function (b) { + return this.copy$(this.divide(b)); + }, + add$: function (b) { + return this.copy$(this.add(b)); + }, + subtract$: function (b) { + return this.copy$(this.subtract(b)); + }, +}); + +module.exports = { ComplexNumber }; + +},{"./Logging":3,"./Math-Functions":4,"./Misc":5}],8:[function(require,module,exports){ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +const mathf = require('./Math-Functions'); +const logger = require('./Logging'); +const { ComplexNumber } = require('./Q-ComplexNumber'); +const {Matrix} = require('./Q-Matrix'); +Gate = function( params ){ + + Object.assign( this, params ) + this.index = Gate.index ++ + + if( typeof this.symbol !== 'string' ) this.symbol = '?' + if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase() + const parameters = Object.assign( {}, params.parameters ) + this.parameters = parameters + + // We use symbols as unique identifiers + // among gate CONSTANTS + // so if you use the same symbol for a non-constant + // that’s not a deal breaker + // but it is good to know. + + const + scope = this, + foundConstant = Object + .values( Gate.constants ) + .find( function( gate ){ + + return gate.symbol === scope.symbol + }) + + if( foundConstant ){ + + logger.warn( `Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant ) + } + + if( typeof this.name !== 'string' ) this.name = 'Unknown' + if( typeof this.nameCss !== 'string' ) this.nameCss = 'unknown' + + + // If our gate’s matrix is to be + // dynamically created or updated + // then we ouoght to do that now. + + if( typeof this.updateMatrix$ === 'function' ) this.updateMatrix$() + + + // Every gate must have an applyToQubit method. + // If it doesn’t exist we’ll create one + // based on whether a matrix property exists or not. + + //Hi there. LTNLN here. We're just gonna toss the applyToQubit function entirely...Gate from here on is independent of Qubit! :).. +} + + + +Object.assign( Gate, { + + index: 0, + constants: {}, + createConstant: function( key, value ){ + this[ key ] = value + this.constants[ key ] = this[ key ] + Object.freeze( this[ key ]) + }, + createConstants: function(){ + + if( arguments.length % 2 !== 0 ){ + + return logger.error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' ) + } + for( let i = 0; i < arguments.length; i += 2 ){ + + this.createConstant( arguments[ i ], arguments[ i + 1 ]) + } + }, + findBy: function( key, value ){ + + return ( + + Object + .values( Gate.constants ) + .find( function( item ){ + + if( typeof value === 'string' && + typeof item[ key ] === 'string' ){ + + return value.toLowerCase() === item[ key ].toLowerCase() + } + return value === item[ key ] + }) + ) + }, + findBySymbol: function( symbol ){ + + return Gate.findBy( 'symbol', symbol ) + }, + findByName: function( name ){ + + return Gate.findBy( 'name', name ) + } +}) + + + + +Object.assign( Gate.prototype, { + + clone: function( params ){ + + return new Gate( Object.assign( {}, this, params )) + }, + set$: function( key, value ){ + + this[ key ] = value + return this + }, + setSymbol$: function( value ){ + + return this.set$( 'symbol', value ) + } +}) + + + + +Gate.createConstants ( + + + // Operate on a single qubit. + + 'IDENTITY', new Gate({ + + symbol: 'I', + symbolAmazonBraket: 'i', + symbolSvg: '', + name: 'Identity', + nameCss: 'identity', + matrix: Matrix.IDENTITY_2X2 + }), + 'CURSOR', new Gate({ + + symbol: '*', + symbolAmazonBraket: 'i', + symbolSvg: '', + name: 'Identity', + nameCss: 'identity', + matrix: Matrix.IDENTITY_2X2 + }), + 'MEASURE', new Gate({ + + symbol: 'M', + symbolAmazonBraket: 'm', + symbolSvg: '', + name: 'Measure', + nameCss: 'measure', + matrix: Matrix.IDENTITY_2X2, + }), + 'HADAMARD', new Gate({ + + symbol: 'H', + symbolAmazonBraket: 'h', + symbolSvg: '', + name: 'Hadamard', + nameCss: 'hadamard', + matrix: new Matrix( + [ Math.SQRT1_2, Math.SQRT1_2 ], + [ Math.SQRT1_2, -Math.SQRT1_2 ]) + }), + 'PAULI_X', new Gate({ + + symbol: 'X', + symbolAmazonBraket: 'x', + symbolSvg: '', + name: 'Pauli X', + nameCss: 'pauli-x', + matrix: new Matrix( + [ 0, 1 ], + [ 1, 0 ]), + //ltnln: NOTE! can_be_controlled refers to whether or not the Braket SDK supports a controlled + //application of this gate; if we want Q to be able to support controlled gated regardless of whether + //or not Braket can, this must be changed.. + can_be_controlled: true + }, + ), + 'PAULI_Y', new Gate({ + + symbol: 'Y', + symbolAmazonBraket: 'y', + symbolSvg: '', + name: 'Pauli Y', + nameCss: 'pauli-y', + matrix: new Matrix( + [ 0, new ComplexNumber( 0, -1 )], + [ new ComplexNumber( 0, 1 ), 0 ]), + can_be_controlled: true + }, + ), + 'PAULI_Z', new Gate({ + + symbol: 'Z', + symbolAmazonBraket: 'z', + symbolSvg: '', + name: 'Pauli Z', + nameCss: 'pauli-z', + matrix: new Matrix( + [ 1, 0 ], + [ 0, -1 ]), + can_be_controlled: true + }, + ), + 'PHASE', new Gate({ + + symbol: 'P', + symbolAmazonBraket: 'phaseshift',// ltnln edit: change from 'p' to 'phaseshift' + symbolSvg: '', + name: 'Phase', + nameCss: 'phase', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ){ + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi; + this.matrix = new Matrix( + [ 1, 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] ))]) + return this + }, + can_be_controlled: true, + has_parameters: true + }), + 'PI_8', new Gate({ + + symbol: 'T', + symbolAmazonBraket: 't',// !!! Double check this !!! + symbolSvg: '', + name: 'π ÷ 8', + nameCss: 'pi8', + matrix: new Matrix( + [ 1, 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, Math.PI / 4 )) ]) + }), + 'BLOCH', new Gate({ + + symbol: 'B', + //symbolAmazonBraket: Does not exist. + symbolSvg: '', + name: 'Bloch sphere', + nameCss: 'bloch', + // applyToQubit: function( qubit ){ + + // // Create Bloch sphere visualizer instance. + // } + }), + 'RX', new Gate({ + + symbol: 'Rx', + symbolAmazonBraket: 'rx', + symbolSvg: '', + name: 'X Rotation', + nameCss: 'x-rotation', + parameters: { "phi" : Math.PI / 2 }, + updateMatrix$: function( phi ){ + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ Math.cos( this.parameters[ "phi" ] / 2 ), new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ], + [ new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 )]) + return this + }, + has_parameters: true + }), + 'RY', new Gate({ + + symbol: 'Ry', + symbolAmazonBraket: 'ry', + symbolSvg: '', + name: 'Y Rotation', + nameCss: 'y-rotation', + parameters: { "phi" : Math.PI / 2 }, + updateMatrix$: function( phi ){ + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ Math.cos( this.parameters[ "phi" ] / 2 ), -Math.sin( phi / 2 ) ], + [ Math.sin( this.parameters[ "phi" ] / 2 ), Math.cos( this.parameters[ "phi" ] / 2 )]) + return this + }, + has_parameters: true + }), + 'RZ', new Gate({ + + symbol: 'Rz', + symbolAmazonBraket: 'rz', + symbolSvg: '', + name: 'Z Rotation', + nameCss: 'z-rotation', + parameters: { "phi" : Math.PI / 2 }, + updateMatrix$: function( phi ){ + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ ComplexNumber.E.power( new ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )), 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] / 2 ))]) + return this + }, + has_parameters: true + }), + 'UNITARY', new Gate({ + + symbol: 'U', + symbolAmazonBraket: 'unitary', + symbolSvg: '', + name: 'Unitary', + nameCss: 'unitary', + //toAmazonBraket will have to use the following matrix as an argument for unitary() + parameters: { "phi" : Math.PI / 2, + "theta" : Math.PI / 2, + "lambda" : Math.PI / 2 }, + updateMatrix$: function( phi, theta, lambda ){ + + if( (mathf.isUsefulNumber( +phi ) === true) && (mathf.isUsefulNumber( +theta ) === true) && (mathf.isUsefulNumber( +lambda ) === true) ) { + this.parameters[ "phi" ] = +phi; + this.parameters[ "theta" ] = +theta; + this.parameters[ "lambda" ] = +lambda; + } + const a = ComplexNumber.multiply( + ComplexNumber.E.power( new ComplexNumber( 0, -( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 )) + const b = ComplexNumber.multiply( + ComplexNumber.E.power( new ComplexNumber( 0, -( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), -Math.sin( this.parameters[ "theta" ] / 2 )) + const c = ComplexNumber.multiply( + ComplexNumber.E.power( new ComplexNumber( 0, ( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), Math.sin( this.parameters[ "theta" ] / 2 )) + const d = ComplexNumber.multiply( + ComplexNumber.E.power( new ComplexNumber( 0, ( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 )) + this.matrix = new Matrix( + [ a, b ], + [ c, d ]) + return this + }, + has_parameters: true + }), + 'NOT1_2', new Gate({ + + symbol: 'V', + symbolAmazonBraket: 'v', + symbolSvg: '', + name: '√Not', + nameCss: 'not1-2', + matrix: new Matrix( + [ new ComplexNumber( 1, 1 ) / 2, new ComplexNumber( 1, -1 ) / 2 ], + [ new ComplexNumber( 1, -1 ) / 2, new ComplexNumber( 1, 1 ) / 2 ]) + }), + 'PI_8_Dagger', new Gate({ + + symbol: 'T†', + symbolAmazonBraket: 'ti', + symbolSvg: '', + name: 'PI_8_Dagger', + nameCss: 'pi8-dagger', + matrix: new Matrix( + [ 1, 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, -Math.PI / 4 )) ]) + }), + 'NOT1_2_Dagger', new Gate({ + + symbol: 'V†', + symbolAmazonBraket: 'vi', + symbolSvg: '', + name: '√Not_Dagger', + nameCss: 'not1-2-dagger', + matrix: new Matrix( + [ new ComplexNumber( 1, -1 ) / 2, new ComplexNumber( 1, 1 ) / 2 ], + [ new ComplexNumber( 1, 1 ) / 2, new ComplexNumber( 1, -1 ) / 2 ]) + }), + //Note that S, S_Dagger, PI_8, and PI_8_Dagger can all be implemented by applying the PHASE gate + //using certain values of phi. + //These gates are included for completeness. + 'S', new Gate({ + symbol: 'S*', //Gotta think of a better symbol name... + symbolAmazonBraket: 's', + symbolSvg: '', + name: 'π ÷ 4', + nameCss: 'pi4', + matrix: new Matrix( + [ 1, 0 ], + [ 0, new ComplexNumber( 0, 1 ) ]) + }), + 'S_Dagger', new Gate({ + + symbol: 'S†', + symbolAmazonBraket: 'si', + symbolSvg: '', + name: 'π ÷ 4 Dagger', + nameCss: 'pi4-dagger', + matrix: new Matrix( + [ 1, 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, -1 )) ]) + }), + // Operate on 2 qubits. + 'SWAP', new Gate({ + + symbol: 'S', + symbolAmazonBraket: 'swap', + symbolSvg: '', + name: 'Swap', + nameCss: 'swap', + parameters: { "phi" : 0.0 }, + updateMatrix$: function( phi ) { + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ 1, 0, 0, 0 ], + [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ], + [ 0, ComplexNumber.E.power(new ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + can_be_controlled: true, + has_parameters: true, + is_multi_qubit: true + }), + 'SWAP1_2', new Gate({ + + symbol: '√S', + //symbolAmazonBraket: !!! UNKNOWN !!! + symbolSvg: '', + name: '√Swap', + nameCss: 'swap1-2', + matrix: new Matrix( + [ 1, 0, 0, 0 ], + [ 0, new ComplexNumber( 0.5, 0.5 ), new ComplexNumber( 0.5, -0.5 ), 0 ], + [ 0, new ComplexNumber( 0.5, -0.5 ), new ComplexNumber( 0.5, 0.5 ), 0 ], + [ 0, 0, 0, 1 ]), + is_multi_qubit: true + }), + 'ISWAP', new Gate({ + + symbol: 'iS', + symbolAmazonBraket: 'iswap', + symbolSvg: '', + name: 'Imaginary Swap', + nameCss: 'iswap', + matrix: new Matrix( + [ 1, 0, 0, 0 ], + [ 0, 0, new ComplexNumber( 0, 1 ), 0 ], + [ 0, new ComplexNumber( 0, 1 ), 0, 0 ], + [ 0, 0, 0, 1 ]), + is_multi_qubit: true + }), + 'ISING-XX', new Gate({ + + symbol: 'XX', + symbolAmazonBraket: 'xx', + symbolSvg: '', + name: 'Ising XX Coupling', + nameCss: 'ising-xx-coupling', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ], + [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], + [ 0, new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], + [ new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ]) + return this + }, + is_multi_qubit: true, + has_parameters: true + }), + 'ISING-XY', new Gate({ + + symbol: 'XY', + symbolAmazonBraket: 'xy', + symbolSvg: '', + name: 'Ising XY Coupling', + nameCss: 'ising-xy-coupling', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ 1, 0, 0, 0 ], + [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], + [ 0, new ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + is_multi_qubit: true, + has_parameters: true + }), + 'ISING-YY', new Gate({ + + symbol: 'YY', + symbolAmazonBraket: 'yy', + symbolSvg: '', + name: 'Ising YY Coupling', + nameCss: 'ising-yy-coupling', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )) ], + [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], + [ 0, new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], + [ new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ]) + return this + }, + is_multi_qubit: true, + has_parameters: true + }), + 'ISING-ZZ', new Gate({ + + symbol: 'ZZ', + symbolAmazonBraket: 'zz', + symbolSvg: '', + name: 'Ising ZZ Coupling', + nameCss: 'ising-zz-coupling', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0, 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0 ], + [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0], + [ 0, 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )) ]) + return this + }, + is_multi_qubit: true, + has_parameters: true + }), + 'CPhase00', new Gate({ + + symbol: '00', //placeholder + symbolAmazonBraket: 'cphaseshift00', + symbolSvg: '', + name: 'Controlled Phase Shift 00', + nameCss: 'cphase00', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + is_multi_qubit: true, + has_parameters: true + }), + 'CPhase01', new Gate({ + + symbol: '01', //placeholder + symbolAmazonBraket: 'cphaseshift01', + symbolSvg: '', + name: 'Controlled Phase Shift 01', + nameCss: 'cphase01', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ 1, 0, 0, 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + is_multi_qubit: true, + has_parameters: true + }), + 'CPhase10', new Gate({ + + symbol: '10', //placeholder + symbolAmazonBraket: 'cphaseshift10', + symbolSvg: '', + name: 'Controlled Phase Shift 10', + nameCss: 'cphase01', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( + [ 1, 0, 0, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + is_multi_qubit: true, + has_parameters: true + }), + 'CSWAP', new Gate({ + + symbol: 'CSWAP', + symbolAmazonBraket: 'cswap', + symbolSvg: '', + name: 'Controlled Swap', + nameCss: 'controlled-swap', + matrix: new Matrix( + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1] + ) + }) + /* + + + All further gates, + such as Toffoli (CCNOT) + or Fredkin (CSWAP) + can be easily constructed + from the above gates + using Q conveniences. + + + */ +) + + + +module.exports = { Gate }; +},{"./Logging":3,"./Math-Functions":4,"./Q-ComplexNumber":7,"./Q-Matrix":10}],9:[function(require,module,exports){ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +const {dispatchCustomEventToGlobal} = require('./Misc'); + + +History = function( instance ){ + + this.instance = instance + this.entries = [[{ + + redo: {}, + undo: [{}] + }]] + this.index = 0 + this.isRecording = true +} + + + + +Object.assign( History.prototype, { + + assess: function(){ + + const instance = this.instance + if( this.index > 0 ){ + + dispatchCustomEventToGlobal( + 'History undo is capable', { detail: { instance }} + ); + } + else { + + dispatchCustomEventToGlobal( + 'History undo is depleted', { detail: { instance }} + ) + } + if( this.index + 1 < this.entries.length ){ + + dispatchCustomEventToGlobal( + 'History redo is capable', { detail: { instance }} + ) + } + else { + + dispatchCustomEventToGlobal( + 'History redo is depleted', { detail: { instance }} + ) + } + return this + }, + createEntry$: function(){ + + this.entries.splice( this.index + 1 ) + this.entries.push([]) + this.index = this.entries.length - 1 + }, + record$: function( entry ){ + + + // Are we recording this history? + // Usually, yes. + // But if our history state is “playback” + // then we will NOT record this. + + if( this.isRecording ){ + + this.entries[ this.index ].push( entry ) + this.index = this.entries.length - 1 + this.assess() + } + return this + }, + step$: function( direction ){ + + + // If we are stepping backward (undo) + // we cannot go back further than index === 0 + // which we would happen if index is already 0 + // before we subtract 1. + // Similarly, if stepping forward (redo) + // we cannot go further than index === entries.length - 1 + // which would happen if the index is already entries.length + // before we add 1. + + if( + ( direction < 0 && this.index < 1 ) || + ( direction > 0 && this.index > this.entries.length - 2 ) + ) return false + + + // Before we step backward (undo) or forward (redo) + // we need to turn OFF history recording. + + this.isRecording = false + + const + instance = this.instance, + command = direction < 0 ? 'undo' : 'redo' + + + // If we are stepping forward (redo) + // then we need to advance the history index + // BEFORE we execute. + + if( direction > 0 ) this.index ++ + + + // Take this history entry, which itself is an Array. + // It may contain several tasks. + // Put my thing down, flip and reverse it. + // .ti esrever dna pilf ,nwod gniht ym tuP + + const entry = direction > 0 ? + Array.from( this.entries[ this.index ]) : + Array.from( this.entries[ this.index ]).reverse() + + entry + .reduce( function( tasks, subentry, s ){ + + return tasks.concat( subentry[ command ]) + + }, [] ) + .forEach( function( task, i ){ + + if( typeof task.func === 'function' ){ + + task.func.apply( instance, task.args ) + } + }) + + + // If we are stepping backward (undo) + // then we decrement the history index + // AFTER the execution above. + + if( direction < 0 ) this.index -- + + + // It’s now safe to turn recording back on. + + this.isRecording = true + + + // Emit an event so the GUI or anyone else listening + // can know if we have available undo or redo commands + // based on where or index is. + + this.assess() + return true + }, + undo$: function(){ return this.step$( -1 )}, + redo$: function(){ return this.step$( 1 )}, + report: function(){ + + const argsParse = function( output, entry, i ){ + + if( i > 0 ) output += ', ' + return output + ( typeof entry === 'object' && entry.name ? entry.name : entry ) + } + return this.entries.reduce( function( output, entry, i ){ + + output += '\n\n'+ i + ' ════════════════════════════════════════'+ + entry.reduce( function( output, entry, i ){ + + output += '\n\n '+ i +' ────────────────────────────────────────\n' + if( entry.redo ){ + + output += '\n ⟳ Redo ── '+ entry.redo.name +' ' + if( entry.redo.args ) output += entry.redo.args.reduce( argsParse, '' ) + } + output += entry.undo.reduce( function( output, entry, i ){ + + output += '\n ⟲ Undo '+ i +' ── '+ entry.name +' ' + if( entry.args ) output += entry.args.reduce( argsParse, '' ) + return output + + }, '' ) + + return output + + }, '' ) + return output + + }, 'History entry cursor: '+ this.index ) + } +}) + + + +module.exports = { History }; +},{"./Misc":5}],10:[function(require,module,exports){ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +const logger = require('./Logging'); +const {ComplexNumber} = require('./Q-ComplexNumber'); + +Matrix = function () { + // We’re keeping track of how many matrices are + // actually being generated. Just curiosity. + + this.index = Matrix.index++; + + let matrixWidth = null; + + // Has Matrix been called with two numerical arguments? + // If so, we need to create an empty Matrix + // with dimensions of those values. + + if (arguments.length == 1 && ComplexNumber.isNumberLike(arguments[0])) { + matrixWidth = arguments[0]; + this.rows = new Array(matrixWidth).fill(0).map(function () { + return new Array(matrixWidth).fill(0); + }); + } else if ( + arguments.length == 2 && + ComplexNumber.isNumberLike(arguments[0]) && + ComplexNumber.isNumberLike(arguments[1]) + ) { + matrixWidth = arguments[0]; + this.rows = new Array(arguments[1]).fill(0).map(function () { + return new Array(matrixWidth).fill(0); + }); + } else { + // Matrices’ primary organization is by rows, + // which is more congruent with our written langauge; + // primarily organizated by horizontally juxtaposed glyphs. + // That means it’s easier to write an instance invocation in code + // and easier to read when inspecting properties in the console. + + let matrixWidthIsBroken = false; + this.rows = Array.from(arguments); + this.rows.forEach(function (row) { + if (row instanceof Array !== true) row = [row]; + if (matrixWidth === null) matrixWidth = row.length; + else if (matrixWidth !== row.length) matrixWidthIsBroken = true; + }); + if (matrixWidthIsBroken) + return logger.error( + `Matrix found upon initialization that matrix#${this.index} row lengths were not equal. You are going to have a bad time.`, + this + ); + } + + // But for convenience we can also organize by columns. + // Note this represents the transposed version of itself! + + const matrix = this; + this.columns = []; + for (let x = 0; x < matrixWidth; x++) { + const column = []; + for (let y = 0; y < this.rows.length; y++) { + // Since we’re combing through here + // this is a good time to convert Number to ComplexNumber! + + const value = matrix.rows[y][x]; + if (typeof value === "number") { + // console.log('Created a complex number!') + matrix.rows[y][x] = new ComplexNumber(value); + } else if (value instanceof ComplexNumber === false) { + return logger.error( + `Matrix found upon initialization that matrix#${this.index} contained non-quantitative values. A+ for creativity, but F for functionality.`, + this + ); + } + + // console.log( x, y, matrix.rows[ y ][ x ]) + + Object.defineProperty(column, y, { + get: function () { + return matrix.rows[y][x]; + }, + set: function (n) { + matrix.rows[y][x] = n; + }, + }); + } + this.columns.push(column); + } +}; + +/////////////////////////// +// // +// Static properties // +// // +/////////////////////////// + +Object.assign(Matrix, { + index: 0, + help: function () { + return logger.help(this); + }, + constants: {}, // Only holds references; an easy way to look up what constants exist. + createConstant: function (key, value) { + this[key] = value; + this.constants[key] = this[key]; + Object.freeze(this[key]); + }, + createConstants: function () { + if (arguments.length % 2 !== 0) { + return logger.error( + "Q attempted to create constants with invalid (KEY, VALUE) pairs." + ); + } + for (let i = 0; i < arguments.length; i += 2) { + this.createConstant(arguments[i], arguments[i + 1]); + } + }, + + isMatrixLike: function (obj) { + //return obj instanceof Matrix || Matrix.prototype.isPrototypeOf( obj ) + return obj instanceof this || this.prototype.isPrototypeOf(obj); + }, + isWithinRange: function (n, minimum, maximum) { + return ( + typeof n === "number" && n >= minimum && n <= maximum && n == parseInt(n) + ); + }, + getWidth: function (matrix) { + return matrix.columns.length; + }, + getHeight: function (matrix) { + return matrix.rows.length; + }, + haveEqualDimensions: function (matrix0, matrix1) { + return ( + matrix0.rows.length === matrix1.rows.length && + matrix0.columns.length === matrix1.columns.length + ); + }, + areEqual: function (matrix0, matrix1) { + if (matrix0 instanceof Matrix !== true) return false; + if (matrix1 instanceof Matrix !== true) return false; + if (Matrix.haveEqualDimensions(matrix0, matrix1) !== true) return false; + return matrix0.rows.reduce(function (state, row, r) { + return ( + state && + row.reduce(function (state, cellValue, c) { + return state && cellValue.isEqualTo(matrix1.rows[r][c]); + }, true) + ); + }, true); + }, + + createSquare: function (size, f) { + if (typeof size !== "number") size = 2; + if (typeof f !== "function") + f = function () { + return 0; + }; + const data = []; + for (let y = 0; y < size; y++) { + const row = []; + for (let x = 0; x < size; x++) { + row.push(f(x, y)); + } + data.push(row); + } + return new Matrix(...data); + }, + createZero: function (size) { + return new Matrix.createSquare(size); + }, + createOne: function (size) { + return new Matrix.createSquare(size, function () { + return 1; + }); + }, + createIdentity: function (size) { + return new Matrix.createSquare(size, function (x, y) { + return x === y ? 1 : 0; + }); + }, + + // Import FROM a format. + + from: function (format) { + if (typeof format !== "string") format = "Array"; + const f = Matrix["from" + format]; + format = format.toLowerCase(); + if (typeof f !== "function") + return logger.error( + `Matrix could not find an importer for “${format}” data.` + ); + return f; + }, + fromArray: function (array) { + return new Matrix(...array); + }, + fromXsv: function (input, rowSeparator, valueSeparator) { + ` + Ingest string data organized by row, then by column + where rows are separated by one token (default: \n) + and column values are separated by another token + (default: \t). + + `; + + if (typeof rowSeparator !== "string") rowSeparator = "\n"; + if (typeof valueSeparator !== "string") valueSeparator = "\t"; + + const inputRows = input.split(rowSeparator), + outputRows = []; + + inputRows.forEach(function (inputRow) { + inputRow = inputRow.trim(); + if (inputRow === "") return; + + const outputRow = []; + inputRow.split(valueSeparator).forEach(function (cellValue) { + outputRow.push(parseFloat(cellValue)); + }); + outputRows.push(outputRow); + }); + return new Matrix(...outputRows); + }, + fromCsv: function (csv) { + return Matrix.fromXsv(csv.replace(/\r/g, "\n"), "\n", ","); + }, + fromTsv: function (tsv) { + return Matrix.fromXsv(tsv, "\n", "\t"); + }, + fromHtml: function (html) { + return Matrix.fromXsv( + html + .replace(/\r?\n|\r||/g, "") + .replace(/<\/td>(\s*)<\/tr>/g, "") + .match(/(.*)<\/table>/i)[1], + "", + "" + ); + }, + + // Export TO a format. + + toXsv: function (matrix, rowSeparator, valueSeparator) { + return matrix.rows.reduce(function (xsv, row) { + return ( + xsv + + rowSeparator + + row.reduce(function (xsv, cell, c) { + return xsv + (c > 0 ? valueSeparator : "") + cell.toText(); + }, "") + ); + }, ""); + }, + toCsv: function (matrix) { + return Matrix.toXsv(matrix, "\n", ","); + }, + toTsv: function (matrix) { + return Matrix.toXsv(matrix, "\n", "\t"); + }, + + // Operate NON-destructive. + + add: function (matrix0, matrix1) { + if ( + Matrix.isMatrixLike(matrix0) !== true || + Matrix.isMatrixLike(matrix1) !== true + ) { + return logger.error( + `Matrix attempted to add something that was not a matrix.` + ); + } + if (Matrix.haveEqualDimensions(matrix0, matrix1) !== true) + return logger.error( + `Matrix cannot add matrix#${matrix0.index} of dimensions ${matrix0.columns.length}x${matrix0.rows.length} to matrix#${matrix1.index} of dimensions ${matrix1.columns.length}x${matrix1.rows.length}.` + ); + + return new Matrix( + ...matrix0.rows.reduce(function (resultMatrixRow, row, r) { + resultMatrixRow.push( + row.reduce(function (resultMatrixColumn, cellValue, c) { + // resultMatrixColumn.push( cellValue + matrix1.rows[ r ][ c ]) + resultMatrixColumn.push(cellValue.add(matrix1.rows[r][c])); + return resultMatrixColumn; + }, []) + ); + return resultMatrixRow; + }, []) + ); + }, + multiplyScalar: function (matrix, scalar) { + if (Matrix.isMatrixLike(matrix) !== true) { + return logger.error( + `Matrix attempted to scale something that was not a matrix.` + ); + } + if (typeof scalar !== "number") { + return logger.error( + `Matrix attempted to scale this matrix#${matrix.index} by an invalid scalar: ${scalar}.` + ); + } + return new Matrix( + ...matrix.rows.reduce(function (resultMatrixRow, row) { + resultMatrixRow.push( + row.reduce(function (resultMatrixColumn, cellValue) { + // resultMatrixColumn.push( cellValue * scalar ) + resultMatrixColumn.push(cellValue.multiply(scalar)); + return resultMatrixColumn; + }, []) + ); + return resultMatrixRow; + }, []) + ); + }, + multiply: function (matrix0, matrix1) { + ` + Two matrices can be multiplied only when + the number of columns in the first matrix + equals the number of rows in the second matrix. + Reminder: Matrix multiplication is not commutative + so the order in which you multiply matters. + + + SEE ALSO + + https://en.wikipedia.org/wiki/Matrix_multiplication + `; + + if ( + Matrix.isMatrixLike(matrix0) !== true || + Matrix.isMatrixLike(matrix1) !== true + ) { + return logger.error( + `Matrix attempted to multiply something that was not a matrix.` + ); + } + if (matrix0.columns.length !== matrix1.rows.length) { + return logger.error( + `Matrix attempted to multiply Matrix#${matrix0.index}(cols==${matrix0.columns.length}) by Matrix#${matrix1.index}(rows==${matrix1.rows.length}) but their dimensions were not compatible for this.` + ); + } + const resultMatrix = []; + matrix0.rows.forEach(function (matrix0Row) { + // Each row of THIS matrix + + const resultMatrixRow = []; + matrix1.columns.forEach(function (matrix1Column) { + // Each column of OTHER matrix + + const sum = new ComplexNumber(); + matrix1Column.forEach(function (matrix1CellValue, index) { + // Work down the column of OTHER matrix + + sum.add$(matrix0Row[index].multiply(matrix1CellValue)); + }); + resultMatrixRow.push(sum); + }); + resultMatrix.push(resultMatrixRow); + }); + //return new Matrix( ...resultMatrix ) + return new this(...resultMatrix); + }, + multiplyTensor: function (matrix0, matrix1) { + ` + https://en.wikipedia.org/wiki/Kronecker_product + https://en.wikipedia.org/wiki/Tensor_product + `; + + if ( + Matrix.isMatrixLike(matrix0) !== true || + Matrix.isMatrixLike(matrix1) !== true + ) { + return logger.error( + `Matrix attempted to tensor something that was not a matrix.` + ); + } + + const resultMatrix = [], + resultMatrixWidth = matrix0.columns.length * matrix1.columns.length, + resultMatrixHeight = matrix0.rows.length * matrix1.rows.length; + + for (let y = 0; y < resultMatrixHeight; y++) { + const resultMatrixRow = []; + for (let x = 0; x < resultMatrixWidth; x++) { + const matrix0X = Math.floor(x / matrix0.columns.length), + matrix0Y = Math.floor(y / matrix0.rows.length), + matrix1X = x % matrix1.columns.length, + matrix1Y = y % matrix1.rows.length; + + resultMatrixRow.push( + //matrix0.rows[ matrix0Y ][ matrix0X ] * matrix1.rows[ matrix1Y ][ matrix1X ] + matrix0.rows[matrix0Y][matrix0X].multiply( + matrix1.rows[matrix1Y][matrix1X] + ) + ); + } + resultMatrix.push(resultMatrixRow); + } + return new Matrix(...resultMatrix); + }, +}); + +////////////////////////////// +// // +// Prototype properties // +// // +////////////////////////////// + +Object.assign(Matrix.prototype, { + isValidRow: function (r) { + return Matrix.isWithinRange(r, 0, this.rows.length - 1); + }, + isValidColumn: function (c) { + return Matrix.isWithinRange(c, 0, this.columns.length - 1); + }, + isValidAddress: function (x, y) { + return this.isValidRow(y) && this.isValidColumn(x); + }, + getWidth: function () { + return Matrix.getWidth(this); + }, + getHeight: function () { + return Matrix.getHeight(this); + }, + + // Read NON-destructive by nature. (Except quantum reads of course! ROFL!!) + + read: function (x, y) { + ` + Equivalent to + this.columns[ x ][ y ] + or + this.rows[ y ][ x ] + but with safety checks. + `; + + if (this.isValidAddress(x, y)) return this.rows[y][x]; + return logger.error( + `Matrix could not read from cell address (x=${x}, y=${y}) in matrix#${this.index}.`, + this + ); + }, + clone: function () { + return new Matrix(...this.rows); + }, + isEqualTo: function (otherMatrix) { + return Matrix.areEqual(this, otherMatrix); + }, + + toArray: function () { + return this.rows; + }, + toXsv: function (rowSeparator, valueSeparator) { + return Matrix.toXsv(this, rowSeparator, valueSeparator); + }, + toCsv: function () { + return Matrix.toXsv(this, "\n", ","); + }, + toTsv: function () { + return Matrix.toXsv(this, "\n", "\t"); + }, + toHtml: function () { + return ( + this.rows.reduce(function (html, row) { + return ( + html + + row.reduce(function (html, cell) { + return html + "\n\t\t"; + }, "\n\t") + + "\n\t" + ); + }, "\n
" + cell.toText() + "
") + "\n
" + ); + }, + + // Write is DESTRUCTIVE by nature. Not cuz I hate ya. + + write$: function (x, y, n) { + ` + Equivalent to + this.columns[ x ][ y ] = n + or + this.rows[ y ][ x ] = n + but with safety checks. + `; + + if (this.isValidAddress(x, y)) { + if (ComplexNumber.isNumberLike(n)) n = new ComplexNumber(n); + if (n instanceof ComplexNumber !== true) + return logger.error( + `Attempted to write an invalid value (${n}) to matrix#${this.index} at x=${x}, y=${y}`, + this + ); + this.rows[y][x] = n; + return this; + } + return logger.error( + `Invalid cell address for Matrix#${this.index}: x=${x}, y=${y}`, + this + ); + }, + copy$: function (matrix) { + if (Matrix.isMatrixLike(matrix) !== true) + return logger.error( + `Matrix attempted to copy something that was not a matrix in to this matrix#${matrix.index}.`, + this + ); + + if (Matrix.haveEqualDimensions(matrix, this) !== true) + return logger.error( + `Matrix cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this matrix#${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`, + this + ); + + const that = this; + matrix.rows.forEach(function (row, r) { + row.forEach(function (n, c) { + that.rows[r][c] = n; + }); + }); + return this; + }, + fromArray$: function (array) { + return this.copy$(Matrix.fromArray(array)); + }, + fromCsv$: function (csv) { + return this.copy$(Matrix.fromCsv(csv)); + }, + fromTsv$: function (tsv) { + return this.copy$(Matrix.fromTsv(tsv)); + }, + fromHtml$: function (html) { + return this.copy$(Matrix.fromHtml(html)); + }, + + // Operate NON-destructive. + + add: function (otherMatrix) { + return Matrix.add(this, otherMatrix); + }, + multiplyScalar: function (scalar) { + return Matrix.multiplyScalar(this, scalar); + }, + multiply: function (otherMatrix) { + return Matrix.multiply(this, otherMatrix); + }, + multiplyTensor: function (otherMatrix) { + return Matrix.multiplyTensor(this, otherMatrix); + }, + + // Operate DESTRUCTIVE. + + add$: function (otherMatrix) { + return this.copy$(this.add(otherMatrix)); + }, + multiplyScalar$: function (scalar) { + return this.copy$(this.multiplyScalar(scalar)); + }, +}); + +////////////////////////// +// // +// Static constants // +// // +////////////////////////// + +Matrix.createConstants( + "IDENTITY_2X2", + Matrix.createIdentity(2), + "IDENTITY_3X3", + Matrix.createIdentity(3), + "IDENTITY_4X4", + Matrix.createIdentity(4), + + "CONSTANT0_2X2", + new Matrix([1, 1], [0, 0]), + + "CONSTANT1_2X2", + new Matrix([0, 0], [1, 1]), + + "NEGATION_2X2", + new Matrix([0, 1], [1, 0]), + + "TEST_MAP_9X9", + new Matrix( + [11, 21, 31, 41, 51, 61, 71, 81, 91], + [12, 22, 32, 42, 52, 62, 72, 82, 92], + [13, 23, 33, 43, 53, 63, 73, 83, 93], + [14, 24, 34, 44, 54, 64, 74, 84, 94], + [15, 25, 35, 45, 55, 65, 75, 85, 95], + [16, 26, 36, 46, 56, 66, 76, 86, 96], + [17, 27, 37, 47, 57, 67, 77, 87, 97], + [18, 28, 38, 48, 58, 68, 78, 88, 98], + [19, 29, 39, 49, 59, 69, 79, 89, 99] + ) +); + +module.exports = { Matrix }; +},{"./Logging":3,"./Q-ComplexNumber":7}],11:[function(require,module,exports){ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +const { Matrix } = require("./Q-Matrix"); +const { Gate } = require("./Q-Gate"); +const { ComplexNumber } = require("./Q-ComplexNumber"); +const misc = require("./Misc"); +const logger = require("./Logging"); + +Qubit = function (a, b, symbol, name) { + // If we’ve received an instance of Matrix as our first argument + // then we’ll assume there are no further arguments + // and just use that matrix as our new Qubit instance. + + if (Matrix.isMatrixLike(a) && b === undefined) { + b = a.rows[1][0]; + a = a.rows[0][0]; + } else { + // All of our internal math now uses complex numbers + // rather than Number literals + // so we’d better convert! + + if (typeof a === "number") a = new ComplexNumber(a, 0); + if (typeof b === "number") b = new ComplexNumber(b, 0); + + // If we receive undefined (or garbage inputs) + // let’s try to make it useable. + // This way we can always call Qubit with no arguments + // to make a new qubit available for computing with. + + if (a instanceof ComplexNumber !== true) a = new ComplexNumber(1, 0); + if (b instanceof ComplexNumber !== true) { + // 1 - |𝒂|² = |𝒃|² + // So this does NOT account for if 𝒃 ought to be imaginary or not. + // Perhaps for completeness we could randomly decide + // to flip the real and imaginary components of 𝒃 after this line? + + b = ComplexNumber.ONE.subtract(Math.pow(a.absolute(), 2)).squareRoot(); + } + } + + // Sanity check! + // Does this constraint hold true? |𝒂|² + |𝒃|² = 1 + + if ( + Math.pow(a.absolute(), 2) + Math.pow(b.absolute(), 2) - 1 > + misc.constants.EPSILON + ) + return logger.error( + `Qubit could not accept the initialization values of a=${a} and b=${b} because their squares do not add up to 1.` + ); + + Matrix.call(this, [a], [b]); + this.index = Qubit.index++; + + // Convenience getters and setters for this qubit’s + // controll bit and target bit. + + Object.defineProperty(this, "alpha", { + get: function () { + return this.rows[0][0]; + }, + set: function (n) { + this.rows[0][0] = n; + }, + }); + Object.defineProperty(this, "beta", { + get: function () { + return this.rows[1][0]; + }, + set: function (n) { + this.rows[1][0] = n; + }, + }); + + // Used for Dirac notation: |?⟩ + + if (typeof symbol === "string") this.symbol = symbol; + if (typeof name === "string") this.name = name; + if (this.symbol === undefined || this.name === undefined) { + const found = Object.values(Qubit.constants).find(function (qubit) { + return a.isEqualTo(qubit.alpha) && b.isEqualTo(qubit.beta); + }); + if (found === undefined) { + this.symbol = "?"; + this.name = "Unnamed"; + } else { + if (this.symbol === undefined) this.symbol = found.symbol; + if (this.name === undefined) this.name = found.name; + } + } +}; +//Qubit inherits from Matrix. +Qubit.prototype = Object.create(Matrix.prototype); +Qubit.prototype.constructor = Qubit; + +Object.assign(Qubit, { + index: 0, + help: function () { + return logger.help(this); + }, + constants: {}, + createConstant: function (key, value) { + this[key] = value; + this.constants[key] = this[key]; + Object.freeze(this[key]); + }, + createConstants: function () { + if (arguments.length % 2 !== 0) { + return logger.error( + "Q attempted to create constants with invalid (KEY, VALUE) pairs." + ); + } + for (let i = 0; i < arguments.length; i += 2) { + this.createConstant(arguments[i], arguments[i + 1]); + } + }, + + findBy: function (key, value) { + return Object.values(Qubit.constants).find(function (item) { + if (typeof value === "string" && typeof item[key] === "string") { + return value.toLowerCase() === item[key].toLowerCase(); + } + return value === item[key]; + }); + }, + findBySymbol: function (symbol) { + return Qubit.findBy("symbol", symbol); + }, + findByName: function (name) { + return Qubit.findBy("name", name); + }, + findByBeta: function (beta) { + if (beta instanceof ComplexNumber === false) { + beta = new ComplexNumber(beta); + } + return Object.values(Qubit.constants).find(function (qubit) { + return qubit.beta.isEqualTo(beta); + }); + }, + areEqual: function (qubit0, qubit1) { + return ( + qubit0.alpha.isEqualTo(qubit1.alpha) && qubit0.beta.isEqualTo(qubit1.beta) + ); + }, + collapse: function (qubit) { + const alpha2 = Math.pow(qubit.alpha.absolute(), 2), + beta2 = Math.pow(qubit.beta.absolute(), 2), + randomNumberRange = Math.pow(2, 32) - 1, + randomNumber = new Uint32Array(1); + + // console.log( 'alpha^2', alpha2 ) + // console.log( 'beta^2', beta2 ) + window.crypto.getRandomValues(randomNumber); + const randomNumberNormalized = randomNumber / randomNumberRange; + if (randomNumberNormalized <= alpha2) { + return new Qubit(1, 0); + } else return new Qubit(0, 1); + }, + applyGate: function (qubit, gate, ...args) { + //TODO test...currently you're updating the gate's matrix property rather than returning a separate instance of the matrix. + //this is okay if "gate" is not one of the constants. otherwise, it's bad. + ` + This is means of inverting what comes first: + the Gate or the Qubit? + If the Gate only operates on a single qubit, + then it doesn’t matter and we can do this: + `; + + if (gate instanceof Gate === false) + return logger.error( + `Qubit attempted to apply something that was not a gate to this qubit #${qubit.index}.` + ); + if (gate == Gate.findBy(gate.symbol)) + return logger.error(`Qubit attempted to apply a reference to the gate constant ${gate.symbol} rather than + a copy. This is disallowed.`); + else { + gate.updateMatrix$(...args); + return new Qubit(gate.matrix.multiply(qubit)); + } + }, + toText: function (qubit) { + //return `|${qubit.beta.toText()}⟩` + return qubit.alpha.toText() + "\n" + qubit.beta.toText(); + }, + toStateVectorText: function (qubit) { + return `|${qubit.beta.toText()}⟩`; + }, + toStateVectorHtml: function (qubit) { + return `${qubit.beta.toText()}`; + }, + + // This code was a pain in the ass to figure out. + // I’m not fluent in trigonometry + // and none of the quantum primers actually lay out + // how to convert arbitrary qubit states + // to Bloch Sphere representation. + // Oh, they provide equivalencies for specific states, sure. + // I hope this is useful to you + // unless you are porting this to a terrible language + // like C# or Java or something ;) + + toBlochSphere: function (qubit) { + ` + Based on this qubit’s state return the + Polar angle θ (theta), + azimuth angle ϕ (phi), + Bloch vector, + corrected surface coordinate. + + https://en.wikipedia.org/wiki/Bloch_sphere + `; + + // Polar angle θ (theta). + + const theta = ComplexNumber.arcCosine(qubit.alpha).multiply(2); + if (isNaN(theta.real)) theta.real = 0; + if (isNaN(theta.imaginary)) theta.imaginary = 0; + + // Azimuth angle ϕ (phi). + + const phi = ComplexNumber.log( + qubit.beta.divide(ComplexNumber.sine(theta.divide(2))) + ).divide(ComplexNumber.I); + if (isNaN(phi.real)) phi.real = 0; + if (isNaN(phi.imaginary)) phi.imaginary = 0; + + // Bloch vector. + + const vector = { + x: ComplexNumber.sine(theta).multiply(ComplexNumber.cosine(phi)).real, + y: ComplexNumber.sine(theta).multiply(ComplexNumber.sine(phi)).real, + z: ComplexNumber.cosine(theta).real, + }; + + // Bloch vector’s axes are wonked. + // Let’s “correct” them for use with Three.js, etc. + + const position = { + x: vector.y, + y: vector.z, + z: vector.x, + }; + + return { + // Wow does this make tweening easier down the road. + + alphaReal: qubit.alpha.real, + alphaImaginary: qubit.alpha.imaginary, + betaReal: qubit.beta.real, + betaImaginary: qubit.beta.imaginary, + + // Ummm... I’m only returnig the REAL portions. Please forgive me! + + theta: theta.real, + phi: phi.real, + vector, // Wonked YZX vector for maths because maths. + position, // Un-wonked XYZ for use by actual 3D engines. + }; + }, + fromBlochVector: function (x, y, z) { + //basically from a Pauli Rotation + }, +}); + +Qubit.createConstants( + // Opposing pairs: + // |H⟩ and |V⟩ + // |D⟩ and |A⟩ + // |R⟩ and |L⟩ + + "HORIZONTAL", + new Qubit(1, 0, "H", "Horizontal"), // ZERO. + "VERTICAL", + new Qubit(0, 1, "V", "Vertical"), // ONE. + "DIAGONAL", + new Qubit(Math.SQRT1_2, Math.SQRT1_2, "D", "Diagonal"), + "ANTI_DIAGONAL", + new Qubit(Math.SQRT1_2, -Math.SQRT1_2, "A", "Anti-diagonal"), + "RIGHT_HAND_CIRCULAR_POLARIZED", + new Qubit( + Math.SQRT1_2, + new ComplexNumber(0, -Math.SQRT1_2), + "R", + "Right-hand Circular Polarized" + ), // RHCP + "LEFT_HAND_CIRCULAR_POLARIZED", + new Qubit( + Math.SQRT1_2, + new ComplexNumber(0, Math.SQRT1_2), + "L", + "Left-hand Circular Polarized" + ) // LHCP +); + +Object.assign(Qubit.prototype, { + copy$: function (matrix) { + if (Matrix.isMatrixLike(matrix) !== true) + return logger.error( + `Qubit attempted to copy something that was not a matrix in this qubit #${qubit.index}.`, + this + ); + + if (Matrix.haveEqualDimensions(matrix, this) !== true) + return logger.error( + `Qubit cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this qubit #${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`, + this + ); + + const that = this; + matrix.rows.forEach(function (row, r) { + row.forEach(function (n, c) { + that.rows[r][c] = n; + }); + }); + this.dirac = matrix.dirac; + return this; + }, + clone: function () { + return new Qubit(this.alpha, this.beta); + }, + isEqualTo: function (otherQubit) { + return Qubit.areEqual(this, otherQubit); // Returns a Boolean, breaks function chaining! + }, + collapse: function () { + return Qubit.collapse(this); + }, + applyGate: function (gate, ...args) { + return Qubit.applyGate(this, gate, ...args); + }, + toText: function () { + return Qubit.toText(this); // Returns a String, breaks function chaining! + }, + toStateVectorText: function () { + return Qubit.toStateVectorText(this); // Returns a String, breaks function chaining! + }, + toStateVectorHtml: function () { + return Qubit.toStateVectorHtml(this); // Returns a String, breaks function chaining! + }, + toBlochSphere: function () { + return Qubit.toBlochSphere(this); // Returns an Object, breaks function chaining! + }, + collapse$: function () { + return this.copy$(Qubit.collapse(this)); + }, + applyGate$: function (gate) { + return this.copy$(Qubit.applyGate(this, gate)); + }, +}); + +module.exports = { Qubit }; + +},{"./Logging":3,"./Misc":5,"./Q-ComplexNumber":7,"./Q-Gate":8,"./Q-Matrix":10}],12:[function(require,module,exports){ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +const misc = require('./Misc'); +const mathf = require('./Math-Functions'); +const {Circuit} = require('./Q-Circuit'); + + + +Q = function () { + // Did we send arguments of the form + // ( bandwidth, timewidth )? + + if ( + arguments.length === 2 && + Array.from(arguments).every(function (argument) { + return mathf.isUsefulInteger(argument); + }) + ) { + return new Circuit(arguments[0], arguments[1]); + } + + // Otherwise assume we are creating a circuit + // from a text block. + + return Circuit.fromText(arguments[0]); +}; + + +console.log(` + + + QQQQQQ +QQ QQ +QQ QQ +QQ QQ +QQ QQ QQ +QQ QQ + QQQQ ${misc.constants.REVISION} + + + +https://quantumjavascript.app + + + +`); + +module.exports = {Q}; + + +},{"./Math-Functions":4,"./Misc":5,"./Q-Circuit":6}],13:[function(require,module,exports){ +const logger = require('./Logging'); +const misc = require('./Misc'); +const mathf = require('./Math-Functions'); +const {ComplexNumber} = require('./Q-ComplexNumber'); +const {Gate} = require('./Q-Gate'); +const {Qubit} = require('./Q-Qubit'); +const {Matrix} = require('./Q-Matrix'); +const {History} = require('./Q-History'); +const {Circuit} = require('./Q-Circuit'); +const {Q} = require('./Q.js'); + +module.exports = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q}; + +},{"./Logging":3,"./Math-Functions":4,"./Misc":5,"./Q-Circuit":6,"./Q-ComplexNumber":7,"./Q-Gate":8,"./Q-History":9,"./Q-Matrix":10,"./Q-Qubit":11,"./Q.js":12}],14:[function(require,module,exports){ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + + + +const {Qubit} = require('quantum-js-util'); +BlochSphere = function( onValueChange ){ + + Object.assign( this, { + + isRotating: false, + radius: 1, + radiusSafe: 1.01, + axesLineWidth: 0.01, + arcLineWidth: 0.015, + state: Qubit.LEFT_HAND_CIRCULAR_POLARIZED.toBlochSphere(), + target: Qubit.HORIZONTAL.toBlochSphere(), + group: new THREE.Group(), + onValueChange + }) + + + // Create the surface of the Bloch sphere. + + const surface = new THREE.Mesh( + + new THREE.SphereGeometry( this.radius, 64, 64 ), + new THREE.MeshPhongMaterial({ + + side: THREE.FrontSide, + map: BlochSphere.makeSurface(), + transparent: true, + opacity: 0.97 + }) + ) + surface.receiveShadow = true + this.group.add( surface ) + + + + + // Create the X, Y, and Z axis lines. + + const + xAxis = new THREE.Mesh( + + new THREE.BoxGeometry( this.axesLineWidth, this.axesLineWidth, this.radius * 2.5 ), + new THREE.MeshBasicMaterial({ color: BlochSphere.xAxisColor }) + ), + yAxis = new THREE.Mesh( + + new THREE.BoxGeometry( this.radius * 2.5, this.axesLineWidth, this.axesLineWidth ), + new THREE.MeshBasicMaterial({ color: BlochSphere.yAxisColor }) + ), + zAxis = new THREE.Mesh( + + new THREE.BoxGeometry( this.axesLineWidth, this.radius * 2.5, this.axesLineWidth ), + new THREE.MeshBasicMaterial({ color: BlochSphere.zAxisColor }) + ) + + this.group.add( xAxis, yAxis, zAxis ) + + + // Create X, Y, and Z arrow heads, + // indicating positive directions for all three. + + const + arrowLength = 0.101,// I know, weird, right? + arrowHeadLength = 0.1, + arrowHeadWidth = 0.1 + + this.group.add( new THREE.ArrowHelper( + + new THREE.Vector3( 0, 0, 1.00 ), + new THREE.Vector3( 0, 0, 1.25 ), + arrowLength, + BlochSphere.xAxisColor,// Red + arrowHeadLength, + arrowHeadWidth + )) + this.group.add( new THREE.ArrowHelper( + + new THREE.Vector3( 1.00, 0, 0 ), + new THREE.Vector3( 1.25, 0, 0 ), + arrowLength, + BlochSphere.yAxisColor,// Green + arrowHeadLength, + arrowHeadWidth + )) + this.group.add( new THREE.ArrowHelper( + + new THREE.Vector3( 0, 1.00, 0 ), + new THREE.Vector3( 0, 1.25, 0 ), + arrowLength, + BlochSphere.zAxisColor,// Blue + arrowHeadLength, + arrowHeadWidth + )) + + + // Create the X, Y, and Z axis labels. + + const + axesLabelStyle = { + + width: 128, + height: 128, + fillStyle: BlochSphere.vectorColor,//'#505962', + font: 'bold italic 64px Georgia, "Times New Roman", serif' + }, + xAxisLabel = new THREE.Sprite( + + new THREE.SpriteMaterial({ + + map: Object.assign( SurfaceText( axesLabelStyle )) + }) + ), + yAxisLabel = new THREE.Sprite( + + new THREE.SpriteMaterial({ + + map: Object.assign( SurfaceText( axesLabelStyle )) + }) + ), + zAxisLabel = new THREE.Sprite( + + new THREE.SpriteMaterial({ + + map: Object.assign( SurfaceText( axesLabelStyle )) + }) + ) + + xAxisLabel.material.map.print( 'x' ) + xAxisLabel.position.set( 0, 0, 1.45 ) + xAxisLabel.scale.set( 0.25, 0.25, 0.25 ) + xAxis.add( xAxisLabel ) + + yAxisLabel.material.map.print( 'y' ) + yAxisLabel.position.set( 1.45, 0, 0 ) + yAxisLabel.scale.set( 0.25, 0.25, 0.25 ) + yAxis.add( yAxisLabel ) + + zAxisLabel.material.map.print( 'z' ) + zAxisLabel.position.set( 0, 1.45, 0 ) + zAxisLabel.scale.set( 0.25, 0.25, 0.25 ) + zAxis.add( zAxisLabel ) + + + this.blochColor = new THREE.Color() + + + // Create the line from the sphere’s origin + // out to where the Bloch vector intersects + // with the sphere’s surface. + + this.blochVector = new THREE.Mesh( + + new THREE.BoxGeometry( 0.04, 0.04, this.radius ), + new THREE.MeshBasicMaterial({ color: BlochSphere.vectorColor }) + ) + this.blochVector.geometry.translate( 0, 0, 0.5 ) + this.group.add( this.blochVector ) + + + // Create the cone that indicates the Bloch vector + // and points to where that vectors + // intersects with the surface of the sphere. + + this.blochPointer = new THREE.Mesh( + + new THREE.CylinderBufferGeometry( 0, 0.5, 1, 32, 1 ), + new THREE.MeshPhongMaterial({ color: BlochSphere.vectorColor }) + ) + this.blochPointer.geometry.translate( 0, -0.5, 0 ) + this.blochPointer.geometry.rotateX( Math.PI / 2 ) + this.blochPointer.geometry.scale( 0.2, 0.2, 0.2 ) + this.blochPointer.lookAt( new THREE.Vector3() ) + this.blochPointer.receiveShadow = true + this.blochPointer.castShadow = true + this.group.add( this.blochPointer ) + + + // Create the Theta ring that will belt the sphere. + + const + arcR = this.radiusSafe * Math.sin( Math.PI / 2 ), + arcH = this.radiusSafe * Math.cos( Math.PI / 2 ), + thetaGeometry = BlochSphere.createLatitudeArc( arcR, 128, Math.PI / 2, Math.PI * 2 ), + thetaLine = new MeshLine(), + thetaPhiMaterial = new MeshLineMaterial({ + + color: BlochSphere.thetaPhiColor,//0x505962, + lineWidth: this.arcLineWidth * 3, + sizeAttenuation: true + }) + + thetaGeometry.rotateX( Math.PI / 2 ) + thetaGeometry.rotateY( Math.PI / 2 ) + thetaGeometry.translate( 0, arcH, 0 ) + thetaLine.setGeometry( thetaGeometry ) + + this.thetaMesh = new THREE.Mesh( + + thetaLine.geometry, + thetaPhiMaterial + ) + this.group.add( this.thetaMesh ) + + + // Create the Phi arc that will draw from the north pole + // down to wherever the Theta arc rests. + + this.phiGeometry = BlochSphere.createLongitudeArc( this.radiusSafe, 64, 0, Math.PI * 2 ), + this.phiLine = new MeshLine() + this.phiLine.setGeometry( this.phiGeometry ) + this.phiMesh = new THREE.Mesh( + + this.phiLine.geometry, + thetaPhiMaterial + ) + this.group.add( this.phiMesh ) + + + + + // Time to put plans to action. + + BlochSphere.prototype.setTargetState.call( this ) +} + + + + + + + //////////////// + // // + // Static // + // // +//////////////// + + +Object.assign( BlochSphere, { + + xAxisColor: 0x333333,// Was 0xCF1717 (red) + yAxisColor: 0x333333,// Was 0x59A112 (green) + zAxisColor: 0x333333,// Was 0x0F66BD (blue) + vectorColor: 0xFFFFFF,// Was 0xF2B90D (yellow) + thetaPhiColor: 0x333333,// Was 0xF2B90D (yellow) + + + // It’s important that we build the texture + // right here and now, rather than load an image. + // Why? Because if we load a pre-existing image + // we run into CORS problems using file:/// ! + + makeSurface: function(){ + + const + width = 2048, + height = width / 2 + + const canvas = document.createElement( 'canvas' ) + canvas.width = width + canvas.height = height + + const context = canvas.getContext( '2d' ) + context.fillStyle = 'hsl( 210, 20%, 100% )' + context.fillRect( 0, 0, width, height ) + + + // Create the base hue gradient for our texture. + + const + hueGradient = context.createLinearGradient( 0, height / 2, width, height / 2 ), + hueSteps = 180, + huesPerStep = 360 / hueSteps + + for( let i = 0; i <= hueSteps; i ++ ){ + + hueGradient.addColorStop( i / hueSteps, 'hsl( '+ ( i * huesPerStep - 90 ) +', 100%, 50% )' ) + } + context.fillStyle = hueGradient + context.fillRect( 0, 0, width, height ) + + + // For both the northern gradient (to white) + // and the southern gradient (to black) + // we’ll leave a thin band of full saturation + // near the equator. + + const whiteGradient = context.createLinearGradient( width / 2, 0, width / 2, height / 2 ) + whiteGradient.addColorStop( 0.000, 'hsla( 0, 0%, 100%, 1 )' ) + whiteGradient.addColorStop( 0.125, 'hsla( 0, 0%, 100%, 1 )' ) + whiteGradient.addColorStop( 0.875, 'hsla( 0, 0%, 100%, 0 )' ) + context.fillStyle = whiteGradient + context.fillRect( 0, 0, width, height / 2 ) + + const blackGradient = context.createLinearGradient( width / 2, height / 2, width / 2, height ) + blackGradient.addColorStop( 0.125, 'hsla( 0, 0%, 0%, 0 )' ) + blackGradient.addColorStop( 0.875, 'hsla( 0, 0%, 0%, 1 )' ) + blackGradient.addColorStop( 1.000, 'hsla( 0, 0%, 0%, 1 )' ) + context.fillStyle = blackGradient + context.fillRect( 0, height / 2, width, height ) + + + // Create lines of latitude and longitude. + // Note this is an inverse Mercatur projection ;) + + context.fillStyle = 'hsla( 0, 0%, 0%, 0.2 )' + const yStep = height / 16 + for( let y = 0; y <= height; y += yStep ){ + + context.fillRect( 0, y, width, 1 ) + } + const xStep = width / 16 + for( let x = 0; x <= width; x += xStep ){ + + context.fillRect( x, 0, 1, height ) + } + + + // Prepare the THREE texture and return it + // so we can use it as a material map. + + const texture = new THREE.CanvasTexture( canvas ) + texture.needsUpdate = true + return texture + }, + + + + + createLongitudeArc: function( radius, segments, thetaStart, thetaLength ){ + + const geometry = new THREE.CircleGeometry( radius, segments, thetaStart, thetaLength ) + geometry.vertices.shift() + + + // This is NOT NORMALLY necessary + // because we expect this to only be + // between PI/2 and PI*2 + // (so the length is only Math.PI instead of PI*2). + + if( thetaLength >= Math.PI * 2 ){ + + geometry.vertices.push( geometry.vertices[ 0 ].clone() ) + } + return geometry + }, + createLatitudeArc: function( radius, segments, phiStart, phiLength ){ + + const geometry = new THREE.CircleGeometry( radius, segments, phiStart, phiLength ) + geometry.vertices.shift() + if( phiLength >= 2 * Math.PI ){ + + geometry.vertices.push( geometry.vertices[ 0 ].clone() ) + } + return geometry + }, + createQuadSphere: function( options ){ + + let { + + radius, + phiStart, + phiLength, + thetaStart, + thetaLength, + latitudeLinesTotal, + longitudeLinesTotal, + latitudeLineSegments, + longitudeLineSegments, + latitudeLinesAttributes, + longitudeLinesAttributes + + } = options + + if( typeof radius !== 'number' ) radius = 1 + if( typeof phiStart !== 'number' ) phiStart = Math.PI / 2 + if( typeof phiLength !== 'number' ) phiLength = Math.PI * 2 + if( typeof thetaStart !== 'number' ) thetaStart = 0 + if( typeof thetaLength !== 'number' ) thetaLength = Math.PI + if( typeof latitudeLinesTotal !== 'number' ) latitudeLinesTotal = 16 + if( typeof longitudeLinesTotal !== 'number' ) longitudeLinesTotal = 16 + if( typeof latitudeLineSegments !== 'number' ) latitudeLineSegments = 64 + if( typeof longitudeLineSegments !== 'number' ) longitudeLineSegments = 64 + if( typeof latitudeLinesAttributes === 'undefined' ) latitudeLinesAttributes = { color: 0xCCCCCC } + if( typeof longitudeLinesAttributes === 'undefined' ) longitudeLinesAttributes = { color: 0xCCCCCC } + + const + sphere = new THREE.Group(), + latitudeLinesMaterial = new THREE.LineBasicMaterial( latitudeLinesAttributes ), + longitudeLinesMaterial = new THREE.LineBasicMaterial( longitudeLinesAttributes ) + + + // Lines of longitude. + // https://en.wikipedia.org/wiki/Longitude + + for( + + let + phiDelta = phiLength / longitudeLinesTotal, + phi = phiStart, + arc = BlochSphere.createLongitudeArc( radius, longitudeLineSegments, thetaStart + Math.PI / 2, thetaLength ); + phi < phiStart + phiLength + phiDelta; + phi += phiDelta ){ + + const geometry = arc.clone() + geometry.rotateY( phi ) + sphere.add( new THREE.Line( geometry, longitudeLinesMaterial )) + } + + + // Lines of latitude. + // https://en.wikipedia.org/wiki/Latitude + + for ( + + let + thetaDelta = thetaLength / latitudeLinesTotal, + theta = thetaStart; + theta < thetaStart + thetaLength; + theta += thetaDelta ){ + + if( theta === 0 ) continue + + const + arcR = radius * Math.sin( theta ), + arcH = radius * Math.cos( theta ), + geometry = BlochSphere.createLatitudeArc( arcR, latitudeLineSegments, phiStart, phiLength ) + + geometry.rotateX( Math.PI / 2 ) + geometry.rotateY( Math.PI / 2 ) + geometry.translate( 0, arcH, 0 ) + sphere.add( new THREE.Line( geometry, latitudeLinesMaterial )) + } + + + return sphere + } +}) + + + + + + + /////////////// + // // + // Proto // + // // +/////////////// + + +Object.assign( BlochSphere.prototype, { + + update: function(){ + + if( this.isRotating ) this.group.rotation.y += Math.PI / 4096 + }, + setTargetState: function( target ){ + + if( target === undefined ) target = Qubit.HORIZONTAL.toBlochSphere() + + + // Always take the shortest path around + // even if it crosses the 0˚ / 360˚ boundary, + // ie. between Anti-Diagonal (-90˚) and + // Right0-and circular polarized (180˚). + + const + rangeHalf = Math.PI, + distance = this.state.phi - target.phi + + if( Math.abs( distance ) > rangeHalf ){ + + this.state.phi += Math.sign( distance ) * rangeHalf * -2 + } + + + // Cheap hack to test if we need to update values + // from within the updateBlochVector method. + + Object.assign( this.target, target ) + + + // Create the tween. + + window.tween = new TWEEN.Tween( this.state ) + .to( target, 1000 ) + .easing( TWEEN.Easing.Quadratic.InOut ) + .onUpdate( this.updateBlochVector.bind( this )) + .start() + }, + updateBlochVector: function( state ){ + + + // Move the big-ass surface pointer. + + if( state.theta !== this.target.theta || + state.phi !== this.target.phi ){ + + this.blochPointer.position.set( + + Math.sin( state.theta ) * Math.sin( state.phi ), + Math.cos( state.theta ), + Math.sin( state.theta ) * Math.cos( state.phi ) + ) + this.blochPointer.lookAt( new THREE.Vector3() ) + this.blochVector.lookAt( this.blochPointer.getWorldPosition( new THREE.Vector3() )) + + + // Determine the correct HSL color + // based on Phi and Theta. + + let hue = state.phi * THREE.Math.RAD2DEG + if( hue < 0 ) hue = 360 + hue + this.blochColor.setHSL( + + hue / 360, + 1, + 1 - ( state.theta / Math.PI ) + ) + this.blochPointer.material.color = this.blochColor + this.blochVector.material.color = this.blochColor + + if( state.theta !== this.target.theta ){ + + + // Slide the Theta ring from the north pole + // down as far south as it needs to go + // and scale its radius so it belts the sphere. + + const thetaScaleSafe = Math.max( state.theta, 0.01 ) + this.thetaMesh.scale.set( + + Math.sin( thetaScaleSafe ), + 1, + Math.sin( thetaScaleSafe ) + ) + this.thetaMesh.position.y = Math.cos( state.theta ) + + + // Redraw the Phi arc to extend from the north pole + // down to only as far as the Theta ring sits. + // Then rotate the whole Phi arc about the poles. + + for( + + let + i = 0, + limit = this.phiGeometry.vertices.length; + + i < limit; + i ++ ){ + + const gain = i / ( limit - 1 ) + this.phiGeometry.vertices[ i ].set( + + Math.sin( state.theta * gain ) * this.radiusSafe, + Math.cos( state.theta * gain ) * this.radiusSafe, + 0 + ) + } + this.phiLine.setGeometry( this.phiGeometry ) + } + if( state.phi !== this.target.phi ){ + + this.phiMesh.rotation.y = state.phi - Math.PI / 2 + } + if( typeof this.onValueChange === 'function' ) this.onValueChange.call( this ) + } + } +}) + + + +module.exports = {BlochSphere} + + + + +},{"quantum-js-util":13}],15:[function(require,module,exports){ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +const {Q, Circuit, Gate, logger, misc, mathf } = require('quantum-js-util'); +Editor = function( circuit, targetEl ){ + // First order of business, + // we require a valid circuit. + + if( circuit instanceof Circuit !== true ) circuit = new Circuit() + this.circuit = circuit + this.index = Editor.index ++ + + + // Editor is all about the DOM + // so we’re going to get some use out of this + // stupid (but convenient) shorthand here. + + const createDiv = function(){ + + return document.createElement( 'div' ) + } + + + + + + + // We want to “name” our circuit editor instance + // but more importantly we want to give it a unique DOM ID. + // Keep in mind we can have MULTIPLE editors + // for the SAME circuit! + // This is a verbose way to do it, + // but each step is clear and I needed clarity today! ;) + + this.name = typeof circuit.name === 'string' ? + circuit.name : + 'Q Editor '+ this.index + + + // If we’ve been passed a target DOM element + // we should use that as our circuit element. + + if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl ) + const circuitEl = targetEl instanceof HTMLElement ? targetEl : createDiv() + circuitEl.classList.add( 'Q-circuit' ) + + + // If the target element already has an ID + // then we want to use that as our domID. + + if( typeof circuitEl.getAttribute( 'id' ) === 'string' ){ + + this.domId = circuitEl.getAttribute( 'id' ) + } + + + // Otherwise let’s transform our name value + // into a usable domId. + + else { + + let domIdBase = this.name + .replace( /^[^a-z]+|[^\w:.-]+/gi, '-' ), + domId = domIdBase, + domIdAttempt = 1 + + while( document.getElementById( domId ) !== null ){ + + domIdAttempt ++ + domId = domIdBase +'-'+ domIdAttempt + } + this.domId = domId + circuitEl.setAttribute( 'id', this.domId ) + } + + + + + // We want a way to easily get to the circuit + // from this interface’s DOM element. + // (But we don’t need a way to reference this DOM element + // from the circuit. A circuit can have many DOM elements!) + // And we also want an easy way to reference this DOM element + // from this Editor instance. + + circuitEl.circuit = circuit + this.domElement = circuitEl + + + // Create a toolbar for containing buttons. + + const toolbarEl = createDiv() + circuitEl.appendChild( toolbarEl ) + toolbarEl.classList.add( 'Q-circuit-toolbar' ) + + + // Create a toggle switch for locking the circuit. + + const lockToggle = createDiv() + toolbarEl.appendChild( lockToggle ) + lockToggle.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-lock' ) + lockToggle.setAttribute( 'title', 'Lock / unlock' ) + lockToggle.innerText = '🔓' + + + // Create an “Undo” button + // that enables and disables + // based on available undo history. + + const undoButton = createDiv() + toolbarEl.appendChild( undoButton ) + undoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-undo' ) + undoButton.setAttribute( 'title', 'Undo' ) + undoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + undoButton.innerHTML = '⟲' + window.addEventListener( 'History undo is depleted', function( event ){ + + if( event.detail.instance === circuit ) + undoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + }) + window.addEventListener( 'History undo is capable', function( event ){ + + if( event.detail.instance === circuit ) + undoButton.removeAttribute( 'Q-disabled' ) + }) + + + // Create an “Redo” button + // that enables and disables + // based on available redo history. + + const redoButton = createDiv() + toolbarEl.appendChild( redoButton ) + redoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-redo' ) + redoButton.setAttribute( 'title', 'Redo' ) + redoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + redoButton.innerHTML = '⟳' + window.addEventListener( 'History redo is depleted', function( event ){ + + if( event.detail.instance === circuit ) + redoButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + }) + window.addEventListener( 'History redo is capable', function( event ){ + + if( event.detail.instance === circuit ) + redoButton.removeAttribute( 'Q-disabled' ) + }) + + + // Create a button for joining + // an “identity cursor” + // and one or more same-gate operations + // into a controlled operation. + // (Will be enabled / disabled from elsewhere.) + + const controlButton = createDiv() + toolbarEl.appendChild( controlButton ) + controlButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-control' ) + controlButton.setAttribute( 'title', 'Create controlled operation' ) + controlButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + controlButton.innerText = 'C' + + + // Create a button for joining + // two “identity cursors” + // into a swap operation. + // (Will be enabled / disabled from elsewhere.) + + const swapButton = createDiv() + toolbarEl.appendChild( swapButton ) + swapButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle-swap' ) + swapButton.setAttribute( 'title', 'Create swap operation' ) + swapButton.setAttribute( 'Q-disabled', 'Q-disabled' ) + swapButton.innerText = 'S' + + + // Create a circuit board container + // so we can house a scrollable circuit board. + + const boardContainerEl = createDiv() + circuitEl.appendChild( boardContainerEl ) + boardContainerEl.classList.add( 'Q-circuit-board-container' ) + //boardContainerEl.addEventListener( 'touchstart', Editor.onPointerPress ) + boardContainerEl.addEventListener( 'mouseleave', function(){ + Editor.unhighlightAll( circuitEl ) + }) + + const boardEl = createDiv() + boardContainerEl.appendChild( boardEl ) + boardEl.classList.add( 'Q-circuit-board' ) + + const backgroundEl = createDiv() + boardEl.appendChild( backgroundEl ) + backgroundEl.classList.add( 'Q-circuit-board-background' ) + + const parameterEl = createDiv() + boardEl.appendChild( parameterEl ) + parameterEl.classList.add( 'Q-parameters-box' ) + // Create background highlight bars + // for each row. + + for( let i = 0; i < circuit.bandwidth; i ++ ){ + + const rowEl = createDiv() + backgroundEl.appendChild( rowEl ) + rowEl.style.position = 'relative' + rowEl.style.gridRowStart = i + 2 + rowEl.style.gridColumnStart = 1 + rowEl.style.gridColumnEnd = Editor.momentIndexToGridColumn( circuit.timewidth ) + 1 + rowEl.setAttribute( 'register-index', i + 1 ) + + const wireEl = createDiv() + rowEl.appendChild( wireEl ) + wireEl.classList.add( 'Q-circuit-register-wire' ) + } + + + // Create background highlight bars + // for each column. + + for( let i = 0; i < circuit.timewidth; i ++ ){ + + const columnEl = createDiv() + backgroundEl.appendChild( columnEl ) + columnEl.style.gridRowStart = 2 + columnEl.style.gridRowEnd = Editor.registerIndexToGridRow( circuit.bandwidth ) + 1 + columnEl.style.gridColumnStart = i + 3 + columnEl.setAttribute( 'moment-index', i + 1 ) + } + + + // Create the circuit board foreground + // for all interactive elements. + + const foregroundEl = createDiv() + boardEl.appendChild( foregroundEl ) + foregroundEl.classList.add( 'Q-circuit-board-foreground' ) + + + // Add “Select All” toggle button to upper-left corner. + + const selectallEl = createDiv() + foregroundEl.appendChild( selectallEl ) + selectallEl.classList.add( 'Q-circuit-header', 'Q-circuit-selectall' ) + selectallEl.setAttribute( 'title', 'Select all' ) + selectallEl.setAttribute( 'moment-index', '0' ) + selectallEl.setAttribute( 'register-index', '0' ) + selectallEl.innerHTML = '↘' + + + // Add register index symbols to left-hand column. + + for( let i = 0; i < circuit.bandwidth; i ++ ){ + + const + registerIndex = i + 1, + registersymbolEl = createDiv() + + foregroundEl.appendChild( registersymbolEl ) + registersymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-label' ) + registersymbolEl.setAttribute( 'title', 'Register '+ registerIndex +' of '+ circuit.bandwidth ) + registersymbolEl.setAttribute( 'register-index', registerIndex ) + registersymbolEl.style.gridRowStart = Editor.registerIndexToGridRow( registerIndex ) + registersymbolEl.innerText = registerIndex + } + + + // Add “Add register” button.q + + const addRegisterEl = createDiv() + foregroundEl.appendChild( addRegisterEl ) + addRegisterEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-add' ) + addRegisterEl.setAttribute( 'title', 'Add register' ) + addRegisterEl.style.gridRowStart = Editor.registerIndexToGridRow( circuit.bandwidth + 1 ) + addRegisterEl.innerText = '+' + + + // Add moment index symbols to top row. + + for( let i = 0; i < circuit.timewidth; i ++ ){ + + const + momentIndex = i + 1, + momentsymbolEl = createDiv() + + foregroundEl.appendChild( momentsymbolEl ) + momentsymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-label' ) + momentsymbolEl.setAttribute( 'title', 'Moment '+ momentIndex +' of '+ circuit.timewidth ) + momentsymbolEl.setAttribute( 'moment-index', momentIndex ) + momentsymbolEl.style.gridColumnStart = Editor.momentIndexToGridColumn( momentIndex ) + momentsymbolEl.innerText = momentIndex + } + + + // Add “Add moment” button. + + const addMomentEl = createDiv() + foregroundEl.appendChild( addMomentEl ) + addMomentEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-add' ) + addMomentEl.setAttribute( 'title', 'Add moment' ) + addMomentEl.style.gridColumnStart = Editor.momentIndexToGridColumn( circuit.timewidth + 1 ) + addMomentEl.innerText = '+' + + + // Add input values. + + circuit.qubits.forEach( function( qubit, i ){ + + const + rowIndex = i + 1, + inputEl = createDiv() + + inputEl.classList.add( 'Q-circuit-header', 'Q-circuit-input' ) + inputEl.setAttribute( 'title', `Qubit #${ rowIndex } starting value` ) + inputEl.setAttribute( 'register-index', rowIndex ) + inputEl.style.gridRowStart = Editor.registerIndexToGridRow( rowIndex ) + inputEl.innerText = qubit.beta.toText() + foregroundEl.appendChild( inputEl ) + }) + + + // Add operations. + + circuit.operations.forEach( function( operation ){ + Editor.set( circuitEl, operation ) + }) + + + // Add event listeners. + + circuitEl.addEventListener( 'mousedown', Editor.onPointerPress ) + circuitEl.addEventListener( 'touchstart', Editor.onPointerPress ) + window.addEventListener( + + 'Circuit.set$', + Editor.prototype.onExternalSet.bind( this ) + ) + window.addEventListener( + + 'Circuit.clear$', + Editor.prototype.onExternalClear.bind( this ) + ) + + + // How can we interact with this circuit + // through code? (How cool is this?!) + + const referenceEl = document.createElement( 'p' ) + circuitEl.appendChild( referenceEl ) + referenceEl.innerHTML = ` + This circuit is accessible in your + JavaScript console + as document.getElementById('${ this.domId }').circuit` + //document.getElementById('Q-Editor-0').circuit + //$('#${ this.domId }') + + + // Put a note in the JavaScript console + // that includes how to reference the circuit via code + // and an ASCII diagram for reference. + + logger.warn( 0.5, + `\n\nCreated a DOM interface for $('#${ this.domId }').circuit\n\n`, + circuit.toDiagram(), + '\n\n\n' + ) +} + + +// Augment Circuit to have this functionality. + +Circuit.toDom = function( circuit, targetEl ){ + + return new Editor( circuit, targetEl ).domElement +} +Circuit.prototype.toDom = function( targetEl ){ + + return new Editor( this, targetEl ).domElement +} + + + + + + + + +Object.assign( Editor, { + + index: 0, + help: function(){ return logger.help( this )}, + dragEl: null, + gridColumnToMomentIndex: function( gridColumn ){ return +gridColumn - 2 }, + momentIndexToGridColumn: function( momentIndex ){ return momentIndex + 2 }, + gridRowToRegisterIndex: function( gridRow ){ return +gridRow - 1 }, + registerIndexToGridRow: function( registerIndex ){ return registerIndex + 1 }, + gridSize: 4,// CSS: grid-auto-columns = grid-auto-rows = 4rem. + pointToGrid: function( p ){ + + + // Take a 1-dimensional point value + // (so either an X or a Y but not both) + // and return what CSS grid cell contains it + // based on our 4rem × 4rem grid setup. + + const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize ) + return 1 + Math.floor( p / ( rem * Editor.gridSize )) + }, + gridToPoint: function( g ){ + + + // Take a 1-dimensional grid cell value + // (so either a row or a column but not both) + // and return the minimum point value it contains. + + const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize ) + return rem * Editor.gridSize * ( g - 1 ) + }, + getInteractionCoordinates: function( event, pageOrClient ){ + + if( typeof pageOrClient !== 'string' ) pageOrClient = 'client'//page + if( event.changedTouches && + event.changedTouches.length ) return { + + x: event.changedTouches[ 0 ][ pageOrClient +'X' ], + y: event.changedTouches[ 0 ][ pageOrClient +'Y' ] + } + return { + x: event[ pageOrClient +'X' ], + y: event[ pageOrClient +'Y' ] + } + }, + createNewElement :function(element_type, element_parent, element_css) { + element = document.createElement(element_type) + if(element_css) element.classList.add(element_css) + if(element_parent) element_parent.appendChild( element ) + return element + }, + createPalette: function( targetEl ){ + + if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl ) + + const + paletteEl = targetEl instanceof HTMLElement ? targetEl : document.createElement( 'div' ), + randomRangeAndSign = function( min, max ){ + + const r = min + Math.random() * ( max - min ) + return Math.floor( Math.random() * 2 ) ? r : -r + } + + //ltnln: added missing Braket operations. + paletteEl.classList.add( 'Q-circuit-palette' ); + 'H,X,Y,Z,P,Rx,Ry,Rz,U,V,V†,S*,S†,T,T†,00,01,10,√S,iS,XX,XY,YY,ZZ,*' + .split( ',' ) + .forEach( function( symbol ){ + + const gate = Gate.findBySymbol( symbol ) + + const operationEl = document.createElement( 'div' ) + paletteEl.appendChild( operationEl ) + operationEl.classList.add( 'Q-circuit-operation' ) + operationEl.classList.add( 'Q-circuit-operation-'+ gate.nameCss ) + operationEl.setAttribute( 'gate-symbol', symbol ) + operationEl.setAttribute( 'title', gate.name ) + + const tileEl = document.createElement( 'div' ) + operationEl.appendChild( tileEl ) + tileEl.classList.add( 'Q-circuit-operation-tile' ) + if( symbol !== Gate.CURSOR.symbol ) tileEl.innerText = symbol + + ;[ 'before', 'after' ].forEach( function( layer ){ + + tileEl.style.setProperty( '--Q-'+ layer +'-rotation', randomRangeAndSign( 0.5, 4 ) +'deg' ) + tileEl.style.setProperty( '--Q-'+ layer +'-x', randomRangeAndSign( 1, 4 ) +'px' ) + tileEl.style.setProperty( '--Q-'+ layer +'-y', randomRangeAndSign( 1, 3 ) +'px' ) + }) + }) + + paletteEl.addEventListener( 'mousedown', Editor.onPointerPress ) + paletteEl.addEventListener( 'touchstart', Editor.onPointerPress ) + return paletteEl + }, + toDom: function( circuit, targetEl ){ + + return new Editor( circuit, targetEl ).domElement + } +}) + + + + + + + ///////////////////////// + // // + // Operation CLEAR // + // // +///////////////////////// + + +Editor.prototype.onExternalClear = function( event ){ + + if( event.detail.circuit === this.circuit ){ + + Editor.clear( this.domElement, { + + momentIndex: event.detail.momentIndex, + registerIndices: event.detail.registerIndices + }) + } +} +Editor.clear = function( circuitEl, operation ){ + + const momentIndex = operation.momentIndex + operation.registerIndices.forEach( function( registerIndex ){ + + Array + .from( circuitEl.querySelectorAll( + + `[moment-index="${ momentIndex }"]`+ + `[register-index="${ registerIndex }"]` + + )) + .forEach( function( op ){ + + op.parentNode.removeChild( op ) + }) + }) +} + + + + + + + /////////////////////// + // // + // Operation SET // + // // +/////////////////////// + + +Editor.prototype.onExternalSet = function( event ){ + + if( event.detail.circuit === this.circuit ){ + + Editor.set( this.domElement, event.detail.operation ) + } +} +Editor.set = function( circuitEl, operation ){ + const + backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ), + foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ), + circuit = circuitEl.circuit, + operationIndex = circuitEl.circuit.operations.indexOf( operation ) + + operation.registerIndices.forEach( function( registerIndex, i ){ + const operationEl = document.createElement( 'div' ) + foregroundEl.appendChild( operationEl ) + operationEl.classList.add( 'Q-circuit-operation', 'Q-circuit-operation-'+ operation.gate.nameCss ) + // operationEl.setAttribute( 'operation-index', operationIndex ) + operationEl.setAttribute( 'gate-symbol', operation.gate.symbol ) + operationEl.setAttribute( 'gate-index', operation.gate.index )// Used as an application-wide unique ID! + operationEl.setAttribute( 'moment-index', operation.momentIndex ) + operationEl.setAttribute( 'register-index', registerIndex ) + operationEl.setAttribute( 'register-array-index', i )// Where within the registerIndices array is this operations fragment located? + operationEl.setAttribute( 'is-controlled', operation.isControlled ) + operationEl.setAttribute( 'title', operation.gate.name ) + operationEl.style.gridColumnStart = Editor.momentIndexToGridColumn( operation.momentIndex ) + operationEl.style.gridRowStart = Editor.registerIndexToGridRow( registerIndex ) + if( operation.gate.has_parameters ) Object.keys(operation.gate.parameters).forEach( element => { + operationEl.setAttribute( element, operation.gate.parameters[element] ) //adds a parameter attribute to the operation! + }) + const tileEl = document.createElement( 'div' ) + operationEl.appendChild( tileEl ) + tileEl.classList.add( 'Q-circuit-operation-tile' ) + if( operation.gate.symbol !== Gate.CURSOR.symbol ) tileEl.innerText = operation.gate.symbol + + + // Add operation link wires + // for multi-qubit operations. + + if( operation.registerIndices.length > 1 ){ + + operationEl.setAttribute( 'register-indices', operation.registerIndices ) + operationEl.setAttribute( 'register-indices-index', i ) + operationEl.setAttribute( + + 'sibling-indices', + operation.registerIndices + .filter( function( siblingRegisterIndex ){ + + return registerIndex !== siblingRegisterIndex + }) + ) + operation.registerIndices.forEach( function( registerIndex, i ){ + + if( i < operation.registerIndices.length - 1 ){ + + const + siblingRegisterIndex = operation.registerIndices[ i + 1 ], + registerDelta = Math.abs( siblingRegisterIndex - registerIndex ), + start = Math.min( registerIndex, siblingRegisterIndex ), + end = Math.max( registerIndex, siblingRegisterIndex ), + containerEl = document.createElement( 'div' ), + linkEl = document.createElement( 'div' ) + + backgroundEl.appendChild( containerEl ) + containerEl.setAttribute( 'moment-index', operation.momentIndex ) + containerEl.setAttribute( 'register-index', registerIndex ) + containerEl.classList.add( 'Q-circuit-operation-link-container' ) + containerEl.style.gridRowStart = Editor.registerIndexToGridRow( start ) + containerEl.style.gridRowEnd = Editor.registerIndexToGridRow( end + 1 ) + containerEl.style.gridColumn = Editor.momentIndexToGridColumn( operation.momentIndex ) + + containerEl.appendChild( linkEl ) + linkEl.classList.add( 'Q-circuit-operation-link' ) + if( registerDelta > 1 ) linkEl.classList.add( 'Q-circuit-operation-link-curved' ) + } + }) + if( operation.isControlled && i === 0 ){ + operationEl.classList.add( 'Q-circuit-operation-control' ) + operationEl.setAttribute( 'title', 'Control' ) + tileEl.innerText = '' + } + else operationEl.classList.add( 'Q-circuit-operation-target' ) + } + }) +} + + + + +Editor.isValidControlCandidate = function( circuitEl ){ + + const + selectedOperations = Array + .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) + + + // We must have at least two operations selected, + // hopefully a control and something else, + // in order to attempt a join. + + if( selectedOperations.length < 2 ) return false + + + // Note the different moment indices present + // among the selected operations. + + const moments = selectedOperations.reduce( function( moments, operationEl ){ + + moments[ operationEl.getAttribute( 'moment-index' )] = true + return moments + + }, {} ) + + + // All selected operations must be in the same moment. + + if( Object.keys( moments ).length > 1 ) return false + + + // If there are multi-register operations present, + // regardless of whether those are controls or swaps, + // all siblings must be present + // in order to join a new gate to this selection. + + // I’m sure we can make this whole routine much more efficient + // but its results are correct and boy am I tired ;) + + const allSiblingsPresent = selectedOperations + .reduce( function( status, operationEl ){ + + const registerIndicesString = operationEl.getAttribute( 'register-indices' ) + + + // If it’s a single-register operation + // there’s no need to search further. + + if( !registerIndicesString ) return status + + + // How many registers are in use + // by this operation? + + const + registerIndicesLength = registerIndicesString + .split( ',' ) + .map( function( registerIndex ){ + + return +registerIndex + }) + .length, + + + // How many of this operation’s siblings + // (including itself) can we find? + + allSiblingsLength = selectedOperations + .reduce( function( siblings, operationEl ){ + + if( operationEl.getAttribute( 'register-indices' ) === registerIndicesString ){ + + siblings.push( operationEl ) + } + return siblings + + }, []) + .length + + + // Did we find all of the siblings for this operation? + // Square that with previous searches. + + return status && allSiblingsLength === registerIndicesLength + + }, true ) + + + // If we’re missing some siblings + // then we cannot modify whatever we have selected here. + + if( allSiblingsPresent !== true ) return false + + // Note the different gate types present + // among the selected operations. + + const gates = selectedOperations.reduce( function( gates, operationEl ){ + const gateSymbol = operationEl.getAttribute( 'gate-symbol' ) + if( !mathf.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1 + else gates[ gateSymbol ] ++ + return gates + + }, {} ) + + + // Note if each operation is already controlled or not. + + const { + + totalControlled, + totalNotControlled + + } = selectedOperations + .reduce( function( stats, operationEl ){ + + if( operationEl.getAttribute( 'is-controlled' ) === 'true' ) + stats.totalControlled ++ + else stats.totalNotControlled ++ + return stats + + }, { + + totalControlled: 0, + totalNotControlled: 0 + }) + + // This could be ONE “identity cursor” + // and one or more of a regular single gate + // that is NOT already controlled. + + if( gates[ Gate.CURSOR.symbol ] === 1 && + Object.keys( gates ).length === 2 && + totalNotControlled === selectedOperations.length ){ + + return true + } + + + // There’s NO “identity cursor” + // but there is one or more of specific gate type + // and at least one of those is already controlled. + + if( gates[ Gate.CURSOR.symbol ] === undefined && + Object.keys( gates ).length === 1 && + totalControlled > 0 && + totalNotControlled > 0 ){ + + return true + } + + + // Any other combination allowed? Nope! + + return false +} +Editor.createControl = function( circuitEl ){ + + if( Editor.isValidControlCandidate( circuitEl ) !== true ) return this + + + const + circuit = circuitEl.circuit, + selectedOperations = Array + .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )), + + + // Are any of these controlled operations?? + // If so, we need to find its control component + // and re-use it. + + existingControlEl = selectedOperations.find( function( operationEl ){ + + return ( + + operationEl.getAttribute( 'is-controlled' ) === 'true' && + operationEl.getAttribute( 'register-array-index' ) === '0' + ) + }), + + + // One control. One or more targets. + + control = existingControlEl || selectedOperations + .find( function( el ){ + + return el.getAttribute( 'gate-symbol' ) === Gate.CURSOR.symbol + }), + targets = selectedOperations + .reduce( function( targets, el ){ + + //if( el.getAttribute( 'gate-symbol' ) !== '!' ) targets.push( el ) + if( el !== control ) targets.push( el ) + return targets + + }, [] ) + + + // Ready to roll. + + circuit.history.createEntry$() + selectedOperations.forEach( function( operationEl ){ + + circuit.clear$( + + +operationEl.getAttribute( 'moment-index' ), + +operationEl.getAttribute( 'register-index' ) + ) + }) + circuit.set$( + targets[ 0 ].getAttribute( 'gate-symbol' ), + +control.getAttribute( 'moment-index' ), + [ +control.getAttribute( 'register-index' )].concat( + + targets.reduce( function( registers, operationEl ){ + + registers.push( +operationEl.getAttribute( 'register-index' )) + return registers + + }, [] ) + ) + ) + + + // Update our toolbar button states. + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + + return this +} + + + + +Editor.isValidSwapCandidate = function( circuitEl ){ + + const + selectedOperations = Array + .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) + + + // We can only swap between two registers. + // No crazy rotation-swap bullshit. (Yet.) + if( selectedOperations.length !== 2 ) return false + + + // Both operations must be “identity cursors.” + // If so, we are good to go. + + areBothCursors = selectedOperations.every( function( operationEl ){ + + return operationEl.getAttribute( 'gate-symbol' ) === Gate.CURSOR.symbol + }) + if( areBothCursors ) return true + + + // Otherwise this is not a valid swap candidate. + + return false +} +Editor.createSwap = function( circuitEl ){ + + if( Editor.isValidSwapCandidate( circuitEl ) !== true ) return this + + const + selectedOperations = Array + .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )), + momentIndex = +selectedOperations[ 0 ].getAttribute( 'moment-index' ) + registerIndices = selectedOperations + .reduce( function( registerIndices, operationEl ){ + + registerIndices.push( +operationEl.getAttribute( 'register-index' )) + return registerIndices + + }, [] ), + circuit = circuitEl.circuit + + + // Create the swap operation. + + circuit.history.createEntry$() + selectedOperations.forEach( function( operation ){ + + circuit.clear$( + + +operation.getAttribute( 'moment-index' ), + +operation.getAttribute( 'register-index' ) + ) + }) + circuit.set$( + + Gate.SWAP, + momentIndex, + registerIndices + ) + + + // Update our toolbar button states. + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + + return this +} + + + + +Editor.onSelectionChanged = function( circuitEl ){ + + const controlButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-control' ) + if( Editor.isValidControlCandidate( circuitEl )){ + + controlButtonEl.removeAttribute( 'Q-disabled' ) + } + else controlButtonEl.setAttribute( 'Q-disabled', true ) + + const swapButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-swap' ) + if( Editor.isValidSwapCandidate( circuitEl )){ + + swapButtonEl.removeAttribute( 'Q-disabled' ) + } + else swapButtonEl.setAttribute( 'Q-disabled', true ) +} +Editor.onCircuitChanged = function( circuitEl ){ + + const circuit = circuitEl.circuit + window.dispatchEvent( new CustomEvent( + + 'Q gui altered circuit', + { detail: { circuit: circuit }} + )) + + // Should we trigger a circuit.evaluate$() here? + // Particularly when we move all that to a new thread?? + // console.log( originCircuit.report$() ) ?? +} + + + + + +Editor.unhighlightAll = function( circuitEl ){ + + Array.from( circuitEl.querySelectorAll( + + '.Q-circuit-board-background > div,'+ + '.Q-circuit-board-foreground > div' + )) + .forEach( function( el ){ + + el.classList.remove( 'Q-circuit-cell-highlighted' ) + }) +} + + + + + + + ////////////////////// + // // + // Pointer MOVE // + // // +////////////////////// + + +Editor.onPointerMove = function( event ){ + + + // We need our cursor coordinates straight away. + // We’ll use that both for dragging (immediately below) + // and for hover highlighting (further below). + // Let’s also hold on to a list of all DOM elements + // that contain this X, Y point + // and also see if one of those is a circuit board container. + + const + { x, y } = Editor.getInteractionCoordinates( event ), + foundEls = document.elementsFromPoint( x, y ), + boardContainerEl = foundEls.find( function( el ){ + + return el.classList.contains( 'Q-circuit-board-container' ) + }) + + + // Are we in the middle of a circuit clipboard drag? + // If so we need to move that thing! + + if( Editor.dragEl !== null ){ + + + // ex. Don’t scroll on touch devices! + + event.preventDefault() + + + // This was a very useful resource + // for a reality check on DOM coordinates: + // https://javascript.info/coordinates + + Editor.dragEl.style.left = ( x + window.pageXOffset + Editor.dragEl.offsetX ) +'px' + Editor.dragEl.style.top = ( y + window.pageYOffset + Editor.dragEl.offsetY ) +'px' + + if( !boardContainerEl && Editor.dragEl.circuitEl ) Editor.dragEl.classList.add( 'Q-circuit-clipboard-danger' ) + else Editor.dragEl.classList.remove( 'Q-circuit-clipboard-danger' ) + } + + + // If we’re not over a circuit board container + // then there’s no highlighting work to do + // so let’s bail now. + + if( !boardContainerEl ) return + + + // Now we know we have a circuit board + // so we must have a circuit + // and if that’s locked then highlighting changes allowed! + + const circuitEl = boardContainerEl.closest( '.Q-circuit' ) + if( circuitEl.classList.contains( 'Q-circuit-locked' )) return + + + // Ok, we’ve found a circuit board. + // First, un-highlight everything. + + Array.from( boardContainerEl.querySelectorAll(` + + .Q-circuit-board-background > div, + .Q-circuit-board-foreground > div + + `)).forEach( function( el ){ + + el.classList.remove( 'Q-circuit-cell-highlighted' ) + }) + + + // Let’s prioritize any element that is “sticky” + // which means it can appear OVER another grid cell. + + + const + cellEl = foundEls.find( function( el ){ + + const style = window.getComputedStyle( el ) + return ( + + style.position === 'sticky' && ( + + el.getAttribute( 'moment-index' ) !== null || + el.getAttribute( 'register-index' ) !== null + ) + ) + }), + highlightByQuery = function( query ){ + + Array.from( boardContainerEl.querySelectorAll( query )) + .forEach( function( el ){ + + el.classList.add( 'Q-circuit-cell-highlighted' ) + }) + } + + + // If we’ve found one of these “sticky” cells + // let’s use its moment and/or register data + // to highlight moments or registers (or all). + + if( cellEl ){ + + const + momentIndex = cellEl.getAttribute( 'moment-index' ), + registerIndex = cellEl.getAttribute( 'register-index' ) + + if( momentIndex === null ){ + + highlightByQuery( `div[register-index="${ registerIndex }"]` ) + return + } + if( registerIndex === null ){ + + highlightByQuery( `div[moment-index="${ momentIndex }"]` ) + return + } + highlightByQuery(` + + .Q-circuit-board-background > div[moment-index], + .Q-circuit-board-foreground > .Q-circuit-operation + + `) + return + } + + + // Ok, we know we’re hovering over the circuit board + // but we’re not on a “sticky” cell. + // We might be over an operation, but we might not. + // No matter -- we’ll infer the moment and register indices + // from the cursor position. + + const + boardElBounds = boardContainerEl.getBoundingClientRect(), + xLocal = x - boardElBounds.left + boardContainerEl.scrollLeft + 1, + yLocal = y - boardElBounds.top + boardContainerEl.scrollTop + 1, + columnIndex = Editor.pointToGrid( xLocal ), + rowIndex = Editor.pointToGrid( yLocal ), + momentIndex = Editor.gridColumnToMomentIndex( columnIndex ), + registerIndex = Editor.gridRowToRegisterIndex( rowIndex ) + + + // If this hover is “out of bounds” + // ie. on the same row or column as an “Add register” or “Add moment” button + // then let’s not highlight anything. + + if( momentIndex > circuitEl.circuit.timewidth || + registerIndex > circuitEl.circuit.bandwidth ) return + + + // If we’re at 0, 0 or below that either means + // we’re over the “Select all” button (already taken care of above) + // or over the lock toggle button. + // Either way, it’s time to bail. + + if( momentIndex < 1 || registerIndex < 1 ) return + + + // If we’ve made it this far that means + // we have valid moment and register indices. + // Highlight them! + + highlightByQuery(` + + div[moment-index="${ momentIndex }"], + div[register-index="${ registerIndex }"] + `) + return +} + + + + /////////////////////// + // // + // Pointer PRESS // + // // +/////////////////////// + + +Editor.onPointerPress = function( event ){ + // This is just a safety net + // in case something terrible has ocurred. + // (ex. Did the user click and then their mouse ran + // outside the window but browser didn’t catch it?) + + if( Editor.dragEl !== null ){ + + Editor.onPointerRelease( event ) + return + } + const + targetEl = event.target, + circuitEl = targetEl.closest( '.Q-circuit' ), + paletteEl = targetEl.closest( '.Q-circuit-palette' ) + parameterEl = targetEl.closest( '.Q-parameters-box' ) + + // If we can’t find a circuit that’s a really bad sign + // considering this event should be fired when a circuit + // is clicked on. So... bail! + + if( !circuitEl && !paletteEl ) return + + // This is a bit of a gamble. + // There’s a possibility we’re not going to drag anything, + // but we’ll prep these variables here anyway + // because both branches of if( circuitEl ) and if( paletteEl ) + // below will have access to this scope. + + dragEl = document.createElement( 'div' ) + dragEl.classList.add( 'Q-circuit-clipboard' ) + const { x, y } = Editor.getInteractionCoordinates( event ) + + + // Are we dealing with a circuit interface? + // ie. NOT a palette interface. + + if( circuitEl && !parameterEl ){ + + // Shall we toggle the circuit lock? + + const + circuit = circuitEl.circuit, + circuitIsLocked = circuitEl.classList.contains( 'Q-circuit-locked' ), + lockEl = targetEl.closest( '.Q-circuit-toggle-lock' ) + + if( lockEl ){ + + // const toolbarEl = Array.from( circuitEl.querySelectorAll( '.Q-circuit-button' )) + if( circuitIsLocked ){ + + circuitEl.classList.remove( 'Q-circuit-locked' ) + lockEl.innerText = '🔓' + } + else { + + circuitEl.classList.add( 'Q-circuit-locked' ) + lockEl.innerText = '🔒' + Editor.unhighlightAll( circuitEl ) + } + + + // We’ve toggled the circuit lock button + // so we should prevent further propagation + // before proceeding further. + // That includes running all this code again + // if it was originally fired by a mouse event + // and about to be fired by a touch event! + + event.preventDefault() + event.stopPropagation() + return + } + + + // If our circuit is already “locked” + // then there’s nothing more to do here. + + if( circuitIsLocked ) { + + logger.warn( `User attempted to interact with a circuit editor but it was locked.` ) + return + } + + + const + cellEl = targetEl.closest(` + + .Q-circuit-board-foreground > div, + .Q-circuit-palette > div + `), + undoEl = targetEl.closest( '.Q-circuit-button-undo' ), + redoEl = targetEl.closest( '.Q-circuit-button-redo' ), + controlEl = targetEl.closest( '.Q-circuit-toggle-control' ), + swapEl = targetEl.closest( '.Q-circuit-toggle-swap' ), + addMomentEl = targetEl.closest( '.Q-circuit-moment-add' ), + addRegisterEl = targetEl.closest( '.Q-circuit-register-add' ) + + if( !cellEl && + !undoEl && + !redoEl && + !controlEl && + !swapEl && + !addMomentEl && + !addRegisterEl ) return + + + // By this point we know that the circuit is unlocked + // and that we’ll activate a button / drag event / etc. + // So we need to hault futher event propagation + // including running this exact code again if this was + // fired by a touch event and about to again by mouse. + // This may SEEM redundant because we did this above + // within the lock-toggle button code + // but we needed to NOT stop propagation if the circuit + // was already locked -- for scrolling and such. + + event.preventDefault() + event.stopPropagation() + + + if( undoEl && circuit.history.undo$() ){ + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + } + if( redoEl && circuit.history.redo$() ){ + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + } + if( controlEl ) Editor.createControl( circuitEl ) + if( swapEl ) Editor.createSwap( circuitEl ) + if( addMomentEl ) console.log( '→ Add moment' ) + if( addRegisterEl ) console.log( '→ Add register' ) + + + // We’re done dealing with external buttons. + // So if we can’t find a circuit CELL + // then there’s nothing more to do here. + + if( !cellEl ) return + + // Once we know what cell we’ve pressed on + // we can get the momentIndex and registerIndex + // from its pre-defined attributes. + // NOTE that we are getting CSS grid column and row + // from our own conversion function and NOT from + // asking its styles. Why? Because browsers convert + // grid commands to a shorthand less easily parsable + // and therefore makes our code and reasoning + // more prone to quirks / errors. Trust me! + + const + momentIndex = +cellEl.getAttribute( 'moment-index' ), + registerIndex = +cellEl.getAttribute( 'register-index' ), + columnIndex = Editor.momentIndexToGridColumn( momentIndex ), + rowIndex = Editor.registerIndexToGridRow( registerIndex ) + + + // Looks like our circuit is NOT locked + // and we have a valid circuit CELL + // so let’s find everything else we could need. + + const + selectallEl = targetEl.closest( '.Q-circuit-selectall' ), + registersymbolEl = targetEl.closest( '.Q-circuit-register-label' ), + momentsymbolEl = targetEl.closest( '.Q-circuit-moment-label' ), + inputEl = targetEl.closest( '.Q-circuit-input' ), + operationEl = targetEl.closest( '.Q-circuit-operation' ) + + // +++++++++++++++ + // We’ll have to add some input editing capability later... + // Of course you can already do this in code! + // For now though most quantum code assumes all qubits + // begin with a value of zero so this is mostly ok ;) + + if( inputEl ){ + + console.log( '→ Edit input Qubit value at', registerIndex ) + return + } + + + // Let’s inspect a group of items via a CSS query. + // If any of them are NOT “selected” (highlighted) + // then select them all. + // But if ALL of them are already selected + // then UNSELECT them all. + + function toggleSelection( query ){ + + const + operations = Array.from( circuitEl.querySelectorAll( query )), + operationsSelectedLength = operations.reduce( function( sum, element ){ + + sum += +element.classList.contains( 'Q-circuit-cell-selected' ) + return sum + + }, 0 ) + + if( operationsSelectedLength === operations.length ){ + + operations.forEach( function( el ){ + el.classList.remove( 'Q-circuit-cell-selected' ) + }) + } + else { + + operations.forEach( function( el ){ + + el.classList.add( 'Q-circuit-cell-selected' ) + }) + } + Editor.onSelectionChanged( circuitEl ) + } + + + // Clicking on the “selectAll” button + // or any of the Moment symbols / Register symbols + // causes a selection toggle. + // In the future we may want to add + // dragging of entire Moment columns / Register rows + // to splice them out / insert them elsewhere + // when a user clicks and drags them. + + if( selectallEl ){ + + toggleSelection( '.Q-circuit-operation' ) + return + } + if( momentsymbolEl ){ + + toggleSelection( `.Q-circuit-operation[moment-index="${ momentIndex }"]` ) + return + } + if( registersymbolEl ){ + + toggleSelection( `.Q-circuit-operation[register-index="${ registerIndex }"]` ) + return + } + + + // Right here we can made a big decision: + // If you’re not pressing on an operation + // then GO HOME. + + if( !operationEl ) return + // If we've doubleclicked on an operation and the operation has parameters, we should be able + // to edit those parameters regardless of whether or not the circuit is locked. + if( event.detail == 2) { + const operation = Gate.findBySymbol(operationEl.getAttribute( 'gate-symbol' )) + if( operation.has_parameters ) { + Editor.onDoubleclick( event, operationEl ) + return + } + } + + // Ok now we know we are dealing with an operation. + // This preserved selection state information + // will be useful for when onPointerRelease is fired. + + if( operationEl.classList.contains( 'Q-circuit-cell-selected' )){ + operationEl.wasSelected = true + } + else operationEl.wasSelected = false + + + // And now we can proceed knowing that + // we need to select this operation + // and possibly drag it + // as well as any other selected operations. + + operationEl.classList.add( 'Q-circuit-cell-selected' ) + const selectedOperations = Array.from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )) + dragEl.circuitEl = circuitEl + dragEl.originEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) + + + // These are the default values; + // will be used if we’re only dragging one operation around. + // But if dragging more than one operation + // and we’re dragging the clipboard by an operation + // that is NOT in the upper-left corner of the clipboard + // then we need to know what the offset is. + // (Will be calculated below.) + + dragEl.columnIndexOffset = 1 + dragEl.rowIndexOffset = 1 + + + // Now collect all of the selected operations, + // rip them from the circuit board’s foreground layer + // and place them on the clipboard. + + let + columnIndexMin = Infinity, + rowIndexMin = Infinity + + selectedOperations.forEach( function( el ){ + + + // WORTH REPEATING: + // Once we know what cell we’ve pressed on + // we can get the momentIndex and registerIndex + // from its pre-defined attributes. + // NOTE that we are getting CSS grid column and row + // from our own conversion function and NOT from + // asking its styles. Why? Because browsers convert + // grid commands to a shorthand less easily parsable + // and therefore makes our code and reasoning + // more prone to quirks / errors. Trust me! + + const + momentIndex = +el.getAttribute( 'moment-index' ), + registerIndex = +el.getAttribute( 'register-index' ), + columnIndex = Editor.momentIndexToGridColumn( momentIndex ), + rowIndex = Editor.registerIndexToGridRow( registerIndex ) + + columnIndexMin = Math.min( columnIndexMin, columnIndex ) + rowIndexMin = Math.min( rowIndexMin, rowIndex ) + el.classList.remove( 'Q-circuit-cell-selected' ) + el.origin = { momentIndex, registerIndex, columnIndex, rowIndex } + dragEl.appendChild( el ) + }) + selectedOperations.forEach( function( el ){ + + const + columnIndexForClipboard = 1 + el.origin.columnIndex - columnIndexMin, + rowIndexForClipboard = 1 + el.origin.rowIndex - rowIndexMin + + el.style.gridColumn = columnIndexForClipboard + el.style.gridRow = rowIndexForClipboard + + + // If this operation element is the one we grabbed + // (mostly relevant if we’re moving multiple operations at once) + // we need to know what the “offset” so everything can be + // placed correctly relative to this drag-and-dropped item. + + if( el.origin.columnIndex === columnIndex && + el.origin.rowIndex === rowIndex ){ + + dragEl.columnIndexOffset = columnIndexForClipboard + dragEl.rowIndexOffset = rowIndexForClipboard + } + }) + + + // We need an XY offset that describes the difference + // between the mouse / finger press position + // and the clipboard’s intended upper-left position. + // To do that we need to know the press position (obviously!), + // the upper-left bounds of the circuit board’s foreground, + // and the intended upper-left bound of clipboard. + + const + boardEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ), + bounds = boardEl.getBoundingClientRect(), + minX = Editor.gridToPoint( columnIndexMin ), + minY = Editor.gridToPoint( rowIndexMin ) + + dragEl.offsetX = bounds.left + minX - x + dragEl.offsetY = bounds.top + minY - y + dragEl.momentIndex = momentIndex + dragEl.registerIndex = registerIndex + } + else if( paletteEl ){ + const operationEl = targetEl.closest( '.Q-circuit-operation' ) + + if( !operationEl ) return + + const + bounds = operationEl.getBoundingClientRect(), + { x, y } = Editor.getInteractionCoordinates( event ) + + dragEl.appendChild( operationEl.cloneNode( true )) + dragEl.originEl = paletteEl + dragEl.offsetX = bounds.left - x + dragEl.offsetY = bounds.top - y + } + else if( parameterEl ){ + const exitEl = targetEl.closest( '.Q-parameter-box-exit' ) + if( !exitEl ) return + parameterEl.style.display = 'none' + const foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) + operationEl = foregroundEl.querySelector( `[moment-index="${ parameterEl.getAttribute( 'operation-moment-index' )}"]` + + `[register-index="${ parameterEl.getAttribute( 'operation-register-index' )}"]` ) + parameters = {} + operationSkeleton = Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' )) + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = operationEl.getAttribute( key ) ? operationEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + //upon exiting, we should update the circuit!!! + circuitEl.circuit.set$( + operationEl.getAttribute( 'gate-symbol' ), + +operationEl.getAttribute( 'moment-index' ), + operationEl.getAttribute( 'register-indices' ) ? operationEl.getAttribute( 'register-indices' ).split(',').map( i => +i ) : + [ +operationEl.getAttribute( 'register-index' )], + parameters + ) + //on exiting the parameter-input-box, we should update the circuit!! + parameterEl.innerHTML = "" + return + } + dragEl.timestamp = Date.now() + + + // Append the clipboard to the document, + // establish a global reference to it, + // and trigger a draw of it in the correct spot. + + document.body.appendChild( dragEl ) + Editor.dragEl = dragEl + Editor.onPointerMove( event ) +} + + + + + + + ///////////////////////// + // // + // Pointer RELEASE // + // // +///////////////////////// + + +Editor.onPointerRelease = function( event ){ + + + // If there’s no dragEl then bail immediately. + if( Editor.dragEl === null ) return + // Looks like we’re moving forward with this plan, + // so we’ll take control of the input now. + event.preventDefault() + event.stopPropagation() + + + // We can’t get the drop target from the event. + // Think about it: What was under the mouse / finger + // when this drop event was fired? THE CLIPBOARD ! + // So instead we need to peek at what elements are + // under the mouse / finger, skipping element [0] + // because that will be the clipboard. + + const { x, y } = Editor.getInteractionCoordinates( event ) + const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container") + if( boardContainerAll.length === 0 ) return + let boardContainerEl = Array.from(boardContainerAll).find((element) => { + let rect = element.getBoundingClientRect() + let clientX = rect.left + let clientY = rect.top + let height = element.offsetHeight + let width = element.offsetWidth + return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height ) + }) + returnToOrigin = function(){ + + + // We can only do a “true” return to origin + // if we were dragging from a circuit. + // If we were dragging from a palette + // we can just stop dragging. + + if( Editor.dragEl.circuitEl ){ + + Array.from( Editor.dragEl.children ).forEach( function( el ){ + + Editor.dragEl.originEl.appendChild( el ) + el.style.gridColumn = el.origin.columnIndex + el.style.gridRow = el.origin.rowIndex + if( el.wasSelected === true ) el.classList.remove( 'Q-circuit-cell-selected' ) + else el.classList.add( 'Q-circuit-cell-selected' ) + }) + Editor.onSelectionChanged( Editor.dragEl.circuitEl ) + } + document.body.removeChild( Editor.dragEl ) + Editor.dragEl = null + } + + + // If we have not dragged on to a circuit board + // then we’re throwing away this operation. + + if( !boardContainerEl ){ + + if( Editor.dragEl.circuitEl ){ + + const + originCircuitEl = Editor.dragEl.circuitEl + originCircuit = originCircuitEl.circuit + + originCircuit.history.createEntry$() + Array + .from( Editor.dragEl.children ) + .forEach( function( child ){ + + originCircuit.clear$( + + child.origin.momentIndex, + child.origin.registerIndex + ) + }) + Editor.onSelectionChanged( originCircuitEl ) + Editor.onCircuitChanged( originCircuitEl ) + } + + + // TIME TO DIE. + // Let’s keep a private reference to + // the current clipboard. + + let clipboardToDestroy = Editor.dragEl + + + // Now we can remove our dragging reference. + + Editor.dragEl = null + + + // Add our CSS animation routine + // which will run for 1 second. + // If we were SUPER AWESOME + // we would have also calculated drag momentum + // and we’d let this glide away! + + clipboardToDestroy.classList.add( 'Q-circuit-clipboard-destroy' ) + + + // And around the time that animation is completing + // we can go ahead and remove our clipboard from the DOM + // and kill the reference. + + setTimeout( function(){ + + document.body.removeChild( clipboardToDestroy ) + clipboardToDestroy = null + + }, 500 ) + + + // No more to do here. Goodbye. + + return + } + + + // If we couldn’t determine a circuitEl + // from the drop target, + // or if there is a target circuit but it’s locked, + // then we need to return these dragged items + // to their original circuit. + + const circuitEl = boardContainerEl.closest( '.Q-circuit' ) + if( circuitEl.classList.contains( 'Q-circuit-locked' )){ + + returnToOrigin() + return + } + + + // Time to get serious. + // Where exactly are we dropping on to this circuit?? + + const + circuit = circuitEl.circuit, + bounds = boardContainerEl.getBoundingClientRect(), + droppedAtX = x - bounds.left + boardContainerEl.scrollLeft, + droppedAtY = y - bounds.top + boardContainerEl.scrollTop, + droppedAtMomentIndex = Editor.gridColumnToMomentIndex( + + Editor.pointToGrid( droppedAtX ) + ), + droppedAtRegisterIndex = Editor.gridRowToRegisterIndex( + + Editor.pointToGrid( droppedAtY ) + ), + foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) + + + // If this is a self-drop + // we can also just return to origin and bail. + + if( Editor.dragEl.circuitEl === circuitEl && + Editor.dragEl.momentIndex === droppedAtMomentIndex && + Editor.dragEl.registerIndex === droppedAtRegisterIndex ){ + + returnToOrigin() + return + } + + + // Is this a valid drop target within this circuit? + + if( + droppedAtMomentIndex < 1 || + droppedAtMomentIndex > circuit.timewidth || + droppedAtRegisterIndex < 1 || + droppedAtRegisterIndex > circuit.bandwidth + ){ + returnToOrigin() + return + } + + + // Finally! Work is about to be done! + // All we need to do is tell the circuit itself + // where we need to place these dragged items. + // It will do all the validation for us + // and then fire events that will place new elements + // where they need to go! + + const + draggedOperations = Array.from( Editor.dragEl.children ), + draggedMomentDelta = droppedAtMomentIndex - Editor.dragEl.momentIndex, + draggedRegisterDelta = droppedAtRegisterIndex - Editor.dragEl.registerIndex, + setCommands = [] + + + // Whatever the next action is that we perform on the circuit, + // this was user-initiated via the graphic user interface (GUI). + + circuit.history.createEntry$() + + + // Now let’s work our way through each of the dragged operations. + // If some of these are components of a multi-register operation + // the sibling components will get spliced out of the array + // to avoid processing any specific operation more than once. + + draggedOperations.forEach( function( childEl, i ){ + + let + momentIndexTarget = droppedAtMomentIndex, + registerIndexTarget = droppedAtRegisterIndex + + if( Editor.dragEl.circuitEl ){ + + momentIndexTarget += childEl.origin.momentIndex - Editor.dragEl.momentIndex + registerIndexTarget += childEl.origin.registerIndex - Editor.dragEl.registerIndex + } + + + // Is this a multi-register operation? + // If so, this is also a from-circuit drop + // rather than a from-palette drop. + + const registerIndicesString = childEl.getAttribute( 'register-indices' ) + if( registerIndicesString ){ + + // What are ALL of the registerIndices + // associated with this multi-register operation? + // (We may use them later as a checklist.) + + const + registerIndices = registerIndicesString + .split( ',' ) + .map( function( str ){ return +str }), + + + // Lets look for ALL of the sibling components of this operation. + // Later we’ll check and see if the length of this array + // is equal to the total number of components for this operation. + // If they’re equal then we know we’re dragging the WHOLE thing. + // Otherwise we need to determine if it needs to break apart + // and if so, what that nature of that break might be. + + foundComponents = Array.from( + + Editor.dragEl.querySelectorAll( + + `[moment-index="${ childEl.origin.momentIndex }"]`+ + `[register-indices="${ registerIndicesString }"]` + ) + ) + .sort( function( a, b ){ + + const + aRegisterIndicesIndex = +a.getAttribute( 'register-indices-index' ), + bRegisterIndicesIndex = +b.getAttribute( 'register-indices-index' ) + + return aRegisterIndicesIndex - bRegisterIndicesIndex + }), + allComponents = Array.from( Editor.dragEl.circuitEl.querySelectorAll( + + `[moment-index="${ childEl.origin.momentIndex }"]`+ + `[register-indices="${ registerIndicesString }"]` + )), + remainingComponents = allComponents.filter( function( componentEl, i ){ + + return !foundComponents.includes( componentEl ) + }), + + + // We can’t pick the gate symbol + // off the 0th gate in the register indices array + // because that will be an identity / control / null gate. + // We need to look at slot 1. + + component1 = Editor.dragEl.querySelector( + + `[moment-index="${ childEl.origin.momentIndex }"]`+ + `[register-index="${ registerIndices[ 1 ] }"]` + ), + gatesymbol = component1 ? + component1.getAttribute( 'gate-symbol' ) : + childEl.getAttribute( 'gate-symbol' ) + + + // We needed to grab the above gatesymbol information + // before we sent any clear$ commands + // which would in turn delete those componentEls. + // We’ve just completed that, + // so now’s the time to send a clear$ command + // before we do any set$ commands. + + draggedOperations.forEach( function( childEl ){ + + Editor.dragEl.circuitEl.circuit.clear$( + + childEl.origin.momentIndex, + childEl.origin.registerIndex + ) + }) + + + // FULL MULTI-REGISTER DRAG (TO ANY POSITION ON ANY CIRCUIT). + // If we are dragging all of the components + // of a multi-register operation + // then we are good to go. + + if( registerIndices.length === foundComponents.length ){ + + const operationSkeleton = Gate.findBySymbol( gatesymbol ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + //circuit.set$( + setCommands.push([ + + gatesymbol, + momentIndexTarget, + + + // We need to remap EACH register index here + // according to the drop position. + // Let’s let set$ do all the validation on this. + + registerIndices.map( function( registerIndex ){ + + const siblingDelta = registerIndex - childEl.origin.registerIndex + registerIndexTarget = droppedAtRegisterIndex + if( Editor.dragEl.circuitEl ){ + + registerIndexTarget += childEl.origin.registerIndex - Editor.dragEl.registerIndex + siblingDelta + } + return registerIndexTarget + }), + parameters + // ) + ]) + } + + + // IN-MOMENT (IN-CIRCUIT) PARTIAL MULTI-REGISTER DRAG. + // It appears we are NOT dragging all components + // of a multi-register operation. + // But if we’re dragging within the same circuit + // and we’re staying within the same moment index + // that might be ok! + + else if( Editor.dragEl.circuitEl === circuitEl && + momentIndexTarget === childEl.origin.momentIndex ){ + + + // We must ensure that only one component + // can sit at each register index. + // This copies registerIndices, + // but inverts the key : property relationship. + const registerMap = registerIndices + .reduce( function( registerMap, registerIndex, r ){ + + registerMap[ registerIndex ] = r + return registerMap + + }, {} ) + + + // First, we must remove each dragged component + // from the register it was sitting at. + + foundComponents.forEach( function( component ){ + + const componentRegisterIndex = +component.getAttribute( 'register-index' ) + + + // Remove this component from + // where this component used to be. + + delete registerMap[ componentRegisterIndex ] + }) + + + // Now we can seat it at its new position. + // Note: This may OVERWRITE one of its siblings! + // And that’s ok. + foundComponents.forEach( function( component ){ + + const + componentRegisterIndex = +component.getAttribute( 'register-index' ), + registerGrabDelta = componentRegisterIndex - Editor.dragEl.registerIndex + + + // Now put it where it wants to go, + // possibly overwriting a sibling component! + //ltnln: if a multiqubit operation component that requires a sibling, overwrites its sibling, both/all components should be destroyed + registerMap[ + + componentRegisterIndex + draggedRegisterDelta + + ] = +component.getAttribute( 'register-indices-index' ) + }) + + + // Now let’s flip that registerMap + // back into an array of register indices. + + const fixedRegistersIndices = Object.entries( registerMap ) + .reduce( function( registers, entry, i ){ + + registers[ +entry[ 1 ]] = +entry[ 0 ] + return registers + + }, [] ) + + + // This will remove any blank entries in the array + // ie. if a dragged sibling overwrote a seated one. + + .filter( function( entry ){ + return mathf.isUsefulInteger( entry ) + }) + + const operationSkeleton = Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + // Finally, we’re ready to set. + // circuit.set$( + setCommands.push([ + //ltnln: if a component of an operation that requires a sibling pair overwrites its sibling, we want it removed entirely. + fixedRegistersIndices.length < 2 && Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? + Gate.NOOP : + childEl.getAttribute( 'gate-symbol' ), + momentIndexTarget, + fixedRegistersIndices, + parameters + // ) + ]) + } + else { + remainingComponents.forEach( function( componentEl, i ){ + //circuit.set$( + const operationSkeleton = Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + setCommands.push([ + + +componentEl.getAttribute( 'register-indices-index' ) && !Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? + gatesymbol : + Gate.NOOP, + +componentEl.getAttribute( 'moment-index' ), + +componentEl.getAttribute( 'register-index' ), + parameters + // ) + ]) + }) + + + // Finally, let’s separate and update + // all the components that were part of the drag. + + foundComponents.forEach( function( componentEl ){ + const operationSkeleton = Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + setCommands.push([ + //ltnln: temporary fix: certain multiqubit operations should only be represented in pairs of registers. If one is removed (i.e. a single component of the pair) + //then the entire operation should be removed. + +componentEl.getAttribute( 'register-indices-index' ) && !Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? + componentEl.getAttribute( 'gate-symbol' ) : + Gate.NOOP, + +componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta, + +componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta, + parameters + // ) + ]) + }) + } + + + // We’ve just completed the movement + // of a multi-register operation. + // But all of the sibling components + // will also trigger this process + // unless we remove them + // from the draggd operations array. + + let j = i + 1 + while( j < draggedOperations.length ){ + + const possibleSibling = draggedOperations[ j ] + if( possibleSibling.getAttribute( 'gate-symbol' ) === gatesymbol && + possibleSibling.getAttribute( 'register-indices' ) === registerIndicesString ){ + + draggedOperations.splice( j, 1 ) + } + else j ++ + } + } + + + // This is just a single-register operation. + // How simple this looks + // compared to all the gibberish above. + + else { + + + // First, if this operation comes from a circuit + // (and not a circuit palette) + // make sure the old positions are cleared away. + + if( Editor.dragEl.circuitEl ){ + + draggedOperations.forEach( function( childEl ){ + + Editor.dragEl.circuitEl.circuit.clear$( + + childEl.origin.momentIndex, + childEl.origin.registerIndex + ) + }) + } + + + // And now set$ the operation + // in its new home. + + // circuit.set$( + let registerIndices = [ registerIndexTarget ] + //ltnln: By default, multiqubit gates appear in pairs on the circuit rather than + // requiring the user to have to pair them like with Swap/CNot. + const operationSkeleton = Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' )) + if(operationSkeleton.is_multi_qubit ) { + registerIndices.push( registerIndexTarget == circuit.bandwidth ? registerIndexTarget - 1 : registerIndexTarget + 1) + } + let parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } + setCommands.push([ + childEl.getAttribute( 'gate-symbol' ), + momentIndexTarget, + registerIndices, + parameters + // ) + ]) + } + }) + + + // DO IT DO IT DO IT + + setCommands.forEach( function( setCommand ){ + + circuit.set$.apply( circuit, setCommand ) + }) + + + // Are we capable of making controls? Swaps? + + Editor.onSelectionChanged( circuitEl ) + Editor.onCircuitChanged( circuitEl ) + + + // If the original circuit and destination circuit + // are not the same thing + // then we need to also eval the original circuit. + + if( Editor.dragEl.circuitEl && + Editor.dragEl.circuitEl !== circuitEl ){ + + const originCircuitEl = Editor.dragEl.circuitEl + Editor.onSelectionChanged( originCircuitEl ) + Editor.onCircuitChanged( originCircuitEl ) + } + + + // We’re finally done here. + // Clean up and go home. + // It’s been a long journey. + // I love you all. + + document.body.removeChild( Editor.dragEl ) + Editor.dragEl = null +} + + + ///////////////////////// + // // + // Pointer DOUBLECLICK // + // // +///////////////////////// +//ltnln: my trying out an idea for parameterized gates... +Editor.onDoubleclick = function( event, operationEl ) { + const operation = Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' )) + const { x, y } = Editor.getInteractionCoordinates( event ) + const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container") + if( boardContainerAll.length === 0 ) return + let boardContainerEl = Array.from(boardContainerAll).find((element) => { + let rect = element.getBoundingClientRect() + let clientX = rect.left + let clientY = rect.top + let height = element.offsetHeight + let width = element.offsetWidth + return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height ) + }) + if( !boardContainerEl ) return; + const parameterEl = boardContainerEl.querySelector('.Q-parameters-box') + const exit = Editor.createNewElement( 'button', parameterEl, 'Q-parameter-box-exit') + exit.appendChild(document.createTextNode( '⬅' )) + parameterEl.setAttribute( "operation-moment-index", operationEl.getAttribute( 'moment-index' )) + parameterEl.setAttribute( "operation-register-index", operationEl.getAttribute( 'register-index' )) + //here we generate queries for each parameter that the gate operation takes! + const parameters = Object.keys(operation.parameters) + parameters.forEach( element => { + if( operation.parameters && operation.parameters[element] !== null ) { + const input_fields = document.createElement( 'div' ) + parameterEl.appendChild( input_fields ) + input_fields.classList.add( 'Q-parameter-box-input-container' ) + + const label = Editor.createNewElement( "span", input_fields, 'Q-parameter-input-label' ) + label.appendChild(document.createTextNode( element )) + + const textbox = Editor.createNewElement( "input", input_fields, 'Q-parameter-box-input') + textbox.setAttribute( 'type', 'text' ) + textbox.setAttribute( 'placeholder', element ) + textbox.setAttribute( 'value', operationEl.getAttribute(element) ? operationEl.getAttribute(element) : operation.parameters[element] ) + //set textbox to update the operation instance (cellEl)'s parameters on value change + textbox.addEventListener( "change", () => { + let parameterValue = +textbox.value; + let oldValue = operationEl.getAttribute( element ) + if( !oldValue ) oldValue = operation.parameters[ element ] + if( parameterValue === null || parameterValue === Infinity ) textbox.value = oldValue.toString() + else { + operationEl.setAttribute( element, parameterValue ) + textbox.value = parameterValue + } + }) + + + } + }) + parameterEl.classList.toggle('overlay') + parameterEl.style.display = 'block' +} + + + + /////////////////// + // // + // Listeners // + // // +/////////////////// + + +// These listeners must be appliedm +// to the entire WINDOW (and not just document.body!) + +window.addEventListener( 'mousemove', Editor.onPointerMove ) +window.addEventListener( 'touchmove', Editor.onPointerMove ) +window.addEventListener( 'mouseup', Editor.onPointerRelease ) +window.addEventListener( 'touchend', Editor.onPointerRelease ) +module.exports = {Editor} +},{"quantum-js-util":13}],16:[function(require,module,exports){ +const {Editor} = require('./Q-Circuit-Editor'); +const {circuit} = require('quantum-js-util'); +const {BlochSphere} = require('./Q-BlochSphere'); +console.log("Welcome to Q.js! The GUI experience!\n"); + +braket = function(){ + + + // Create the HTML bits we need, + // contain them all together, + // and output them to Jupyter. + if( arguments.length === 0 || arguments.length > 3 ) return; + const element = arguments[0]; + const args = (Array.from(arguments)).slice(1); + let circuit + if(args.length === 0) { + circuit = new Q( 4, 8 ) + } + else if(args.length === 1) { + circuit = new Q( args[0] ) + } + else { + circuit = new Q( args[0], args[1] ) + } + container = document.createElement( 'div' ) + container.appendChild( Editor.createPalette() ) + container.appendChild( circuit.toDom() ) + element.html( container ) + document.querySelectorAll('.Q-circuit-palette').forEach( paletteEl => paletteEl.style.width = "50%") + + // We’re going to take this SLOOOOOOOOWLY + // because there are many potential things to debug. + + const thisCell = Jupyter.notebook.get_selected_cell() + // console.log( 'thisCell', thisCell ) + + const thisCellIndex = Jupyter.notebook.get_cells().indexOf( thisCell ) + // console.log( 'thisCellIndex', thisCellIndex ) + + const nextCell = Jupyter.notebook.insert_cell_below( 'code', thisCellIndex - 1 ) + const nextNextCell = Jupyter.notebook.insert_cell_below( 'markdown', Jupyter.notebook.get_cells().indexOf( thisCell ) - 1 ) + // console.log( 'nextCell', nextCell ) + + nextCell.set_text( circuit.toAmazonBraket() ) + nextNextCell.set_text( circuit.report$() ) + + + + + + + window.addEventListener( 'Q gui altered circuit', function( event ){ + + // updatePlaygroundFromDom( event.detail.circuit ) + if( event.detail.circuit === circuit ){ + + console.log( 'Updating circuit from GUI', circuit ) + circuit.evaluate$() + nextCell.set_text( circuit.toAmazonBraket() ) + + } + }) + + window.addEventListener( 'Circuit.evaluate completed', function( event ) { + if( event.detail.circuit === circuit ) { + nextNextCell.set_text( circuit.report$() ) + } + }) + + + + // nextCell.render() + + // console.log( 'thisCell', thisCell ) + // console.log( 'nextCell', nextCell ) + // console.log( 'thisCellIndex', thisCellIndex ) + + // code = Jupyter.notebook.insert_cell_{0}('code'); + // code.set_text(atob("{1}")) + + // var t_cell = Jupyter.notebook.get_selected_cell() + // t_cell.set_text(' \\n{}') + // var t_index = Jupyter.notebook.get_cells().indexOf(t_cell) + // Jupyter.notebook.to_markdown(t_index) + // Jupyter.notebook.get_cell(t_index).render() +} + + +module.exports = {Editor, BlochSphere, braket}; +},{"./Q-BlochSphere":14,"./Q-Circuit-Editor":15,"quantum-js-util":13}]},{},[1]); From c36ecd99e3d4e0ba9ade94110e999f174be1f3cb Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 21:45:33 +0000 Subject: [PATCH 10/22] Update gate to not create default amazonBraketSymbols for gates. --- packages/quantum-js-util/Q-Gate.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/quantum-js-util/Q-Gate.js b/packages/quantum-js-util/Q-Gate.js index 472dde1..43190ff 100644 --- a/packages/quantum-js-util/Q-Gate.js +++ b/packages/quantum-js-util/Q-Gate.js @@ -11,7 +11,6 @@ Gate = function( params ){ this.index = Gate.index ++ if( typeof this.symbol !== 'string' ) this.symbol = '?' - if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase() const parameters = Object.assign( {}, params.parameters ) this.parameters = parameters From 1eb920332a496b5ce33084d25bfc7104999cab3e Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 21:51:18 +0000 Subject: [PATCH 11/22] Update bundle.js, no longer creates default AmazonBraketSymbols for Qjs --- build/bundle.js | 1 - 1 file changed, 1 deletion(-) diff --git a/build/bundle.js b/build/bundle.js index 5a90412..e728e18 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -3454,7 +3454,6 @@ Gate = function( params ){ this.index = Gate.index ++ if( typeof this.symbol !== 'string' ) this.symbol = '?' - if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase() const parameters = Object.assign( {}, params.parameters ) this.parameters = parameters From 19eff8356c4b4d92a9101a28094ab3b2d5d1bba7 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 22:05:10 +0000 Subject: [PATCH 12/22] Reduce width of palette on creation in a JupyterNotebook via braket() function --- build/bundle.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/bundle.js b/build/bundle.js index e728e18..07d46b7 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -8170,10 +8170,12 @@ braket = function(){ circuit = new Q( args[0], args[1] ) } container = document.createElement( 'div' ) - container.appendChild( Editor.createPalette() ) + let paletteEl = Editor.createPalette(); + paletteEl.style.width = "50%"; + container.appendChild( paletteEl ); container.appendChild( circuit.toDom() ) element.html( container ) - document.querySelectorAll('.Q-circuit-palette').forEach( paletteEl => paletteEl.style.width = "50%") + // We’re going to take this SLOOOOOOOOWLY // because there are many potential things to debug. From c3c08fa66407434a9ee925705ce6c8a1c3d00ddc Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 11 Aug 2021 22:11:29 +0000 Subject: [PATCH 13/22] Reduce width of palette on creation in a JupyterNotebook via braket() function --- packages/quantum-js-vis/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/quantum-js-vis/index.js b/packages/quantum-js-vis/index.js index 6d200e6..6838703 100644 --- a/packages/quantum-js-vis/index.js +++ b/packages/quantum-js-vis/index.js @@ -23,10 +23,12 @@ braket = function(){ circuit = new Q( args[0], args[1] ) } container = document.createElement( 'div' ) - container.appendChild( Editor.createPalette() ) + let paletteEl = Editor.createPalette(); + paletteEl.style.width = "50%"; + container.appendChild( paletteEl ); container.appendChild( circuit.toDom() ) element.html( container ) - document.querySelectorAll('.Q-circuit-palette').forEach( paletteEl => paletteEl.style.width = "50%") + // We’re going to take this SLOOOOOOOOWLY // because there are many potential things to debug. From 059d22122f91dd6dec1bde4a4f367eb5d057f514 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 12 Aug 2021 22:52:06 +0000 Subject: [PATCH 14/22] Update eslint scripts in root and quantum-js-vis package --- package.json | 4 ++-- packages/quantum-js-vis/package.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index b89ee85..cb29f4e 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "main": "Q/Q.js", "scripts": { "test": "npm run test -ws", - "lint": "eslint", - "prettier": "prettier --write", + "lint": "npm run lint -ws", + "prettier": "npm run prettier -ws", "dev": "vite", "build": "npx browserify index.js > build/bundle.js && npx concat -o build/bundle.css packages/quantum-js-vis/Q.css packages/quantum-js-vis/Q-Circuit-Editor.css" }, diff --git a/packages/quantum-js-vis/package.json b/packages/quantum-js-vis/package.json index e9aea82..f17495b 100644 --- a/packages/quantum-js-vis/package.json +++ b/packages/quantum-js-vis/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "jest", - "prettier": "echo 'I am a prettier vis!'" + "prettier": "npx prettier --write **/*.js" , + "lint": "npx eslint **/*.js" }, "keywords": [], "author": "", From 49b52cd8ab66aef463b355dc6ea98e7ef56b38dd Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Fri, 13 Aug 2021 19:52:24 +0000 Subject: [PATCH 15/22] Revert Q.css to original Q.css to remove unicode discrepancies. Update bundle.css to match --- build/bundle.css | 885 +++++++++++++++++++++++++++++++++- package.json | 1 - packages/quantum-js-vis/Q.css | 885 +++++++++++++++++++++++++++++++++- 3 files changed, 1742 insertions(+), 29 deletions(-) diff --git a/build/bundle.css b/build/bundle.css index 1fb9f31..ba23b8d 100644 --- a/build/bundle.css +++ b/build/bundle.css @@ -1,6 +1,6 @@ /* - Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. */ @charset "utf-8"; @@ -11,8 +11,8 @@ /* This file is in the process of being separated - in to “essential global Q.js styles” which will - remain here in Q.css, and “documentation-specific” + in to “essential global Q.js styles” which will + remain here in Q.css, and “documentation-specific” styles which will be removed from here and placed within the /other/documentation.css file instead. @@ -319,7 +319,7 @@ svg, :root { /* - The below still need to be prefaced with “Q-” + The below still need to be prefaced with “Q-” and for the HTML pages to be updated accordingly. */ @@ -341,8 +341,6 @@ svg, :root { max-width: 100%; overflow-x: auto; font-family: var( --Q-font-family-sans ); - /*letter-spacing: 0.03em;*/ - word-spacing: 0.2em; } dd .maths { @@ -395,23 +393,22 @@ dd .maths { vertical-align: middle; position: relative; align: middle; - margin: 1em 0.5em; + margin: 1em; padding: 1em; - /*font-family: var( --Q-font-family-mono );*/ + font-family: var( --Q-font-family-mono ); font-weight: 300; line-height: 1em; - /*text-align: right;*/ - text-align: center; + text-align: right; } .matrix td { - padding: 0.25em 0.5em; + padding: 5px 10px; } .matrix-bracket-left, .matrix-bracket-right { position: absolute; top: 0; - width: 0.5em; + width: 5px; height: 100%; border: 1px solid #CCC; } @@ -440,7 +437,7 @@ dd .maths { .Q-state-vector.bra::before, .complex-vector.bra::before { - content: '⟨'; + content: '⟨'; color: #BBB; } .Q-state-vector.bra::after, @@ -458,7 +455,7 @@ dd .maths { .Q-state-vector.ket::after, .complex-vector.ket::after { - content: '⟩'; + content: '⟩'; color: #BBB; } .Q-state-vector.bra + .Q-state-vector.ket::before, @@ -488,6 +485,866 @@ dd .maths { +/* + + Z indices: + + Clipboard =100 + Selected op 10 + Operation 0 + Shadow -10 + Background -20 + + + + + + Circuit + + Menu Moments + ╭───────┬───┬───┬───┬───╮ + │ ≡ ↘ │ 1 │ 2 │ 3 │ + │ Add moment + ├───┬───┼───┼───┼───┼───╯ + R │ 0 │|0⟩│ H │ C0│ X │ - + e ├───┼───┼───┼───┼───┤ + g │ 1 │|0⟩│ I │ C1│ X │ - + s ├───┼───┴───┴───┴───┘ + │ + │ - - - - + ╰───╯ + Add + register + + + Circuit Palette + + ╭───────────────────┬───╮ + │ H X Y Z S T π M … │ @ │ + ╰───────────────────┴───╯ + + + Circuit clipboard + + ┌───────────────┐ + ▟│ ┌───┬───────┐ │ + █│ │ H │ X#0.0 │ │ + █│ ├───┼───────┤ │ + █│ │ I │ X#0.1 │ │ + █│ └───┴───────┘ │ + █└───────────────┘ + ███████████████▛ + + + + ◢◣ + ◢■■■■◣ +◢■■■■■■■■◣ +◥■■■■■■■■◤ + ◥■■■■◤ + ◥◤ + + + ◢■■■■■■◤ + ◢◤ ◢◤ +◢■■■■■■◤ + + + ─────────── + ╲ ╱ ╱ ╱ + ╳ ╱ ╱ + ╱ ╲╱ ╱ + ─────── + + + ─────⦢ + ╱ ╱ +⦣───── + + +*/ + + + + + + +.Q-circuit, +.Q-circuit-palette { + + position: relative; + width: 100%; +} +.Q-circuit-palette { + + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + line-height: 0; +} +.Q-circuit-palette > div { + + display: inline-block; + position: relative; + width: 4rem; + height: 4rem; +} + + +.Q-circuit { + + margin: 1rem 0 2rem 0; + /*border-top: 2px solid hsl( 0, 0%, 50% );*/ +} +.Q-circuit-board-foreground { + + line-height: 3.85rem; + width: auto; +} + + + + + + + /***************/ + /* */ + /* Toolbar */ + /* */ +/***************/ + + +.Q-circuit-toolbar { + + display: block; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + margin-bottom: 0.5rem; + + box-sizing: border-box; + display: grid; + grid-auto-columns: 3.6rem; + grid-auto-rows: 3.0rem; + grid-auto-flow: column; + +} +.Q-circuit-button { + + position: relative; + display: inline-block; + /*margin: 0 0.5rem 0.5rem 0;*/ + width: 3.6rem; + height: 3rem; +/* box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 0.8 ), + 0.1rem 0.1rem 0.1rem rgba( 0, 0, 0, 0.35 );*/ + + border-top: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 100% + ); + border-right: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 90% + ); + border-bottom: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 85% + ); + border-left: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 97% + ); + background: var( --Q-color-background ); +/* background: + var( --Q-color-background ) + linear-gradient( + + 0.4turn, + + rgba( 0, 0, 0, 0.02 ), + rgba( 255, 255, 255, 0.1 ) + );*/ + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 30% + ); + text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 ); + /*border-radius: 0.5rem;*/ + /*border-radius: 100%;*/ + line-height: 2.9rem; + text-align: center; + cursor: pointer; + overflow: hidden; + font-weight: 900; +} +.Q-circuit-toolbar .Q-circuit-button:first-child { + + border-top-left-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; +} +.Q-circuit-toolbar .Q-circuit-button:last-child { + + border-top-right-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} +.Q-circuit-locked .Q-circuit-button, +.Q-circuit-button[Q-disabled] { + + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 85% + ); + cursor: not-allowed; +} +.Q-circuit-locked .Q-circuit-toggle-lock { + + color: inherit; + cursor: pointer; +} + + + + +.Q-circuit-board-container { + + position: relative; + margin: 0 0 2rem 0; + margin: 0; + width: 100%; + max-height: 60vh; + overflow: scroll; +} +.Q-circuit-board { + + position: relative; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +/*.Q-circuit-palette,*/ +.Q-circuit-board-foreground, +.Q-circuit-board-background, +.Q-circuit-clipboard { + + box-sizing: border-box; + display: grid; + grid-auto-rows: 4rem; + grid-auto-columns: 4rem; + grid-auto-flow: column; +} +/*.Q-circuit-palette,*/ +.Q-circuit-board-foreground, +.Q-circuit-board-background { + + position: relative; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.Q-circuit-clipboard { + + position: absolute; + z-index: 100; + min-width: 4rem; + min-height: 4rem; + transform: scale( 1.05 ); +} +.Q-circuit-clipboard, .Q-circuit-clipboard > div { + + cursor: grabbing; +} +.Q-circuit-clipboard-danger .Q-circuit-operation { + + background-color: var( --Q-color-yellow ); +} +.Q-circuit-clipboard-destroy { + + animation-name: Q-circuit-clipboard-poof; + animation-fill-mode: forwards; + animation-duration: 0.3s; + animation-iteration-count: 1; +} +@keyframes Q-circuit-clipboard-poof { + + 100% { + + transform: scale( 1.5 ); + opacity: 0; + } +} +.Q-circuit-board-background { + + /* + + Clipboard: 100 + Operation: 0 + Shadow: -10 + Background: -20 + + */ + position: absolute; + z-index: -20; + color: rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-board-background > div { + +/* transition: + background-color 0.2s, + color 0.2s;*/ +} +.Q-circuit-board-background .Q-circuit-cell-highlighted { + + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); + /*transition: none;*/ +} + + + + +.Q-circuit-register-wire { + + position: absolute; + top: calc( 50% - 0.5px ); + width: 100%; + height: 1px; + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 50% + ); +} + + + +.Q-circuit-palette > div, +.Q-circuit-clipboard > div, +.Q-circuit-board-foreground > div { + + text-align: center; +} + + + + + + + /***************/ + /* */ + /* Headers */ + /* */ +/***************/ + + +.Q-circuit-header { + + position: sticky; + z-index: 2; + margin: 0; + /*background-color: var( --Q-color-background );*/ + background-color: white; + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 75% + ); + font-family: var( --Q-font-family-mono ); +} +.Q-circuit-input.Q-circuit-cell-highlighted, +.Q-circuit-header.Q-circuit-cell-highlighted { + + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); + color: black; +} +.Q-circuit-selectall { + + z-index: 3; + margin: 0; + top: 0; + /*left: 4rem;*/ + /*grid-column: 2;*/ + left: 0; + grid-column-start: 1; + grid-column-end: 3; + grid-row: 1; + cursor: se-resize; +} +.Q-circuit-moment-label, +.Q-circuit-moment-add { + + grid-row: 1; + top: 0; + cursor: s-resize; +} +.Q-circuit-register-label, +.Q-circuit-register-add { + + grid-column: 2; + left: 4rem; + cursor: e-resize; +} +.Q-circuit-moment-add, +.Q-circuit-register-add { + + cursor: pointer; +} +.Q-circuit-moment-add, +.Q-circuit-register-add { + + display: none; +} +.Q-circuit-selectall, +.Q-circuit-moment-label, +.Q-circuit-moment-add { + + border-bottom: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); +} +.Q-circuit-selectall, +.Q-circuit-register-label, +.Q-circuit-register-add { + + border-right: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); +} +.Q-circuit-input { + + position: sticky; + z-index: 2; + grid-column: 1; + left: 0; + /*background-color: var( --Q-color-background );*/ + background-color: white; + font-size: 1.5rem; + font-weight: 900; + font-family: var( --Q-font-family-mono ); +} + + + + + + +.Q-circuit-operation-link-container { + + --Q-link-stroke: 3px; + --Q-link-radius: 100%; + + display: block; + position: relative; + left: calc( 50% - ( var( --Q-link-stroke ) / 2 )); + width: 50%; + height: 100%; + overflow: hidden; +} +.Q-circuit-operation-link-container.Q-circuit-cell-highlighted { + + background-color: transparent; +} +.Q-circuit-operation-link { + + display: block; + position: absolute; + width: calc( var( --Q-link-stroke ) * 2 ); + height: calc( 100% - 4rem + var( --Q-link-stroke )); + /*border: var( --Q-link-stroke ) solid hsl( 0, 0%, 50% );*/ + border: var( --Q-link-stroke ) solid hsl( + + var( --Q-color-background-hue ), + 10%, + 30% + ); + + /*border: var( --Q-link-stroke ) solid var( --Q-color-orange );*/ + + transform: translate( -50%, calc( 2rem - ( var( --Q-link-stroke ) / 2 ))); + transform-origin: center; +} +.Q-circuit-operation-link.Q-circuit-operation-link-curved { + + width: calc( var( --Q-link-radius ) - var( --Q-link-stroke )); + width: 200%; + border-radius: 100%; +} + + + + + + + /******************/ + /* */ + /* Operations */ + /* */ +/******************/ + + +.Q-circuit-operation { + + position: relative; + /*--Q-operation-color-hue: var( --Q-color-green-hue ); + --Q-operation-color-main: var( --Q-color-green );*/ + + --Q-operation-color-hue: var( --Q-color-blue-hue ); + --Q-operation-color-main: hsl( + + var( --Q-operation-color-hue ), + 10%, + 35% + ); + + --Q-operation-color-light: hsl( + + var( --Q-operation-color-hue ), + 10%, + 50% + ); + --Q-operation-color-dark: hsl( + + var( --Q-operation-color-hue ), + 10%, + 25% + ); + color: white; + text-shadow: -0.05rem -0.05rem 0 rgba( 0, 0, 0, 0.1 ); + font-size: 1.5rem; + line-height: 2.9rem; + font-weight: 900; + cursor: grab; +} +.Q-circuit-locked .Q-circuit-operation { + + cursor: not-allowed; +} +.Q-circuit-operation-tile { + + position: absolute; + top: 0.5rem; + left: 0.5rem; + right: 0.5rem; + bottom: 0.5rem; + + /*margin: 0.5rem;*/ + /*padding: 0.5rem;*/ + + /*box-shadow: 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.2 );*/ + border-radius: 0.2rem; + /* + border-top: 0.1rem solid var( --Q-operation-color-light ); + border-left: 0.1rem solid var( --Q-operation-color-light ); + border-right: 0.1rem solid var( --Q-operation-color-dark ); + border-bottom: 0.1rem solid var( --Q-operation-color-dark ); + */ + background: + var( --Q-operation-color-main ) + /*linear-gradient( + + 0.45turn, + rgba( 255, 255, 255, 0.1 ), + rgba( 0, 0, 0, 0.05 ) + )*/; +} +.Q-circuit-palette .Q-circuit-operation:hover { + + /*background-color: rgba( 255, 255, 255, 0.6 );*/ + background-color: white; +} +.Q-circuit-palette .Q-circuit-operation-tile { + + --Q-before-rotation: 12deg; + --Q-before-x: 1px; + --Q-before-y: -2px; + + --Q-after-rotation: -7deg; + --Q-after-x: -2px; + --Q-after-y: 3px; + + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-palette .Q-circuit-operation-tile:before, +.Q-circuit-palette .Q-circuit-operation-tile:after { + + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: 0.2rem; + /*background-color: hsl( 0, 0%, 60% );*/ + + background-color: var( --Q-operation-color-dark ); + transform: + translate( var( --Q-before-x ), var( --Q-before-y )) + rotate( var( --Q-before-rotation )); + z-index: -10; + /*z-index: 10;*/ + display: block; + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-palette .Q-circuit-operation-tile:after { + + transform: + translate( var( --Q-after-x ), var( --Q-after-y )) + rotate( var( --Q-after-rotation )); + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-operation:hover .Q-circuit-operation-tile { + + color: white; +} + + + + +.Q-circuit-operation-hadamard .Q-circuit-operation-tile { + + /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ + /*--Q-operation-color-main: var( --Q-color-red );*/ + + /*--Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 10% );*/ + + +/* background: + linear-gradient( + + -33deg, + var( --Q-color-blue ) 20%, + #6f3c69 50%, + var( --Q-color-red ) 80% + );*/ +} +.Q-circuit-operation-identity .Q-circuit-operation-tile, +.Q-circuit-operation-control .Q-circuit-operation-tile, +.Q-circuit-operation-target .Q-circuit-operation-tile { + + /*--Q-operation-color-hue: var( --Q-color-orange-hue );*/ + /*--Q-operation-color-main: var( --Q-color-orange );*/ + border-radius: 100%; +} +.Q-circuit-operation-identity .Q-circuit-operation-tile, +.Q-circuit-operation-control .Q-circuit-operation-tile { + + top: calc( 50% - 0.7rem ); + left: calc( 50% - 0.7rem ); + width: 1.4rem; + height: 1.4rem; + overflow: hidden; +/* --Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 10% );*/ +} +.Q-circuit-operation-pauli-x, +.Q-circuit-operation-pauli-y, +.Q-circuit-operation-pauli-z { + + /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ + /*--Q-operation-color-main: var( --Q-color-red );*/ + +/* --Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 30% );*/ +} +.Q-circuit-operation-swap .Q-circuit-operation-tile { + + top: calc( 50% - 0.55rem ); + left: calc( 50% - 0.55rem ); + width: 1.2rem; + height: 1.2rem; + border-radius: 0; + transform-origin: center; + transform: rotate( 45deg ); + font-size: 0; +} + + + + + + + /********************/ + /* */ + /* Other states */ + /* */ +/********************/ + + +.Q-circuit-palette > div:hover, +.Q-circuit-board-foreground > div:hover { + + outline: 2px solid var( --Q-hyperlink-internal-color ); + outline-offset: -2px; +} +.Q-circuit-palette > div:hover .Q-circuit-operation-tile { + + box-shadow: none; +} +/*.Q-circuit-palette > div:hover,*/ +.Q-circuit-board-foreground > div:hover { + + background-color: white; + color: black; +} + + + + + + +.Q-circuit-clipboard > div, +.Q-circuit-cell-selected { + + background-color: white; +} +.Q-circuit-clipboard > div:before, +.Q-circuit-cell-selected:before { + + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: block; + z-index: -10; + box-shadow: + 0 0 1rem rgba( 0, 0, 0, 0.2 ), + 0.4rem 0.4rem 0.2rem rgba( 0, 0, 0, 0.2 ); + outline: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 50% + ); + /*outline-offset: -1px;*/ +} + + + + +.Q-circuit-clipboard > div { + + background-color: white; +} +.Q-circuit-clipboard > div:before { + + /* + + This was very helpful! + https://blog.dudak.me/2014/css-shadows-under-adjacent-elements/ + + */ + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -10; + display: block; + box-shadow: 0.4rem 0.4rem 0.3rem rgba( 0, 0, 0, 0.2 ); +} + + + + + + /***************/ + /* */ + /* Buttons */ + /* */ +/***************/ + + +.Q-circuit-locked .Q-circuit-toggle-lock, +.Q-circuit-locked .Q-circuit-toggle-lock:hover { + + background-color: var( --Q-color-red ); +} +.Q-circuit-toggle-lock { + + z-index: 3; + left: 0; + top: 0; + grid-column: 1; + grid-row: 1; + cursor: pointer; + font-size: 1.1rem; + text-shadow: none; + font-weight: normal; +} +.Q-circuit-button-undo, +.Q-circuit-button-redo { + + font-size: 1.2rem; + line-height: 2.6rem; + font-weight: normal; +} + + + +.Q-circuit p { + + padding: 1rem; + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 66% + ); +} + + + +/* + + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +*/ +@charset "utf-8"; + + + + + + + + + /* Z indices: diff --git a/package.json b/package.json index cb29f4e..82cdd1c 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "prettier": "2.3.2" }, "dependencies": { - "requirejs": "^2.3.6" }, "workspaces": [ "./packages/*" diff --git a/packages/quantum-js-vis/Q.css b/packages/quantum-js-vis/Q.css index cb10c3c..ebacba8 100644 --- a/packages/quantum-js-vis/Q.css +++ b/packages/quantum-js-vis/Q.css @@ -1,6 +1,6 @@ /* - Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. */ @charset "utf-8"; @@ -11,8 +11,8 @@ /* This file is in the process of being separated - in to “essential global Q.js styles” which will - remain here in Q.css, and “documentation-specific” + in to “essential global Q.js styles” which will + remain here in Q.css, and “documentation-specific” styles which will be removed from here and placed within the /other/documentation.css file instead. @@ -319,7 +319,7 @@ svg, :root { /* - The below still need to be prefaced with “Q-” + The below still need to be prefaced with “Q-” and for the HTML pages to be updated accordingly. */ @@ -341,8 +341,6 @@ svg, :root { max-width: 100%; overflow-x: auto; font-family: var( --Q-font-family-sans ); - /*letter-spacing: 0.03em;*/ - word-spacing: 0.2em; } dd .maths { @@ -395,23 +393,22 @@ dd .maths { vertical-align: middle; position: relative; align: middle; - margin: 1em 0.5em; + margin: 1em; padding: 1em; - /*font-family: var( --Q-font-family-mono );*/ + font-family: var( --Q-font-family-mono ); font-weight: 300; line-height: 1em; - /*text-align: right;*/ - text-align: center; + text-align: right; } .matrix td { - padding: 0.25em 0.5em; + padding: 5px 10px; } .matrix-bracket-left, .matrix-bracket-right { position: absolute; top: 0; - width: 0.5em; + width: 5px; height: 100%; border: 1px solid #CCC; } @@ -440,7 +437,7 @@ dd .maths { .Q-state-vector.bra::before, .complex-vector.bra::before { - content: '⟨'; + content: '⟨'; color: #BBB; } .Q-state-vector.bra::after, @@ -458,7 +455,7 @@ dd .maths { .Q-state-vector.ket::after, .complex-vector.ket::after { - content: '⟩'; + content: '⟩'; color: #BBB; } .Q-state-vector.bra + .Q-state-vector.ket::before, @@ -472,3 +469,863 @@ dd .maths { + +/* + + Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +*/ +@charset "utf-8"; + + + + + + + + + +/* + + Z indices: + + Clipboard =100 + Selected op 10 + Operation 0 + Shadow -10 + Background -20 + + + + + + Circuit + + Menu Moments + ╭───────┬───┬───┬───┬───╮ + │ ≡ ↘ │ 1 │ 2 │ 3 │ + │ Add moment + ├───┬───┼───┼───┼───┼───╯ + R │ 0 │|0⟩│ H │ C0│ X │ - + e ├───┼───┼───┼───┼───┤ + g │ 1 │|0⟩│ I │ C1│ X │ - + s ├───┼───┴───┴───┴───┘ + │ + │ - - - - + ╰───╯ + Add + register + + + Circuit Palette + + ╭───────────────────┬───╮ + │ H X Y Z S T π M … │ @ │ + ╰───────────────────┴───╯ + + + Circuit clipboard + + ┌───────────────┐ + ▟│ ┌───┬───────┐ │ + █│ │ H │ X#0.0 │ │ + █│ ├───┼───────┤ │ + █│ │ I │ X#0.1 │ │ + █│ └───┴───────┘ │ + █└───────────────┘ + ███████████████▛ + + + + ◢◣ + ◢■■■■◣ +◢■■■■■■■■◣ +◥■■■■■■■■◤ + ◥■■■■◤ + ◥◤ + + + ◢■■■■■■◤ + ◢◤ ◢◤ +◢■■■■■■◤ + + + ─────────── + ╲ ╱ ╱ ╱ + ╳ ╱ ╱ + ╱ ╲╱ ╱ + ─────── + + + ─────⦢ + ╱ ╱ +⦣───── + + +*/ + + + + + + +.Q-circuit, +.Q-circuit-palette { + + position: relative; + width: 100%; +} +.Q-circuit-palette { + + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + line-height: 0; +} +.Q-circuit-palette > div { + + display: inline-block; + position: relative; + width: 4rem; + height: 4rem; +} + + +.Q-circuit { + + margin: 1rem 0 2rem 0; + /*border-top: 2px solid hsl( 0, 0%, 50% );*/ +} +.Q-circuit-board-foreground { + + line-height: 3.85rem; + width: auto; +} + + + + + + + /***************/ + /* */ + /* Toolbar */ + /* */ +/***************/ + + +.Q-circuit-toolbar { + + display: block; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + margin-bottom: 0.5rem; + + box-sizing: border-box; + display: grid; + grid-auto-columns: 3.6rem; + grid-auto-rows: 3.0rem; + grid-auto-flow: column; + +} +.Q-circuit-button { + + position: relative; + display: inline-block; + /*margin: 0 0.5rem 0.5rem 0;*/ + width: 3.6rem; + height: 3rem; +/* box-shadow: + -0.1rem -0.1rem 0 rgba( 255, 255, 255, 0.8 ), + 0.1rem 0.1rem 0.1rem rgba( 0, 0, 0, 0.35 );*/ + + border-top: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 100% + ); + border-right: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 90% + ); + border-bottom: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 85% + ); + border-left: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 97% + ); + background: var( --Q-color-background ); +/* background: + var( --Q-color-background ) + linear-gradient( + + 0.4turn, + + rgba( 0, 0, 0, 0.02 ), + rgba( 255, 255, 255, 0.1 ) + );*/ + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 30% + ); + text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 ); + /*border-radius: 0.5rem;*/ + /*border-radius: 100%;*/ + line-height: 2.9rem; + text-align: center; + cursor: pointer; + overflow: hidden; + font-weight: 900; +} +.Q-circuit-toolbar .Q-circuit-button:first-child { + + border-top-left-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; +} +.Q-circuit-toolbar .Q-circuit-button:last-child { + + border-top-right-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} +.Q-circuit-locked .Q-circuit-button, +.Q-circuit-button[Q-disabled] { + + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 85% + ); + cursor: not-allowed; +} +.Q-circuit-locked .Q-circuit-toggle-lock { + + color: inherit; + cursor: pointer; +} + + + + +.Q-circuit-board-container { + + position: relative; + margin: 0 0 2rem 0; + margin: 0; + width: 100%; + max-height: 60vh; + overflow: scroll; +} +.Q-circuit-board { + + position: relative; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +/*.Q-circuit-palette,*/ +.Q-circuit-board-foreground, +.Q-circuit-board-background, +.Q-circuit-clipboard { + + box-sizing: border-box; + display: grid; + grid-auto-rows: 4rem; + grid-auto-columns: 4rem; + grid-auto-flow: column; +} +/*.Q-circuit-palette,*/ +.Q-circuit-board-foreground, +.Q-circuit-board-background { + + position: relative; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.Q-circuit-clipboard { + + position: absolute; + z-index: 100; + min-width: 4rem; + min-height: 4rem; + transform: scale( 1.05 ); +} +.Q-circuit-clipboard, .Q-circuit-clipboard > div { + + cursor: grabbing; +} +.Q-circuit-clipboard-danger .Q-circuit-operation { + + background-color: var( --Q-color-yellow ); +} +.Q-circuit-clipboard-destroy { + + animation-name: Q-circuit-clipboard-poof; + animation-fill-mode: forwards; + animation-duration: 0.3s; + animation-iteration-count: 1; +} +@keyframes Q-circuit-clipboard-poof { + + 100% { + + transform: scale( 1.5 ); + opacity: 0; + } +} +.Q-circuit-board-background { + + /* + + Clipboard: 100 + Operation: 0 + Shadow: -10 + Background: -20 + + */ + position: absolute; + z-index: -20; + color: rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-board-background > div { + +/* transition: + background-color 0.2s, + color 0.2s;*/ +} +.Q-circuit-board-background .Q-circuit-cell-highlighted { + + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); + /*transition: none;*/ +} + + + + +.Q-circuit-register-wire { + + position: absolute; + top: calc( 50% - 0.5px ); + width: 100%; + height: 1px; + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 50% + ); +} + + + +.Q-circuit-palette > div, +.Q-circuit-clipboard > div, +.Q-circuit-board-foreground > div { + + text-align: center; +} + + + + + + + /***************/ + /* */ + /* Headers */ + /* */ +/***************/ + + +.Q-circuit-header { + + position: sticky; + z-index: 2; + margin: 0; + /*background-color: var( --Q-color-background );*/ + background-color: white; + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 75% + ); + font-family: var( --Q-font-family-mono ); +} +.Q-circuit-input.Q-circuit-cell-highlighted, +.Q-circuit-header.Q-circuit-cell-highlighted { + + background-color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); + color: black; +} +.Q-circuit-selectall { + + z-index: 3; + margin: 0; + top: 0; + /*left: 4rem;*/ + /*grid-column: 2;*/ + left: 0; + grid-column-start: 1; + grid-column-end: 3; + grid-row: 1; + cursor: se-resize; +} +.Q-circuit-moment-label, +.Q-circuit-moment-add { + + grid-row: 1; + top: 0; + cursor: s-resize; +} +.Q-circuit-register-label, +.Q-circuit-register-add { + + grid-column: 2; + left: 4rem; + cursor: e-resize; +} +.Q-circuit-moment-add, +.Q-circuit-register-add { + + cursor: pointer; +} +.Q-circuit-moment-add, +.Q-circuit-register-add { + + display: none; +} +.Q-circuit-selectall, +.Q-circuit-moment-label, +.Q-circuit-moment-add { + + border-bottom: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); +} +.Q-circuit-selectall, +.Q-circuit-register-label, +.Q-circuit-register-add { + + border-right: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 95% + ); +} +.Q-circuit-input { + + position: sticky; + z-index: 2; + grid-column: 1; + left: 0; + /*background-color: var( --Q-color-background );*/ + background-color: white; + font-size: 1.5rem; + font-weight: 900; + font-family: var( --Q-font-family-mono ); +} + + + + + + +.Q-circuit-operation-link-container { + + --Q-link-stroke: 3px; + --Q-link-radius: 100%; + + display: block; + position: relative; + left: calc( 50% - ( var( --Q-link-stroke ) / 2 )); + width: 50%; + height: 100%; + overflow: hidden; +} +.Q-circuit-operation-link-container.Q-circuit-cell-highlighted { + + background-color: transparent; +} +.Q-circuit-operation-link { + + display: block; + position: absolute; + width: calc( var( --Q-link-stroke ) * 2 ); + height: calc( 100% - 4rem + var( --Q-link-stroke )); + /*border: var( --Q-link-stroke ) solid hsl( 0, 0%, 50% );*/ + border: var( --Q-link-stroke ) solid hsl( + + var( --Q-color-background-hue ), + 10%, + 30% + ); + + /*border: var( --Q-link-stroke ) solid var( --Q-color-orange );*/ + + transform: translate( -50%, calc( 2rem - ( var( --Q-link-stroke ) / 2 ))); + transform-origin: center; +} +.Q-circuit-operation-link.Q-circuit-operation-link-curved { + + width: calc( var( --Q-link-radius ) - var( --Q-link-stroke )); + width: 200%; + border-radius: 100%; +} + + + + + + + /******************/ + /* */ + /* Operations */ + /* */ +/******************/ + + +.Q-circuit-operation { + + position: relative; + /*--Q-operation-color-hue: var( --Q-color-green-hue ); + --Q-operation-color-main: var( --Q-color-green );*/ + + --Q-operation-color-hue: var( --Q-color-blue-hue ); + --Q-operation-color-main: hsl( + + var( --Q-operation-color-hue ), + 10%, + 35% + ); + + --Q-operation-color-light: hsl( + + var( --Q-operation-color-hue ), + 10%, + 50% + ); + --Q-operation-color-dark: hsl( + + var( --Q-operation-color-hue ), + 10%, + 25% + ); + color: white; + text-shadow: -0.05rem -0.05rem 0 rgba( 0, 0, 0, 0.1 ); + font-size: 1.5rem; + line-height: 2.9rem; + font-weight: 900; + cursor: grab; +} +.Q-circuit-locked .Q-circuit-operation { + + cursor: not-allowed; +} +.Q-circuit-operation-tile { + + position: absolute; + top: 0.5rem; + left: 0.5rem; + right: 0.5rem; + bottom: 0.5rem; + + /*margin: 0.5rem;*/ + /*padding: 0.5rem;*/ + + /*box-shadow: 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.2 );*/ + border-radius: 0.2rem; + /* + border-top: 0.1rem solid var( --Q-operation-color-light ); + border-left: 0.1rem solid var( --Q-operation-color-light ); + border-right: 0.1rem solid var( --Q-operation-color-dark ); + border-bottom: 0.1rem solid var( --Q-operation-color-dark ); + */ + background: + var( --Q-operation-color-main ) + /*linear-gradient( + + 0.45turn, + rgba( 255, 255, 255, 0.1 ), + rgba( 0, 0, 0, 0.05 ) + )*/; +} +.Q-circuit-palette .Q-circuit-operation:hover { + + /*background-color: rgba( 255, 255, 255, 0.6 );*/ + background-color: white; +} +.Q-circuit-palette .Q-circuit-operation-tile { + + --Q-before-rotation: 12deg; + --Q-before-x: 1px; + --Q-before-y: -2px; + + --Q-after-rotation: -7deg; + --Q-after-x: -2px; + --Q-after-y: 3px; + + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-palette .Q-circuit-operation-tile:before, +.Q-circuit-palette .Q-circuit-operation-tile:after { + + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: 0.2rem; + /*background-color: hsl( 0, 0%, 60% );*/ + + background-color: var( --Q-operation-color-dark ); + transform: + translate( var( --Q-before-x ), var( --Q-before-y )) + rotate( var( --Q-before-rotation )); + z-index: -10; + /*z-index: 10;*/ + display: block; + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-palette .Q-circuit-operation-tile:after { + + transform: + translate( var( --Q-after-x ), var( --Q-after-y )) + rotate( var( --Q-after-rotation )); + box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 ); +} +.Q-circuit-operation:hover .Q-circuit-operation-tile { + + color: white; +} + + + + +.Q-circuit-operation-hadamard .Q-circuit-operation-tile { + + /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ + /*--Q-operation-color-main: var( --Q-color-red );*/ + + /*--Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 10% );*/ + + +/* background: + linear-gradient( + + -33deg, + var( --Q-color-blue ) 20%, + #6f3c69 50%, + var( --Q-color-red ) 80% + );*/ +} +.Q-circuit-operation-identity .Q-circuit-operation-tile, +.Q-circuit-operation-control .Q-circuit-operation-tile, +.Q-circuit-operation-target .Q-circuit-operation-tile { + + /*--Q-operation-color-hue: var( --Q-color-orange-hue );*/ + /*--Q-operation-color-main: var( --Q-color-orange );*/ + border-radius: 100%; +} +.Q-circuit-operation-identity .Q-circuit-operation-tile, +.Q-circuit-operation-control .Q-circuit-operation-tile { + + top: calc( 50% - 0.7rem ); + left: calc( 50% - 0.7rem ); + width: 1.4rem; + height: 1.4rem; + overflow: hidden; +/* --Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 10% );*/ +} +.Q-circuit-operation-pauli-x, +.Q-circuit-operation-pauli-y, +.Q-circuit-operation-pauli-z { + + /*--Q-operation-color-hue: var( --Q-color-red-hue );*/ + /*--Q-operation-color-main: var( --Q-color-red );*/ + +/* --Q-operation-color-hue: 0; + --Q-operation-color-main: hsl( 0, 0%, 30% );*/ +} +.Q-circuit-operation-swap .Q-circuit-operation-tile { + + top: calc( 50% - 0.55rem ); + left: calc( 50% - 0.55rem ); + width: 1.2rem; + height: 1.2rem; + border-radius: 0; + transform-origin: center; + transform: rotate( 45deg ); + font-size: 0; +} + + + + + + + /********************/ + /* */ + /* Other states */ + /* */ +/********************/ + + +.Q-circuit-palette > div:hover, +.Q-circuit-board-foreground > div:hover { + + outline: 2px solid var( --Q-hyperlink-internal-color ); + outline-offset: -2px; +} +.Q-circuit-palette > div:hover .Q-circuit-operation-tile { + + box-shadow: none; +} +/*.Q-circuit-palette > div:hover,*/ +.Q-circuit-board-foreground > div:hover { + + background-color: white; + color: black; +} + + + + + + +.Q-circuit-clipboard > div, +.Q-circuit-cell-selected { + + background-color: white; +} +.Q-circuit-clipboard > div:before, +.Q-circuit-cell-selected:before { + + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: block; + z-index: -10; + box-shadow: + 0 0 1rem rgba( 0, 0, 0, 0.2 ), + 0.4rem 0.4rem 0.2rem rgba( 0, 0, 0, 0.2 ); + outline: 1px solid hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 50% + ); + /*outline-offset: -1px;*/ +} + + + + +.Q-circuit-clipboard > div { + + background-color: white; +} +.Q-circuit-clipboard > div:before { + + /* + + This was very helpful! + https://blog.dudak.me/2014/css-shadows-under-adjacent-elements/ + + */ + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: -10; + display: block; + box-shadow: 0.4rem 0.4rem 0.3rem rgba( 0, 0, 0, 0.2 ); +} + + + + + + /***************/ + /* */ + /* Buttons */ + /* */ +/***************/ + + +.Q-circuit-locked .Q-circuit-toggle-lock, +.Q-circuit-locked .Q-circuit-toggle-lock:hover { + + background-color: var( --Q-color-red ); +} +.Q-circuit-toggle-lock { + + z-index: 3; + left: 0; + top: 0; + grid-column: 1; + grid-row: 1; + cursor: pointer; + font-size: 1.1rem; + text-shadow: none; + font-weight: normal; +} +.Q-circuit-button-undo, +.Q-circuit-button-redo { + + font-size: 1.2rem; + line-height: 2.6rem; + font-weight: normal; +} + + + +.Q-circuit p { + + padding: 1rem; + color: hsl( + + var( --Q-color-background-hue ), + var( --Q-color-background-saturation ), + 66% + ); +} + + From 12128ff834be474fa2ccf479fedc6f26db25bed8 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Fri, 13 Aug 2021 20:55:01 +0000 Subject: [PATCH 16/22] update package.json differences --- .gitignore | 2 + build/q.js | 1378 ++++- .../quantum-js-cli/__test__/runner.test.js | 1 - packages/quantum-js-cli/index.js | 2 + packages/quantum-js-cli/package.json | 4 +- packages/quantum-js-util/package-lock.json | 5233 ++++++++++++++++- packages/quantum-js-util/package.json | 9 +- 7 files changed, 6329 insertions(+), 300 deletions(-) diff --git a/.gitignore b/.gitignore index b90121d..b85986f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ notes.txt .DS_Store node_modules +coverage +__test__ \ No newline at end of file diff --git a/build/q.js b/build/q.js index 52a0ab0..ddcf90e 100644 --- a/build/q.js +++ b/build/q.js @@ -1,15 +1,10 @@ -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. const Q = function(){ // Did we send arguments of the form // ( bandwidth, timewidth )? - if( arguments.length === 2 && Array.from( arguments ).every( function( argument ){ @@ -68,7 +63,7 @@ Object.assign( Q, { function countPrefixTabs( text ){ - // Is counting tabs “manually” + // Is counting tabs “manually” // actually more performant than regex? let count = index = 0 @@ -79,7 +74,7 @@ Object.assign( Q, { //------------------- TO DO! // we should check that there is ONLY whitespace between the function opening and the tick mark! - // otherwise it’s not documentation. + // otherwise it’s not documentation. let tabs = Number.MAX_SAFE_INTEGER @@ -284,11 +279,11 @@ Q.createConstants( 'REVISION', 19, - // Yeah... F’ing floating point numbers, Man! - // Here’s the issue: + // Yeah... F’ing floating point numbers, Man! + // Here’s the issue: // var a = new Q.ComplexNumber( 1, 2 ) // a.multiply(a).isEqualTo( a.power( new Q.ComplexNumber( 2, 0 ))) - // That’s only true if Q.EPSILON >= Number.EPSILON * 6 + // That’s only true if Q.EPSILON >= Number.EPSILON * 6 'EPSILON', Number.EPSILON * 6, @@ -506,7 +501,7 @@ Q.createConstants( 'Turkey', 'Turtle', // U - 'Vicuña', + 'Vicuña', 'Viper', 'Vulture', 'Wallaby', @@ -622,7 +617,7 @@ https://quantumjavascript.app -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. @@ -630,28 +625,28 @@ https://quantumjavascript.app Q.ComplexNumber = function( real, imaginary ){ ` - The set of “real numbers” (ℝ) contains any number that can be expressed + The set of “real numbers” (ℝ) contains any number that can be expressed along an infinite timeline. https://en.wikipedia.org/wiki/Real_number - … -3 -2 -1 0 +1 +2 +3 … - ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄ - √2 𝒆 π + … -3 -2 -1 0 +1 +2 +3 … + ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄ + √2 𝒆 Ï€ - Meanwhile, “imaginary numbers” (𝕀) consist of a real (ℝ) multiplier and - the symbol 𝒊, which is the impossible solution to the equation 𝒙² = −1. + Meanwhile, “imaginary numbers” (𝕀) consist of a real (ℝ) multiplier and + the symbol 𝒊, which is the impossible solution to the equation 𝒙² = −1. Note that no number when multiplied by itself can ever result in a - negative product, but the concept of 𝒊 gives us a way to reason around + negative product, but the concept of 𝒊 gives us a way to reason around this imaginary scenario nonetheless. https://en.wikipedia.org/wiki/Imaginary_number - … -3𝒊 -2𝒊 -1𝒊 0𝒊 +1𝒊 +2𝒊 +3𝒊 … - ┄───┴───┴───┴───┴───┴───┴───┴───┄ + … -3𝒊 -2𝒊 -1𝒊 0𝒊 +1𝒊 +2𝒊 +3𝒊 … + ┄───┴───┴───┴───┴───┴───┴───┴───┄ - A “complex number“ (ℂ) is a number that can be expressed in the form - 𝒂 + 𝒃𝒊, where 𝒂 is the real component (ℝ) and 𝒃𝒊 is the imaginary - component (𝕀). https://en.wikipedia.org/wiki/Complex_number + A “complex number“ (â„‚) is a number that can be expressed in the form + 𝒂 + 𝒃𝒊, where 𝒂 is the real component (ℝ) and 𝒃𝒊 is the imaginary + component (𝕀). https://en.wikipedia.org/wiki/Complex_number Operation functions on Q.ComplexNumber instances generally accept as @@ -664,7 +659,7 @@ Q.ComplexNumber = function( real, imaginary ){ imaginary = real.imaginary real = real.real - Q.warn( 'Q.ComplexNumber tried to create a new instance with an argument that is already a Q.ComplexNumber — and that’s weird!' ) + Q.warn( 'Q.ComplexNumber tried to create a new instance with an argument that is already a Q.ComplexNumber — and that’s weird!' ) } else if( real === undefined ) real = 0 if( imaginary === undefined ) imaginary = 0 @@ -687,6 +682,82 @@ Object.assign( Q.ComplexNumber, { constants: {}, createConstant: Q.createConstant, createConstants: Q.createConstants, + + + + + toText: function( rNumber, iNumber, roundToDecimal, padPositive ){ + + // Should we round these numbers? + // Our default is yes: to 3 digits. + // Otherwise round to specified decimal. + + if( typeof roundToDecimal !== 'number' ) roundToDecimal = 3 + const factor = Math.pow( 10, roundToDecimal ) + rNumber = Math.round( rNumber * factor ) / factor + iNumber = Math.round( iNumber * factor ) / factor + + + // Convert padPositive + // from a potential Boolean + // to a String. + // If we don’t receive a FALSE + // then we’ll pad the positive numbers. + + padPositive = padPositive === false ? '' : ' ' + + + // We need the absolute values of each. + + let + rAbsolute = Math.abs( rNumber ), + iAbsolute = Math.abs( iNumber ) + + + // And an absolute value string. + + let + rText = rAbsolute.toString(), + iText = iAbsolute.toString() + + + // Is this an IMAGINARY-ONLY number? + // Don’t worry: -0 === 0. + + if( rNumber === 0 ){ + + if( iNumber === Infinity ) return padPositive +'∞i' + if( iNumber === -Infinity ) return '-∞i' + if( iNumber === 0 ) return padPositive +'0' + if( iNumber === -1 ) return '-i' + if( iNumber === 1 ) return padPositive +'i' + if( iNumber >= 0 ) return padPositive + iText +'i' + if( iNumber < 0 ) return '-'+ iText +'i' + return iText +'i'// NaN + } + + + // This number contains a real component + // and may also contain an imaginary one as well. + + if( rNumber === Infinity ) rText = padPositive +'∞' + else if( rNumber === -Infinity ) rText = '-∞' + else if( rNumber >= 0 ) rText = padPositive + rText + else if( rNumber < 0 ) rText = '-'+ rText + + if( iNumber === Infinity ) return rText +' + ∞i' + if( iNumber === -Infinity ) return rText +' - ∞i' + if( iNumber === 0 ) return rText + if( iNumber === -1 ) return rText +' - i' + if( iNumber === 1 ) return rText +' + i' + if( iNumber > 0 ) return rText +' + '+ iText +'i' + if( iNumber < 0 ) return rText +' - '+ iText +'i' + return rText +' + '+ iText +'i'// NaN + }, + + + + isNumberLike: function( n ){ return isNaN( n ) === false && ( typeof n === 'number' || n instanceof Number ) @@ -746,6 +817,7 @@ Object.assign( Q.ComplexNumber, { + absolute: function( n ){ return Q.hypotenuse( n.real, n.imaginary ) @@ -875,7 +947,7 @@ Object.assign( Q.ComplexNumber, { // If our exponent is Real (has no Imaginary component) - // then we’re really just raising to a power. + // then we’re really just raising to a power. if( b.imaginary === 0 ){ @@ -987,7 +1059,7 @@ Object.assign( Q.ComplexNumber, { firsts = a.real * b.real, outers = a.real * b.imaginary, inners = a.imaginary * b.real, - lasts = a.imaginary * b.imaginary * -1// Because i² = -1. + lasts = a.imaginary * b.imaginary * -1// Because i² = -1. return new Q.ComplexNumber( @@ -1021,7 +1093,7 @@ Object.assign( Q.ComplexNumber, { function( a, b ){ - // Ermergerd I had to look this up because it’s been so long. + // Ermergerd I had to look this up because it’s been so long. // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/complex-conjugates-and-dividing-complex-numbers/a/dividing-complex-numbers-review const @@ -1145,6 +1217,13 @@ Object.assign( Q.ComplexNumber.prototype, { if( this.imaginary === 0 ) return this.real return this }, + toText: function( roundToDecimal, padPositive ){ + + + // Note: this will kill function chaining. + + return Q.ComplexNumber.toText( this.real, this.imaginary, roundToDecimal, padPositive ) + }, isNaN: function( n ){ @@ -1209,84 +1288,6 @@ Object.assign( Q.ComplexNumber.prototype, { }, - toText: function( roundToDecimal, padPositive ){ - - - // Convert padPositive - // from a potential Boolean - // to a String. - // If we don’t receive a FALSE - // then we’ll pad the positive numbers. - - padPositive = padPositive === false ? '' : ' ' - - - // Right. Let’s copy over the actual numbers. - - let - rNumber = this.real, - iNumber = this.imaginary - - - // Should we round these numbers? - // Our default is yes: to 3 digits. - // Otherwise round to specified decimal. - - if( typeof roundToDecimal !== 'number' ) roundToDecimal = 3 - const factor = Math.pow( 10, roundToDecimal ) - rNumber = Math.round( rNumber * factor ) / factor - iNumber = Math.round( iNumber * factor ) / factor - - - // We need the absolute values of each. - - let - rAbsolute = Math.abs( rNumber ), - iAbsolute = Math.abs( iNumber ) - - - // And an absolute value string. - - let - rText = rAbsolute.toString(), - iText = iAbsolute.toString() - - - // Is this an IMAGINARY-ONLY number? - // Don’t worry: -0 === 0. - - if( rNumber === 0 ){ - - if( iNumber === Infinity ) return padPositive +'∞i' - if( iNumber === -Infinity ) return '-∞i' - if( iNumber === 0 ) return padPositive +'0' - if( iNumber === -1 ) return '-i' - if( iNumber === 1 ) return padPositive +'i' - if( iNumber >= 0 ) return padPositive + iText +'i' - if( iNumber < 0 ) return '-'+ iText +'i' - return iText +'i'// NaN - } - - - // This number contains a real component - // and may also contain an imaginary one as well. - - if( rNumber === Infinity ) rText = padPositive +'∞' - else if( rNumber === -Infinity ) rText = '-∞' - else if( rNumber >= 0 ) rText = padPositive + rText - else if( rNumber < 0 ) rText = '-'+ rText - - if( iNumber === Infinity ) return rText +' + ∞i' - if( iNumber === -Infinity ) return rText +' - ∞i' - if( iNumber === 0 ) return rText - if( iNumber === -1 ) return rText +' - i' - if( iNumber === 1 ) return rText +' + i' - if( iNumber > 0 ) return rText +' + '+ iText +'i' - if( iNumber < 0 ) return rText +' - '+ iText +'i' - return rText +' + '+ iText +'i'// NaN - }, - - // DESTRUCTIVE operations. @@ -1337,7 +1338,7 @@ Object.assign( Q.ComplexNumber.prototype, { -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. @@ -1345,7 +1346,7 @@ Object.assign( Q.ComplexNumber.prototype, { Q.Matrix = function(){ - // We’re keeping track of how many matrices are + // We’re keeping track of how many matrices are // actually being generated. Just curiosity. this.index = Q.Matrix.index ++ @@ -1379,10 +1380,10 @@ Q.Matrix = function(){ } else { - // Matrices’ primary organization is by rows, + // Matrices’ primary organization is by rows, // which is more congruent with our written langauge; // primarily organizated by horizontally juxtaposed glyphs. - // That means it’s easier to write an instance invocation in code + // That means it’s easier to write an instance invocation in code // and easier to read when inspecting properties in the console. let matrixWidthIsBroken = false @@ -1413,7 +1414,7 @@ Q.Matrix = function(){ for( let y = 0; y < this.rows.length; y ++ ){ - // Since we’re combing through here + // Since we’re combing through here // this is a good time to convert Number to ComplexNumber! const value = matrix.rows[ y ][ x ] @@ -1547,7 +1548,7 @@ Object.assign( Q.Matrix, { const f = Q.Matrix[ 'from'+ format ] format = format.toLowerCase() if( typeof f !== 'function' ) - return Q.error( `Q.Matrix could not find an importer for “${format}” data.` ) + return Q.error( `Q.Matrix could not find an importer for “${format}” data.` ) return f }, fromArray: function( array ){ @@ -1999,16 +2000,16 @@ Q.Matrix.createConstants( -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -Q.Qubit = function( a, b, label, name ){ +Q.Qubit = function( a, b, symbol, name ){ - // If we’ve received an instance of Q.Matrix as our first argument - // then we’ll assume there are no further arguments + // If we’ve received an instance of Q.Matrix as our first argument + // then we’ll assume there are no further arguments // and just use that matrix as our new Q.Qubit instance. if( Q.Matrix.isMatrixLike( a ) && b === undefined ){ @@ -2021,14 +2022,14 @@ Q.Qubit = function( a, b, label, name ){ // All of our internal math now uses complex numbers // rather than Number literals - // so we’d better convert! + // so we’d better convert! if( typeof a === 'number' ) a = new Q.ComplexNumber( a, 0 ) if( typeof b === 'number' ) b = new Q.ComplexNumber( b, 0 ) // If we receive undefined (or garbage inputs) - // let’s try to make it useable. + // let’s try to make it useable. // This way we can always call Q.Qubit with no arguments // to make a new qubit available for computing with. @@ -2036,10 +2037,10 @@ Q.Qubit = function( a, b, label, name ){ if( b instanceof Q.ComplexNumber !== true ){ - // 1 - |𝒂|² = |𝒃|² - // So this does NOT account for if 𝒃 ought to be imaginary or not. + // 1 - |𝒂|² = |𝒃|² + // So this does NOT account for if 𝒃 ought to be imaginary or not. // Perhaps for completeness we could randomly decide - // to flip the real and imaginary components of 𝒃 after this line? + // to flip the real and imaginary components of 𝒃 after this line? b = Q.ComplexNumber.ONE.subtract( Math.pow( a.absolute(), 2 )).squareRoot() } @@ -2047,7 +2048,7 @@ Q.Qubit = function( a, b, label, name ){ // Sanity check! - // Does this constraint hold true? |𝒂|² + |𝒃|² = 1 + // Does this constraint hold true? |𝒂|² + |𝒃|² = 1 if( Math.pow( a.absolute(), 2 ) + Math.pow( b.absolute(), 2 ) - 1 > Q.EPSILON ) return Q.error( `Q.Qubit could not accept the initialization values of a=${a} and b=${b} because their squares do not add up to 1.` ) @@ -2056,7 +2057,7 @@ Q.Qubit = function( a, b, label, name ){ this.index = Q.Qubit.index ++ - // Convenience getters and setters for this qubit’s + // Convenience getters and setters for this qubit’s // controll bit and target bit. Object.defineProperty( this, 'alpha', { @@ -2071,11 +2072,11 @@ Q.Qubit = function( a, b, label, name ){ }) - // Used for Dirac notation: |?⟩ + // Used for Dirac notation: |?⟩ - if( typeof label === 'string' ) this.label = label + if( typeof symbol === 'string' ) this.symbol = symbol if( typeof name === 'string' ) this.name = name - if( this.label === undefined || this.name === undefined ){ + if( this.symbol === undefined || this.name === undefined ){ const found = Object.values( Q.Qubit.constants ).find( function( qubit ){ @@ -2087,12 +2088,12 @@ Q.Qubit = function( a, b, label, name ){ }) if( found === undefined ){ - this.label = '?' + this.symbol = '?' this.name = 'Unnamed' } else { - if( this.label === undefined ) this.label = found.label + if( this.symbol === undefined ) this.symbol = found.symbol if( this.name === undefined ) this.name = found.name } } @@ -2182,7 +2183,7 @@ Object.assign( Q.Qubit, { This is means of inverting what comes first: the Gate or the Qubit? If the Gate only operates on a single qubit, - then it doesn’t matter and we can do this: + then it doesn’t matter and we can do this: ` if( gate instanceof Q.Gate === false ) return Q.error( `Q.Qubit attempted to apply something that was not a gate to this qubit #${ qubit.index }.` ) @@ -2190,12 +2191,12 @@ Object.assign( Q.Qubit, { }, toText: function( qubit ){ - //return `|${qubit.beta.toText()}⟩` + //return `|${qubit.beta.toText()}⟩` return qubit.alpha.toText() +'\n'+ qubit.beta.toText() }, toStateVectorText: function( qubit ){ - return `|${ qubit.beta.toText() }⟩` + return `|${ qubit.beta.toText() }⟩` }, toStateVectorHtml: function( qubit ){ @@ -2205,7 +2206,7 @@ Object.assign( Q.Qubit, { // This code was a pain in the ass to figure out. - // I’m not fluent in trigonometry + // I’m not fluent in trigonometry // and none of the quantum primers actually lay out // how to convert arbitrary qubit states // to Bloch Sphere representation. @@ -2217,9 +2218,9 @@ Object.assign( Q.Qubit, { toBlochSphere: function( qubit ){ ` - Based on this qubit’s state return the - Polar angle θ (theta), - azimuth angle ϕ (phi), + Based on this qubit’s state return the + Polar angle θ (theta), + azimuth angle Ï• (phi), Bloch vector, corrected surface coordinate. @@ -2227,14 +2228,14 @@ Object.assign( Q.Qubit, { ` - // Polar angle θ (theta). + // Polar angle θ (theta). const theta = Q.ComplexNumber.arcCosine( qubit.alpha ).multiply( 2 ) if( isNaN( theta.real )) theta.real = 0 if( isNaN( theta.imaginary )) theta.imaginary = 0 - // Azimuth angle ϕ (phi). + // Azimuth angle Ï• (phi). const phi = Q.ComplexNumber.log( @@ -2255,8 +2256,8 @@ Object.assign( Q.Qubit, { } - // Bloch vector’s axes are wonked. - // Let’s “correct” them for use with Three.js, etc. + // Bloch vector’s axes are wonked. + // Let’s “correct” them for use with Three.js, etc. const position = { @@ -2268,10 +2269,18 @@ Object.assign( Q.Qubit, { return { - // Ummm... I’m only returnig the REAL portions. Please forgive me! + // Wow does this make tweening easier down the road. + + alphaReal: qubit.alpha.real, + alphaImaginary: qubit.alpha.imaginary, + betaReal: qubit.beta.real, + betaImaginary: qubit.beta.imaginary, + + + // Ummm... I’m only returnig the REAL portions. Please forgive me! theta: theta.real, - phi: phi.real, + phi: phi.real, vector, // Wonked YZX vector for maths because maths. position// Un-wonked XYZ for use by actual 3D engines. } @@ -2291,9 +2300,9 @@ Q.Qubit.createConstants( // Opposing pairs: - // |H⟩ and |V⟩ - // |D⟩ and |A⟩ - // |R⟩ and |L⟩ + // |H⟩ and |V⟩ + // |D⟩ and |A⟩ + // |R⟩ and |L⟩ 'HORIZONTAL', new Q.Qubit( 1, 0, 'H', 'Horizontal' ),// ZERO. 'VERTICAL', new Q.Qubit( 0, 1, 'V', 'Vertical' ),// ONE. @@ -2384,7 +2393,8 @@ Q.Gate = function( params ){ if( typeof this.symbol !== 'string' ) this.symbol = '?' if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase() - + const parameters = Object.assign( {}, params.parameters ) + this.parameters = parameters // We use symbols as unique identifiers // among gate CONSTANTS @@ -2401,10 +2411,12 @@ Q.Gate = function( params ){ return gate.symbol === scope.symbol }) - if( foundConstant ){ + //Muting this warning in order to have parameterized gates (that don't totally mess with the constants), we need + //to make clones of the constants...a lot if you're using a lot of parameterized gates. Warning gets annoying :/. + // if( foundConstant ){ - Q.warn( `Q.Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant ) - } + // Q.warn( `Q.Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant ) + // } if( typeof this.name !== 'string' ) this.name = 'Unknown' if( typeof this.nameCss !== 'string' ) this.nameCss = 'unknown' @@ -2553,8 +2565,13 @@ Q.Gate.createConstants( nameCss: 'pauli-x', matrix: new Q.Matrix( [ 0, 1 ], - [ 1, 0 ]) - }), + [ 1, 0 ]), + //ltnln: NOTE! can_be_controlled refers to whether or not the Braket SDK supports a controlled + //application of this gate; if we want Q to be able to support controlled gated regardless of whether + //or not Braket can, this must be changed.. + can_be_controlled: true + }, + ), 'PAULI_Y', new Q.Gate({ symbol: 'Y', @@ -2564,8 +2581,10 @@ Q.Gate.createConstants( nameCss: 'pauli-y', matrix: new Q.Matrix( [ 0, new Q.ComplexNumber( 0, -1 )], - [ new Q.ComplexNumber( 0, 1 ), 0 ]) - }), + [ new Q.ComplexNumber( 0, 1 ), 0 ]), + can_be_controlled: true + }, + ), 'PAULI_Z', new Q.Gate({ symbol: 'Z', @@ -2575,32 +2594,35 @@ Q.Gate.createConstants( nameCss: 'pauli-z', matrix: new Q.Matrix( [ 1, 0 ], - [ 0, -1 ]) - }), + [ 0, -1 ]), + can_be_controlled: true + }, + ), 'PHASE', new Q.Gate({ symbol: 'P', - symbolAmazonBraket: 'p',// !!! Double check this !!! + symbolAmazonBraket: 'phaseshift',// ltnln edit: change from 'p' to 'phaseshift' symbolSvg: '', name: 'Phase', nameCss: 'phase', - phi: 1, + parameters: { "phi" : 1 }, updateMatrix$: function( phi ){ - - if( Q.isUsefulNumber( phi ) === true ) this.phi = phi + if( Q.isUsefulNumber( phi ) === true ) this.parameters[ "phi" ] = phi this.matrix = new Q.Matrix( [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.phi ))]) + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters["phi"] ))]) return this }, applyToQubit: function( qubit, phi ){ - if( Q.isUsefulNumber( phi ) !== true ) phi = this.phi + if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] const matrix = new Q.Matrix( [ 1, 0 ], [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi ))]) return new Q.Qubit( matrix.multiply( qubit )) - } + }, + can_be_controlled: true, + has_parameters: true }), 'PI_8', new Q.Gate({ @@ -2625,22 +2647,224 @@ Q.Gate.createConstants( // Create Bloch sphere visualizer instance. } }), + 'RX', new Q.Gate({ + + symbol: 'Rx', + symbolAmazonBraket: 'rx', + symbolSvg: '', + name: 'X Rotation', + nameCss: 'x-rotation', + parameters: { "phi" : Math.PI / 2 }, + updateMatrix$: function( phi ){ + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ], + [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 )]) + return this + }, + applyToQubit: function( qubit, phi ){ + if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ], + [ new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 )]) + return new Q.Qubit( matrix.multiply( qubit )) + }, + has_parameters: true + }), + 'RY', new Q.Gate({ + + symbol: 'Ry', + symbolAmazonBraket: 'ry', + symbolSvg: '', + name: 'Y Rotation', + nameCss: 'y-rotation', + parameters: { "phi" : Math.PI / 2 }, + updateMatrix$: function( phi ){ - // Operate on 2 qubits. + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ Math.cos( this.parameters[ "phi" ] / 2 ), -Math.sin( phi / 2 ) ], + [ Math.sin( this.parameters[ "phi" ] / 2 ), Math.cos( this.parameters[ "phi" ] / 2 )]) + return this + }, + applyToQubit: function( qubit, phi ){ + + if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ Math.cos( phi / 2 ), -Math.sin( phi / 2 ) ], + [ Math.sin( phi / 2 ), Math.cos( phi / 2 )]) + return new Q.Qubit( matrix.multiply( qubit )) + }, + has_parameters: true + }), + 'RZ', new Q.Gate({ + + symbol: 'Rz', + symbolAmazonBraket: 'rz', + symbolSvg: '', + name: 'Z Rotation', + nameCss: 'z-rotation', + parameters: { "phi" : Math.PI / 2 }, + updateMatrix$: function( phi ){ + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )), 0 ], + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 ))]) + return this + }, + applyToQubit: function( qubit, phi ){ + if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -phi / 2 )), 0 ], + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 ))]) + return new Q.Qubit( matrix.multiply( qubit )) + }, + has_parameters: true + }), + 'UNITARY', new Q.Gate({ + + symbol: 'U', + symbolAmazonBraket: 'unitary', + symbolSvg: '', + name: 'Unitary', + nameCss: 'unitary', + //toAmazonBraket will have to use the following matrix as an argument for unitary() + parameters: { "phi" : Math.PI / 2, + "theta" : Math.PI / 2, + "lambda" : Math.PI / 2 }, + updateMatrix$: function( phi, theta, lambda ){ + //if all are valid, update; otherwise, update none. + if( (Q.isUsefulNumber( +phi ) === true) && (Q.isUsefulNumber( +theta ) === true) && (Q.isUsefulNumber( +lambda ) === true) ) { + this.parameters[ "phi" ] = +phi; + this.parameters[ "theta" ] = +theta; + this.parameters[ "lambda" ] = +lambda; + } + const a = Q.ComplexNumber.multiply( + Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 )) + const b = Q.ComplexNumber.multiply( + Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), -Math.sin( this.parameters[ "theta" ] / 2 )) + const c = Q.ComplexNumber.multiply( + Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), Math.sin( this.parameters[ "theta" ] / 2 )) + const d = Q.ComplexNumber.multiply( + Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 )) + this.matrix = new Q.Matrix( + [ a, b ], + [ c, d ]) + return this + }, + applyToQubit: function( qubit, phi, theta, lambda ){ + if( Q.isUsefulNumber( phi ) === true ) phi = this.parameters[ "phi" ] + if( Q.isUsefulNumber( theta ) === true ) theta = this.parameters[ "theta" ] + if( Q.isUsefulNumber( lambda ) === true ) lambda = this.parameters[ "lambda" ] + const a = Q.ComplexNumber.multiply( + Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( phi + lambda ) / 2 )), Math.cos( theta / 2 )); + const b = Q.ComplexNumber.multiply( + Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( phi - lambda ) / 2 )), -Math.sin( theta / 2 )); + const c = Q.ComplexNumber.multiply( + Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( phi - lambda ) / 2 )), Math.sin( theta / 2 )); + const d = Q.ComplexNumber.multiply( + Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( phi + lambda ) / 2 )), Math.cos( theta / 2 )); + const matrix = new Q.Matrix( + [ a, b ], + [ c, d ]) + return new Q.Qubit( matrix.multiply( qubit )) + }, + has_parameters: true + }), + 'NOT1_2', new Q.Gate({ + + symbol: 'V', + symbolAmazonBraket: 'v', + symbolSvg: '', + name: '√Not', + nameCss: 'not1-2', + matrix: new Q.Matrix( + [ new Q.ComplexNumber( 1, 1 ) / 2, new Q.ComplexNumber( 1, -1 ) / 2 ], + [ new Q.ComplexNumber( 1, -1 ) / 2, new Q.ComplexNumber( 1, 1 ) / 2 ]) + }), + 'PI_8_Dagger', new Q.Gate({ + + symbol: 'T†', + symbolAmazonBraket: 'ti', + symbolSvg: '', + name: 'PI_8_Dagger', + nameCss: 'pi8-dagger', + matrix: new Q.Matrix( + [ 1, 0 ], + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -Math.PI / 4 )) ]) + }), + 'NOT1_2_Dagger', new Q.Gate({ + + symbol: 'V†', + symbolAmazonBraket: 'vi', + symbolSvg: '', + name: '√Not_Dagger', + nameCss: 'not1-2-dagger', + matrix: new Q.Matrix( + [ new Q.ComplexNumber( 1, -1 ) / 2, new Q.ComplexNumber( 1, 1 ) / 2 ], + [ new Q.ComplexNumber( 1, 1 ) / 2, new Q.ComplexNumber( 1, -1 ) / 2 ]) + }), + //Note that S, S_Dagger, PI_8, and PI_8_Dagger can all be implemented by applying the PHASE gate + //using certain values of phi. + //These gates are included for completeness. + 'S', new Q.Gate({ + symbol: 'S*', //Gotta think of a better symbol name... + symbolAmazonBraket: 's', + symbolSvg: '', + name: 'π ÷ 4', + nameCss: 'pi4', + matrix: new Q.Matrix( + [ 1, 0 ], + [ 0, new Q.ComplexNumber( 0, 1 ) ]) + }), + 'S_Dagger', new Q.Gate({ + + symbol: 'S†', + symbolAmazonBraket: 'si', + symbolSvg: '', + name: 'π ÷ 4 Dagger', + nameCss: 'pi4-dagger', + matrix: new Q.Matrix( + [ 1, 0 ], + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -1 )) ]) + }), + // Operate on 2 qubits. 'SWAP', new Q.Gate({ - symbol: 'S', - symbolAmazonBraket: 's',// !!! Double check this !!! + symbol: 'S', + symbolAmazonBraket: 'swap', symbolSvg: '', name: 'Swap', nameCss: 'swap', - matrix: new Q.Matrix( - [ 1, 0, 0, 0 ], - [ 0, 0, 1, 0 ], - [ 0, 1, 0, 0 ], - [ 0, 0, 0, 1 ]) + parameters: { "phi" : 0.0 }, + updateMatrix$: function( phi ) { + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ], + [ 0, Q.ComplexNumber.E.power(new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + applyToQubit: function( qubit, phi ) { + + if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi )), 0 ], + [ 0, new Q.ComplexNumber( 0, 1 ), 0, 0 ], + [ 0, 0, 0, 1 ] + ) + return new Q.Qubit( matrix.multiply( qubit )) + }, + can_be_controlled: true, + has_parameters: true, + is_multi_qubit: true }), 'SWAP1_2', new Q.Gate({ @@ -2653,8 +2877,258 @@ Q.Gate.createConstants( [ 1, 0, 0, 0 ], [ 0, new Q.ComplexNumber( 0.5, 0.5 ), new Q.ComplexNumber( 0.5, -0.5 ), 0 ], [ 0, new Q.ComplexNumber( 0.5, -0.5 ), new Q.ComplexNumber( 0.5, 0.5 ), 0 ], - [ 0, 0, 0, 1 ]) - }) + [ 0, 0, 0, 1 ]), + is_multi_qubit: true + }), + 'ISWAP', new Q.Gate({ + + symbol: 'iS', + symbolAmazonBraket: 'iswap', + symbolSvg: '', + name: 'Imaginary Swap', + nameCss: 'iswap', + matrix: new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, 0, new Q.ComplexNumber( 0, 1 ), 0 ], + [ 0, new Q.ComplexNumber( 0, 1 ), 0, 0 ], + [ 0, 0, 0, 1 ]), + is_multi_qubit: true + }), + 'ISING-XX', new Q.Gate({ + + symbol: 'XX', + symbolAmazonBraket: 'xx', + symbolSvg: '', + name: 'Ising XX Coupling', + nameCss: 'ising-xx-coupling', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ], + [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], + [ 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], + [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ]) + return this + }, + applyToQubit: function( qubit, phi ) { + if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ Math.cos( phi / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ], + [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ], + [ 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], + [ new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ] + ) + return new Q.Qubit( matrix.multiply( qubit )) + }, + is_multi_qubit: true, + has_parameters: true + }), + 'ISING-XY', new Q.Gate({ + + symbol: 'XY', + symbolAmazonBraket: 'xy', + symbolSvg: '', + name: 'Ising XY Coupling', + nameCss: 'ising-xy-coupling', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], + [ 0, new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + applyToQubit: function( qubit, phi ) { + if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, Math.sin( phi / 2 )), 0 ], + [ 0, new Q.ComplexNumber( 0, Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], + [ 0, 0, 0, 1 ] + ) + return new Q.Qubit( matrix.multiply( qubit )) + }, + is_multi_qubit: true, + has_parameters: true + }), + 'ISING-YY', new Q.Gate({ + + symbol: 'YY', + symbolAmazonBraket: 'yy', + symbolSvg: '', + name: 'Ising YY Coupling', + nameCss: 'ising-yy-coupling', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )) ], + [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ], + [ 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ], + [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ]) + return this + }, + applyToQubit: function( qubit, phi ) { + if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ Math.cos( phi / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ], + [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ], + [ 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], + [ new Q.ComplexNumber( 0, Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ] + ) + return new Q.Qubit( matrix.multiply( qubit )) + }, + is_multi_qubit: true, + has_parameters: true + }), + 'ISING-ZZ', new Q.Gate({ + + symbol: 'ZZ', + symbolAmazonBraket: 'zz', + symbolSvg: '', + name: 'Ising ZZ Coupling', + nameCss: 'ising-zz-coupling', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0, 0 ], + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0 ], + [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0], + [ 0, 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )) ]) + return this + }, + applyToQubit: function( qubit, phi ) { + if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0, 0, 0 ], + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0, 0 ], + [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0], + [ 0, 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -phi / 2 )) ] + ) + return new Q.Qubit( matrix.multiply( qubit )) + }, + is_multi_qubit: true, + has_parameters: true + }), + 'CPhase00', new Q.Gate({ + + symbol: '00', //placeholder + symbolAmazonBraket: 'cphaseshift00', + symbolSvg: '', + name: 'Controlled Phase Shift 00', + nameCss: 'cphase00', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + applyToQubit: function( qubit, phi ) { + if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi )), 0, 0, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ] + ) + return new Q.Qubit( matrix.multiply( qubit )) + }, + is_multi_qubit: true, + has_parameters: true + }), + 'CPhase01', new Q.Gate({ + + symbol: '01', //placeholder + symbolAmazonBraket: 'cphaseshift01', + symbolSvg: '', + name: 'Controlled Phase Shift 01', + nameCss: 'cphase01', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + applyToQubit: function( qubit, phi ) { + if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ] + ) + return new Q.Qubit( matrix.multiply( qubit )) + }, + is_multi_qubit: true, + has_parameters: true + }), + 'CPhase10', new Q.Gate({ + + symbol: '10', //placeholder + symbolAmazonBraket: 'cphaseshift10', + symbolSvg: '', + name: 'Controlled Phase Shift 10', + nameCss: 'cphase01', + parameters: { "phi" : 1 }, + updateMatrix$: function( phi ) { + + if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ], + [ 0, 0, 0, 1 ]) + return this + }, + applyToQubit: function( qubit, phi ) { + if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0 ], + [ 0, 0, 0, 1 ] + ) + return new Q.Qubit( matrix.multiply( qubit )) + }, + is_multi_qubit: true, + has_parameters: true + }), + 'CSWAP', new Q.Gate({ + + symbol: 'CSWAP', + symbolAmazonBraket: 'cswap', + symbolSvg: '', + name: 'Controlled Swap', + nameCss: 'controlled-swap', + matrix: new Q.Matrix( + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1] + ) + }) /* @@ -2667,12 +3141,14 @@ Q.Gate.createConstants( */ -) +) + -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. @@ -2738,7 +3214,7 @@ Object.assign( Q.History.prototype, { // Are we recording this history? // Usually, yes. - // But if our history state is “playback” + // But if our history state is “playback” // then we will NOT record this. if( this.isRecording ){ @@ -2815,7 +3291,7 @@ Object.assign( Q.History.prototype, { if( direction < 0 ) this.index -- - // It’s now safe to turn recording back on. + // It’s now safe to turn recording back on. this.isRecording = true @@ -2838,18 +3314,18 @@ Object.assign( Q.History.prototype, { } return this.entries.reduce( function( output, entry, i ){ - output += '\n\n'+ i + ' ════════════════════════════════════════'+ + output += '\n\n'+ i + ' ════════════════════════════════════════'+ entry.reduce( function( output, entry, i ){ - output += '\n\n '+ i +' ────────────────────────────────────────\n' + output += '\n\n '+ i +' ────────────────────────────────────────\n' if( entry.redo ){ - output += '\n ⟳ Redo ── '+ entry.redo.name +' ' + output += '\n ⟳ Redo ── '+ entry.redo.name +' ' if( entry.redo.args ) output += entry.redo.args.reduce( argsParse, '' ) } output += entry.undo.reduce( function( output, entry, i ){ - output += '\n ⟲ Undo '+ i +' ── '+ entry.name +' ' + output += '\n ⟲ Undo '+ i +' ── '+ entry.name +' ' if( entry.args ) output += entry.args.reduce( argsParse, '' ) return output @@ -2944,7 +3420,6 @@ Object.assign( Q.Circuit, { if( text === undefined ) return new Q.Circuit() - // Is this a String Template -- as opposed to a regular String? // If so, let’s convert it to a regular String. // Yes, this maintains the line breaks. @@ -3064,7 +3539,6 @@ Object.assign( Q.Circuit, { fromTableTransposed: function( table ){ - const bandwidth = table.length, timewidth = table.reduce( function( max, moments ){ @@ -3084,7 +3558,6 @@ Object.assign( Q.Circuit, { const momentIndex = m + 1, operation = table[ r ][ m ] - let siblingHasBeenFound = false for( let s = 0; s < r; s ++ ){ @@ -3097,7 +3570,6 @@ Object.assign( Q.Circuit, { // We’ve found a sibling ! - const operationsIndex = circuit.operations.findIndex( function( operation ){ return ( @@ -3245,7 +3717,6 @@ Object.assign( Q.Circuit, { if( bitsEqual ){ // console.log( 'bits ARE equal' ) - let istar = 0, jstar = 0, @@ -3257,8 +3728,6 @@ Object.assign( Q.Circuit, { istar |= (( i & ( 1 << q )) >> q ) << k jstar |= (( j & ( 1 << q )) >> q ) << k } - - //console.log( 'U.read( istar, jstar )', U.read( istar, jstar ).toText() ) // console.log( 'before write$', result.toTsv()) @@ -3373,11 +3842,15 @@ Object.assign( Q.Circuit, { // Yikes. May need to separate registerIndices in to controls[] and targets[] ?? // Works for now tho..... - - for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){ - - U = Q.Circuit.controlled( U ) - // console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() ) + // Houston we have a problem. Turns out, not every gate with registerIndices.length > 1 is + // controlled. + // This is a nasty fix, leads to a lot of edge cases. (For instance: hard-coding cswaps...) But just experimenting. + if(!operation.gate.is_multi_qubit || (operation.gate.symbol == 'S' && operation.registerIndices.length > 2) && operation.gate.can_be_controlled) { + for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){ + + U = Q.Circuit.controlled( U ) + //console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() ) + } } @@ -3387,8 +3860,6 @@ Object.assign( Q.Circuit, { // and wow -- tracking down that bug was painful! const registerIndices = operation.registerIndices.slice() - - state = Q.Circuit.expandMatrix( circuit.bandwidth, @@ -3399,7 +3870,6 @@ Object.assign( Q.Circuit, { - operationsCompleted ++ const progress = operationsCompleted / operationsTotal @@ -3465,7 +3935,6 @@ Object.assign( Q.Circuit, { - return matrix } @@ -3982,46 +4451,102 @@ https://cirq.readthedocs.io/en/stable/tutorial.html return headers }, toAmazonBraket: function(){ - + let is_valid_braket_circuit = true const header = `import boto3 -from braket.aws import AwsQuantumSimulator, AwsQuantumSimulatorArns +from braket.aws import AwsDevice from braket.circuits import Circuit -aws_account_id = boto3.client("sts").get_caller_identity()["Account"] -device = AwsQuantumSimulator(AwsQuantumSimulatorArns.QS1) -s3_folder = (f"braket-output-{aws_account_id}", "folder-name") - -` +my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket +my_prefix = "Your-Folder-Name" # the name of the folder in the bucket +s3_folder = (my_bucket, my_prefix)\n +device = LocalSimulator()\n\n` +//TODO (ltnln): Syntax is different for simulators and actual quantum computers. Should there be a default? Should there be a way to change? +//vs an actual quantum computer? May not be necessary. + let variables = '' + let num_unitaries = 0 //`qjs_circuit = Circuit().h(0).cnot(0,1)` + //ltnln change: from gate.AmazonBraketName -> gate.symbolAmazonBraket let circuit = this.operations.reduce( function( string, operation ){ - - let awsGate = operation.gate.AmazonBraketName !== undefined ? - operation.gate.AmazonBraketName : + let awsGate = operation.gate.symbolAmazonBraket !== undefined ? + operation.gate.symbolAmazonBraket : operation.gate.symbol.substr( 0, 1 ).toLowerCase() + if( operation.gate.symbolAmazonBraket === undefined ) is_valid_braket_circuit = false + if( operation.gate.symbol === 'X' ) { + if( operation.registerIndices.length === 1 ) awsGate = operation.gate.symbolAmazonBraket + else if( operation.registerIndices.length === 2 ) awsGate = 'cnot' + else if( operation.registerIndices.length === 3) awsGate = 'ccnot' + else is_valid_braket_circuit = false + } + + else if( operation.gate.symbol === 'S' ) { + if( operation.gate.parameters["phi"] === 0 ) { + awsGate = operation.registerIndices.length == 2 ? awsGate : "cswap" + return string +'.'+ awsGate +'(' + + operation.registerIndices.reduce( function( string, registerIndex, r ){ - if( operation.gate.symbol === 'X' && - operation.registerIndices.length > 1 ){ + return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) - awsGate = 'cnot' + }, '' ) + ')' + } + awsGate = 'pswap' } - if( operation.gate.symbol === '*' ){ + //ltnln note: removed the if( operation.gate.symbol == '*') branch as it should be covered by + //the inclusion of the CURSOR gate. + else if( operation.gate.symbol === 'Y' || operation.gate.symbol === 'Z' || operation.gate.symbol === 'P' ) { + if( operation.registerIndices.length === 1) awsGate = operation.gate.symbolAmazonBraket + else if( operation.registerIndices.length === 2 ) awsGate = (operation.gate.symbol === 'Y') ? 'cy' : (operation.gate.symbol === 'Z') ? 'cz' : 'cphaseshift' + else is_valid_braket_circuit = false + } + //for all unitary gates, there must be a line of code to initialize the matrix for use + //in Braket's .u(matrix=my_unitary, targets[0]) function + else if( operation.gate.symbol === 'U') { + //check that this truly works as a unique id + is_valid_braket_circuit &= operation.registerIndices.length === 1 + const new_matrix = `unitary_` + num_unitaries + num_unitaries++ + const a = Q.ComplexNumber.toText(Math.cos(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), + Math.sin(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2)) + .replace('i', 'j') + const b = Q.ComplexNumber.toText(-Math.cos(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), + -Math.sin(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2) + .replace('i', 'j') + const c = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), + -Math.sin((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2) + .replace('i', 'j') + const d = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), + Math.sin((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2)) / 2) + .replace('i', 'j') + variables += new_matrix + ` = np.array(` + + `[[` + a + ', ' + b + `],`+ + `[` + c + ', ' + d + `]])\n` + return string +'.'+ awsGate +'(' + new_matrix +','+ + operation.registerIndices.reduce( function( string, registerIndex, r ){ - awsGate = 'i' + return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) + + }, '' ) + ')' } - + // I believe this line should ensure that we don't include any controlled single-qubit gates that aren't allowed in Braket. + // The registerIndices.length > 1 technically shouldn't be necessary, but if changes are made later, it's just for safety. + else is_valid_braket_circuit &= (operation.registerIndices.length === 1) || ( operation.registerIndices.length > 1 && operation.gate.is_multi_qubit ) return string +'.'+ awsGate +'(' + operation.registerIndices.reduce( function( string, registerIndex, r ){ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) - }, '' ) + ')' + }, '' ) + ((operation.gate.has_parameters) ? + Object.values( operation.gate.parameters ).reduce( function( string, parameter ) { + return string + "," + parameter + }, '') + : '') + ')' }, 'qjs_circuit = Circuit()' ) + variables += '\n' if( this.operations.length === 0 ) circuit += '.i(0)'// Quick fix to avoid an error here! const footer = `\n\ntask = device.run(qjs_circuit, s3_folder, shots=100) print(task.result().measurement_counts)` - return header + circuit + footer + return is_valid_braket_circuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###` }, toLatex: function(){ @@ -4228,15 +4753,13 @@ print(task.result().measurement_counts)` }, - set$: function( gate, momentIndex, registerIndices ){ + set$: function( gate, momentIndex, registerIndices, parameters = {} ){ const circuit = this - - // Is this a valid gate? - - if( typeof gate === 'string' ) gate = Q.Gate.findBySymbol( gate ) - if( gate instanceof Q.Gate !== true ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate ) + // We clone the gate rather than using the constant; this way, if we change it's parameters, we don't change the constant. + if( typeof gate === 'string' ) gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( gate ) ) + if( gate instanceof Q.Gate !== true ) return Q.error( `Q.Circuit attempted to add a gate (${ gate }) to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate ) // Is this a valid moment index? @@ -4307,7 +4830,11 @@ print(task.result().measurement_counts)` // Aren’t you glad we handle all this for you? const - isControlled = registerIndices.length > 1 && gate !== Q.Gate.SWAP, + //TODO: For ltnln (have to fix) + // a) allow users to control whatever they want! Just because it's not allowed in Braket + // doesn't mean they shouldn't be allowed to do it in Q! (Probably fixable by adjusting toAmazonBraket) + // b) Controlling a multi_qubit gate will not treat the control icon like a control gate! + isControlled = registerIndices.length > 1 && gate !== Q.Gate.SWAP && gate.can_be_controlled !== undefined operation = { gate, @@ -4315,6 +4842,9 @@ print(task.result().measurement_counts)` registerIndices, isControlled } + //perform parameter update here!!! + Object.keys(parameters).forEach( element => { parameters[element] = +parameters[element] }) + if(gate.has_parameters) gate.updateMatrix$.apply( gate, Object.values(parameters) ) this.operations.push( operation ) @@ -4327,14 +4857,15 @@ print(task.result().measurement_counts)` // Let’s make history. - + const redo_args = Array.from( arguments ) + Object.assign( redo_args[ redo_args.length - 1 ], parameters ) this.history.record$({ redo: { name: 'set$', func: circuit.set$, - args: Array.from( arguments ) + args: redo_args }, undo: [{ @@ -4903,6 +5434,40 @@ Q.Circuit.createConstants( + + + + + + + + + + + +/* + + +%%HTML + + + + + +%%javascript +Q.braket( element ) + + + + +*/ + + + +//%%javascript + + + // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. @@ -5088,7 +5653,6 @@ Q.Circuit.Editor = function( circuit, targetEl ){ boardContainerEl.classList.add( 'Q-circuit-board-container' ) //boardContainerEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress ) boardContainerEl.addEventListener( 'mouseleave', function(){ - Q.Circuit.Editor.unhighlightAll( circuitEl ) }) @@ -5100,7 +5664,9 @@ Q.Circuit.Editor = function( circuit, targetEl ){ boardEl.appendChild( backgroundEl ) backgroundEl.classList.add( 'Q-circuit-board-background' ) - + const parameterEl = createDiv() + boardEl.appendChild( parameterEl ) + parameterEl.classList.add( 'Q-parameters-box' ) // Create background highlight bars // for each row. @@ -5170,7 +5736,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){ } - // Add “Add register” button. + // Add “Add register” button.q const addRegisterEl = createDiv() foregroundEl.appendChild( addRegisterEl ) @@ -5227,7 +5793,6 @@ Q.Circuit.Editor = function( circuit, targetEl ){ // Add operations. circuit.operations.forEach( function( operation ){ - Q.Circuit.Editor.set( circuitEl, operation ) }) @@ -5349,11 +5914,11 @@ Object.assign( Q.Circuit.Editor, { const r = min + Math.random() * ( max - min ) return Math.floor( Math.random() * 2 ) ? r : -r } - + + //ltnln: added missing Braket operations. paletteEl.classList.add( 'Q-circuit-palette' ) - - 'HXYZPT*' - .split( '' ) + 'H,X,Y,Z,P,Rx,Ry,Rz,U,V,V†,S*,S†,T,T†,00,01,10,√S,iS,XX,XY,YY,ZZ,*' + .split( ',' ) .forEach( function( symbol ){ const gate = Q.Gate.findBySymbol( symbol ) @@ -5446,7 +6011,6 @@ Q.Circuit.Editor.prototype.onExternalSet = function( event ){ } } Q.Circuit.Editor.set = function( circuitEl, operation ){ - const backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ), foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ), @@ -5454,7 +6018,6 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){ operationIndex = circuitEl.circuit.operations.indexOf( operation ) operation.registerIndices.forEach( function( registerIndex, i ){ - const operationEl = document.createElement( 'div' ) foregroundEl.appendChild( operationEl ) operationEl.classList.add( 'Q-circuit-operation', 'Q-circuit-operation-'+ operation.gate.nameCss ) @@ -5468,7 +6031,9 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){ operationEl.setAttribute( 'title', operation.gate.name ) operationEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( operation.momentIndex ) operationEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( registerIndex ) - + if( operation.gate.has_parameters ) Object.keys(operation.gate.parameters).forEach( element => { + operationEl.setAttribute( element, operation.gate.parameters[element] ) //adds a parameter attribute to the operation! + }) const tileEl = document.createElement( 'div' ) operationEl.appendChild( tileEl ) tileEl.classList.add( 'Q-circuit-operation-tile' ) @@ -5517,7 +6082,6 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){ } }) if( operation.isControlled && i === 0 ){ - operationEl.classList.add( 'Q-circuit-operation-control' ) operationEl.setAttribute( 'title', 'Control' ) tileEl.innerText = '' @@ -5622,12 +6186,10 @@ Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){ if( allSiblingsPresent !== true ) return false - // Note the different gate types present // among the selected operations. const gates = selectedOperations.reduce( function( gates, operationEl ){ - const gateSymbol = operationEl.getAttribute( 'gate-symbol' ) if( !Q.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1 else gates[ gateSymbol ] ++ @@ -5657,7 +6219,6 @@ Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){ totalNotControlled: 0 }) - // This could be ONE “identity cursor” // and one or more of a regular single gate // that is NOT already controlled. @@ -5741,7 +6302,6 @@ Q.Circuit.Editor.createControl = function( circuitEl ){ ) }) circuit.set$( - targets[ 0 ].getAttribute( 'gate-symbol' ), +control.getAttribute( 'moment-index' ), [ +control.getAttribute( 'register-index' )].concat( @@ -5776,7 +6336,6 @@ Q.Circuit.Editor.isValidSwapCandidate = function( circuitEl ){ // We can only swap between two registers. // No crazy rotation-swap bullshit. (Yet.) - if( selectedOperations.length !== 2 ) return false @@ -5974,7 +6533,6 @@ Q.Circuit.Editor.onPointerMove = function( event ){ // Let’s prioritize any element that is “sticky” // which means it can appear OVER another grid cell. - const cellEl = foundEls.find( function( el ){ @@ -6074,9 +6632,6 @@ Q.Circuit.Editor.onPointerMove = function( event ){ - - - /////////////////////// // // // Pointer PRESS // @@ -6085,25 +6640,21 @@ Q.Circuit.Editor.onPointerMove = function( event ){ Q.Circuit.Editor.onPointerPress = function( event ){ - - // This is just a safety net // in case something terrible has ocurred. // (ex. Did the user click and then their mouse ran // outside the window but browser didn’t catch it?) - + console.log("event target: ", event.target); if( Q.Circuit.Editor.dragEl !== null ){ Q.Circuit.Editor.onPointerRelease( event ) return } - - const targetEl = event.target, circuitEl = targetEl.closest( '.Q-circuit' ), paletteEl = targetEl.closest( '.Q-circuit-palette' ) - + parameterEl = targetEl.closest( '.Q-parameters-box' ) // If we can’t find a circuit that’s a really bad sign // considering this event should be fired when a circuit @@ -6111,7 +6662,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){ if( !circuitEl && !paletteEl ) return - // This is a bit of a gamble. // There’s a possibility we’re not going to drag anything, // but we’ll prep these variables here anyway @@ -6126,9 +6676,8 @@ Q.Circuit.Editor.onPointerPress = function( event ){ // Are we dealing with a circuit interface? // ie. NOT a palette interface. - if( circuitEl ){ + if( circuitEl && !parameterEl ){ - // Shall we toggle the circuit lock? const @@ -6233,7 +6782,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){ if( !cellEl ) return - // Once we know what cell we’ve pressed on // we can get the momentIndex and registerIndex // from its pre-defined attributes. @@ -6262,7 +6810,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){ inputEl = targetEl.closest( '.Q-circuit-input' ), operationEl = targetEl.closest( '.Q-circuit-operation' ) - // +++++++++++++++ // We’ll have to add some input editing capability later... // Of course you can already do this in code! @@ -6296,7 +6843,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){ if( operationsSelectedLength === operations.length ){ operations.forEach( function( el ){ - el.classList.remove( 'Q-circuit-cell-selected' ) }) } @@ -6341,14 +6887,21 @@ Q.Circuit.Editor.onPointerPress = function( event ){ // then GO HOME. if( !operationEl ) return - + // If we've doubleclicked on an operation and the operation has parameters, we should be able + // to edit those parameters regardless of whether or not the circuit is locked. + if( event.detail == 2) { + const operation = Q.Gate.findBySymbol(operationEl.getAttribute( 'gate-symbol' )) + if( operation.has_parameters ) { + Q.Circuit.Editor.onDoubleclick( event, operationEl ) + return + } + } // Ok now we know we are dealing with an operation. // This preserved selection state information // will be useful for when onPointerRelease is fired. if( operationEl.classList.contains( 'Q-circuit-cell-selected' )){ - operationEl.wasSelected = true } else operationEl.wasSelected = false @@ -6454,7 +7007,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){ dragEl.registerIndex = registerIndex } else if( paletteEl ){ - const operationEl = targetEl.closest( '.Q-circuit-operation' ) if( !operationEl ) return @@ -6468,6 +7020,29 @@ Q.Circuit.Editor.onPointerPress = function( event ){ dragEl.offsetX = bounds.left - x dragEl.offsetY = bounds.top - y } + else if( parameterEl ){ + const exitEl = targetEl.closest( '.Q-parameter-box-exit' ) + if( !exitEl ) return + parameterEl.style.display = 'none' + const foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ) + operationEl = foregroundEl.querySelector( `[moment-index="${ parameterEl.getAttribute( 'operation-moment-index' )}"]` + + `[register-index="${ parameterEl.getAttribute( 'operation-register-index' )}"]` ) + parameters = {} + operationSkeleton = Q.Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' )) + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = operationEl.getAttribute( key ) ? operationEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + //on exiting the parameter-input-box, we should update the circuit!! + circuitEl.circuit.set$( + operationEl.getAttribute( 'gate-symbol' ), + +operationEl.getAttribute( 'moment-index' ), + operationEl.getAttribute( 'register-indices' ) ? operationEl.getAttribute( 'register-indices' ).split(',').map( i => +i ) : + [ +operationEl.getAttribute( 'register-index' )], + parameters + ) + parameterEl.innerHTML = "" + return + } dragEl.timestamp = Date.now() @@ -6496,10 +7071,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ // If there’s no dragEl then bail immediately. - if( Q.Circuit.Editor.dragEl === null ) return - - // Looks like we’re moving forward with this plan, // so we’ll take control of the input now. @@ -6514,13 +7086,18 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ // under the mouse / finger, skipping element [0] // because that will be the clipboard. - const - { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ), - boardContainerEl = document.elementsFromPoint( x, y ) - .find( function( el ){ - - return el.classList.contains( 'Q-circuit-board-container' ) - }), + // doing this because elementsFromPoint() doesnt work well with JSDOM for testing purposes + const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ) + const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container") + if( boardContainerAll.length === 0 ) return + let boardContainerEl = Array.from(boardContainerAll).find((element) => { + let rect = element.getBoundingClientRect() + let clientX = rect.left + let clientY = rect.top + let height = element.offsetHeight + let width = element.offsetWidth + return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height ) + }) returnToOrigin = function(){ @@ -6665,7 +7242,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ droppedAtRegisterIndex < 1 || droppedAtRegisterIndex > circuit.bandwidth ){ - returnToOrigin() return } @@ -6716,7 +7292,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ const registerIndicesString = childEl.getAttribute( 'register-indices' ) if( registerIndicesString ){ - // What are ALL of the registerIndices // associated with this multi-register operation? // (We may use them later as a checklist.) @@ -6800,6 +7375,13 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ if( registerIndices.length === foundComponents.length ){ + const operationSkeleton = Q.Gate.findBySymbol( gatesymbol ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } //circuit.set$( setCommands.push([ @@ -6820,7 +7402,8 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex + siblingDelta } return registerIndexTarget - }) + }), + parameters // ) ]) } @@ -6841,7 +7424,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ // can sit at each register index. // This copies registerIndices, // but inverts the key : property relationship. - const registerMap = registerIndices .reduce( function( registerMap, registerIndex, r ){ @@ -6869,7 +7451,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ // Now we can seat it at its new position. // Note: This may OVERWRITE one of its siblings! // And that’s ok. - foundComponents.forEach( function( component ){ const @@ -6879,7 +7460,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ // Now put it where it wants to go, // possibly overwriting a sibling component! - + //ltnln: if a multiqubit operation component that requires a sibling, overwrites its sibling, both/all components should be destroyed registerMap[ componentRegisterIndex + draggedRegisterDelta @@ -6904,34 +7485,47 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ // ie. if a dragged sibling overwrote a seated one. .filter( function( entry ){ - return Q.isUsefulInteger( entry ) }) - + const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } // Finally, we’re ready to set. - // circuit.set$( setCommands.push([ - + //ltnln: if a component of an operation that requires a sibling pair overwrites its sibling, we want it removed entirely. + fixedRegistersIndices.length < 2 && Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? + Q.Gate.NOOP : childEl.getAttribute( 'gate-symbol' ), momentIndexTarget, - fixedRegistersIndices + fixedRegistersIndices, + parameters // ) ]) } else { - remainingComponents.forEach( function( componentEl, i ){ - //circuit.set$( + const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } setCommands.push([ - +componentEl.getAttribute( 'register-indices-index' ) ? + +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? gatesymbol : Q.Gate.NOOP, +componentEl.getAttribute( 'moment-index' ), - +componentEl.getAttribute( 'register-index' ) + +componentEl.getAttribute( 'register-index' ), + parameters // ) ]) }) @@ -6941,15 +7535,22 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ // all the components that were part of the drag. foundComponents.forEach( function( componentEl ){ - - // circuit.set$( + const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ) + parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } setCommands.push([ - - +componentEl.getAttribute( 'register-indices-index' ) ? - gatesymbol : + //ltnln: temporary fix: certain multiqubit operations should only be represented in pairs of registers. If one is removed (i.e. a single component of the pair) + //then the entire operation should be removed. + +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ? + componentEl.getAttribute( 'gate-symbol' ) : Q.Gate.NOOP, +componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta, +componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta, + parameters // ) ]) }) @@ -6983,7 +7584,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ else { - + // First, if this operation comes from a circuit // (and not a circuit palette) // make sure the old positions are cleared away. @@ -7005,11 +7606,24 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ // in its new home. // circuit.set$( + let registerIndices = [ registerIndexTarget ] + //ltnln: By default, multiqubit gates appear in pairs on the circuit rather than + // requiring the user to have to pair them like with Swap/CNot. + const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' )) + if(operationSkeleton.is_multi_qubit ) { + registerIndices.push( registerIndexTarget == circuit.bandwidth ? registerIndexTarget - 1 : registerIndexTarget + 1) + } + let parameters = {} + if( operationSkeleton.has_parameters ) { + Object.keys( operationSkeleton.parameters ).forEach( key => { + parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ] + }) + } setCommands.push([ - childEl.getAttribute( 'gate-symbol' ), momentIndexTarget, - [ registerIndexTarget ] + registerIndices, + parameters // ) ]) } @@ -7053,8 +7667,83 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ } + ///////////////////////// + // // + // Pointer DOUBLECLICK // + // // +///////////////////////// +//ltnln: my trying out an idea for parameterized gates... +Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) { + // assumption for the following 3 lines is that we've already decided that we are on-top of a valid gate operation in + // the circuit + const operation = Q.Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' )) + const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ) + + const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container") + if( boardContainerAll.length === 0 ) return + let boardContainerEl = Array.from(boardContainerAll).find((element) => { + let rect = element.getBoundingClientRect() + let clientX = rect.left + let clientY = rect.top + let height = element.offsetHeight + let width = element.offsetWidth + return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height ) + }) + if( !boardContainerEl ) return; + const parameterEl = boardContainerEl.querySelector('.Q-parameters-box') + const exit = document.createElement( 'button' ) + parameterEl.appendChild( exit ) + exit.classList.add( 'Q-parameter-box-exit' ) + exit.appendChild(document.createTextNode( '⬅' )) + parameterEl.setAttribute( "operation-moment-index", operationEl.getAttribute( 'moment-index' )) + parameterEl.setAttribute( "operation-register-index", operationEl.getAttribute( 'register-index' )) + + + //here we generate queries for each parameter that the gate operation takes! + const parameters = Object.keys(operation.parameters) + parameters.forEach( element => { + if( operation.parameters && operation.parameters[element] !== null ) { + const input_fields = document.createElement( 'div' ) + parameterEl.appendChild( input_fields ) + input_fields.classList.add( 'Q-parameter-box-input-container' ) + const label = document.createElement( "span" ) + input_fields.appendChild( label ) + label.classList.add( 'Q-parameter-input-label' ) + label.appendChild(document.createTextNode( element )) + + const textbox = document.createElement( "input" ) + input_fields.appendChild( textbox ) + textbox.classList.add( 'Q-parameter-box-input' ) + textbox.setAttribute( 'type', 'text' ) + textbox.setAttribute( 'placeholder', element ) + textbox.setAttribute( 'value', operationEl.getAttribute(element) ? operationEl.getAttribute(element) : operation.parameters[element] ) + //set textbox to update the operation instance (cellEl)'s parameters on value change + textbox.addEventListener( "change", () => { + let parameterValue + let oldValue = operationEl.getAttribute( element ) + if( !oldValue ) oldValue = operation.parameters[ element ] + try { + //TODO: figure out how to properly import the mathjs library... + parameterValue = +(textbox.value.toLowerCase()); + } + catch( err ) { + parameterValue = oldValue + } + + if( !parameterValue || parameterValue === Infinity ) textbox.value = oldValue.toString() + else { + operationEl.setAttribute( element, parameterValue ) + textbox.value = parameterValue.toString() + } + }) + + } + }) + parameterEl.classList.toggle('overlay') + parameterEl.style.display = 'block' +} /////////////////// @@ -7078,3 +7767,108 @@ window.addEventListener( 'touchend', Q.Circuit.Editor.onPointerRelease ) +/* + + +%%HTML + + + + + +%%javascript +Q.braket( element ) + + + + +*/ + + + +//%%javascript + + + +Q.braket = function(){ + + + // Create the HTML bits we need, + // contain them all together, + // and output them to Jupyter. + if( arguments.length === 0 || arguments.length > 3 ) return; + const element = arguments[0]; + const args = (Array.from(arguments)).slice(1); + let circuit + if(args.length === 0) { + circuit = new Q( 4, 8 ) + } + else if(args.length === 1) { + circuit = new Q( args[0] ) + } + else { + circuit = new Q( args[0], args[1] ) + } + container = document.createElement( 'div' ) + container.appendChild( Q.Circuit.Editor.createPalette() ) + container.appendChild( circuit.toDom() ) + element.html( container ) + + + // We’re going to take this SLOOOOOOOOWLY + // because there are many potential things to debug. + + const thisCell = Jupyter.notebook.get_selected_cell() + // console.log( 'thisCell', thisCell ) + + const thisCellIndex = Jupyter.notebook.get_cells().indexOf( thisCell ) + // console.log( 'thisCellIndex', thisCellIndex ) + + const nextCell = Jupyter.notebook.insert_cell_below( 'code', thisCellIndex - 1 ) + const nextNextCell = Jupyter.notebook.insert_cell_below( 'markdown', Jupyter.notebook.get_cells().indexOf( thisCell ) - 1 ) + // console.log( 'nextCell', nextCell ) + + nextCell.set_text( circuit.toAmazonBraket() ) + nextNextCell.set_text( circuit.report$() ) + + + + + + + window.addEventListener( 'Q gui altered circuit', function( event ){ + + // updatePlaygroundFromDom( event.detail.circuit ) + if( event.detail.circuit === circuit ){ + + console.log( 'Updating circuit from GUI', circuit ) + circuit.evaluate$() + nextCell.set_text( circuit.toAmazonBraket() ) + + } + }) + + window.addEventListener( 'Q.Circuit.evaluate completed', function( event ) { + if( event.detail.circuit === circuit ) { + nextNextCell.set_text( circuit.report$() ) + } + }) + + + // nextCell.render() + + // console.log( 'thisCell', thisCell ) + // console.log( 'nextCell', nextCell ) + // console.log( 'thisCellIndex', thisCellIndex ) + + // code = Jupyter.notebook.insert_cell_{0}('code'); + // code.set_text(atob("{1}")) + + // var t_cell = Jupyter.notebook.get_selected_cell() + // t_cell.set_text(' \\n{}') + // var t_index = Jupyter.notebook.get_cells().indexOf(t_cell) + // Jupyter.notebook.to_markdown(t_index) + // Jupyter.notebook.get_cell(t_index).render() +} + +module.exports = Q \ No newline at end of file diff --git a/packages/quantum-js-cli/__test__/runner.test.js b/packages/quantum-js-cli/__test__/runner.test.js index 1be8866..f16818c 100644 --- a/packages/quantum-js-cli/__test__/runner.test.js +++ b/packages/quantum-js-cli/__test__/runner.test.js @@ -95,7 +95,6 @@ describe("Testing various forms of gate-operation call expression and see that e Object.entries(quantumjs.Gate.constants).forEach(function(entry) { let gate = entry[1]; let input = gate.nameCss + (gate.is_multi_qubit ? "(1, [1, 2])" : "(1, [1])"); - console.log(input); test("Checking that evaluate operation is called once for the input '" + input + "'", () => { let circuit = quantumjs.Q(); runner.evaluateInput(input, circuit); diff --git a/packages/quantum-js-cli/index.js b/packages/quantum-js-cli/index.js index a28abe7..d9a69ae 100644 --- a/packages/quantum-js-cli/index.js +++ b/packages/quantum-js-cli/index.js @@ -1,3 +1,5 @@ const {run} = require('./runner'); run(); + +module.exports = {run}; \ No newline at end of file diff --git a/packages/quantum-js-cli/package.json b/packages/quantum-js-cli/package.json index f30a8c7..1f15a31 100644 --- a/packages/quantum-js-cli/package.json +++ b/packages/quantum-js-cli/package.json @@ -4,7 +4,9 @@ "description": "", "main": "index.js", "scripts": { - "test": "jest" + "test": "jest", + "pretter": "npx prettier --write", + "lint": "npx eslint **/*.js" }, "dependencies": { "prompt-sync": "^4.2.0", diff --git a/packages/quantum-js-util/package-lock.json b/packages/quantum-js-util/package-lock.json index 9a5f4af..8be5282 100644 --- a/packages/quantum-js-util/package-lock.json +++ b/packages/quantum-js-util/package-lock.json @@ -1,8 +1,4696 @@ { "name": "quantum-js-util", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "custom-event": "^1.0.1" + }, + "devDependencies": { + "cross-env": "^7.0.3", + "eslint": "^7.31.0", + "jest": "^27.0.6", + "jsdom": "^16.6.0", + "prettier": "2.3.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", + "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", + "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.8", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.8", + "@babel/helpers": "^7.14.8", + "@babel/parser": "^7.14.8", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", + "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", + "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", + "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", + "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", + "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", + "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz", + "integrity": "sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-simple-access": "^7.14.8", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.8", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", + "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", + "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", + "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.8.tgz", + "integrity": "sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz", + "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz", + "integrity": "sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.8.tgz", + "integrity": "sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.8", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-hoist-variables": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/parser": "^7.14.8", + "@babel/types": "^7.14.8", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz", + "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.8", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.0.6.tgz", + "integrity": "sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.0.6", + "jest-util": "^27.0.6", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/core": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.0.6.tgz", + "integrity": "sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow==", + "dev": true, + "dependencies": { + "@jest/console": "^27.0.6", + "@jest/reporters": "^27.0.6", + "@jest/test-result": "^27.0.6", + "@jest/transform": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^27.0.6", + "jest-config": "^27.0.6", + "jest-haste-map": "^27.0.6", + "jest-message-util": "^27.0.6", + "jest-regex-util": "^27.0.6", + "jest-resolve": "^27.0.6", + "jest-resolve-dependencies": "^27.0.6", + "jest-runner": "^27.0.6", + "jest-runtime": "^27.0.6", + "jest-snapshot": "^27.0.6", + "jest-util": "^27.0.6", + "jest-validate": "^27.0.6", + "jest-watcher": "^27.0.6", + "micromatch": "^4.0.4", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.0.6.tgz", + "integrity": "sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "jest-mock": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.0.6.tgz", + "integrity": "sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "@sinonjs/fake-timers": "^7.0.2", + "@types/node": "*", + "jest-message-util": "^27.0.6", + "jest-mock": "^27.0.6", + "jest-util": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.0.6.tgz", + "integrity": "sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.0.6", + "@jest/types": "^27.0.6", + "expect": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.0.6.tgz", + "integrity": "sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.0.6", + "@jest/test-result": "^27.0.6", + "@jest/transform": "^27.0.6", + "@jest/types": "^27.0.6", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^27.0.6", + "jest-resolve": "^27.0.6", + "jest-util": "^27.0.6", + "jest-worker": "^27.0.6", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/source-map": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.0.6.tgz", + "integrity": "sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.0.6.tgz", + "integrity": "sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w==", + "dev": true, + "dependencies": { + "@jest/console": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.0.6.tgz", + "integrity": "sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.0.6", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.0.6", + "jest-runtime": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.0.6.tgz", + "integrity": "sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.0.6", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.0.6", + "jest-regex-util": "^27.0.6", + "jest-util": "^27.0.6", + "micromatch": "^4.0.4", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.6.tgz", + "integrity": "sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.15", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.15.tgz", + "integrity": "sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", + "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.5.tgz", + "integrity": "sha512-+0GPv/hIFNoy8r5MFf7vRpBjnqNYNrlHdetoy23E7TYc7JB2ctwyi3GMKpviozaHQ/Sy2kBNUTvG9ywN66zV1g==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/babel-jest": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.0.6.tgz", + "integrity": "sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA==", + "dev": true, + "dependencies": { + "@jest/transform": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^27.0.6", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.6.tgz", + "integrity": "sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.0.6.tgz", + "integrity": "sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^27.0.6", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001248", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz", + "integrity": "sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", + "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz", + "integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.3.789", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.789.tgz", + "integrity": "sha512-lK4xn6C6ZF1kgLaC/EhOtC1MSKENExj3rMwGVnBTfHW81Z/Hb1Rge5YaWawN/YOXy3xCaESuE4KWSD50kOZ9rQ==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz", + "integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "ansi-styles": "^5.0.0", + "jest-get-type": "^27.0.6", + "jest-matcher-utils": "^27.0.6", + "jest-message-util": "^27.0.6", + "jest-regex-util": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-ci": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", + "integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==", + "dev": true, + "dependencies": { + "ci-info": "^3.1.1" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", + "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.0.6.tgz", + "integrity": "sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA==", + "dev": true, + "dependencies": { + "@jest/core": "^27.0.6", + "import-local": "^3.0.2", + "jest-cli": "^27.0.6" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.0.6.tgz", + "integrity": "sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.0.6.tgz", + "integrity": "sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.0.6", + "@jest/test-result": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.0.6", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.0.6", + "jest-matcher-utils": "^27.0.6", + "jest-message-util": "^27.0.6", + "jest-runtime": "^27.0.6", + "jest-snapshot": "^27.0.6", + "jest-util": "^27.0.6", + "pretty-format": "^27.0.6", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-config": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.0.6.tgz", + "integrity": "sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^27.0.6", + "@jest/types": "^27.0.6", + "babel-jest": "^27.0.6", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "is-ci": "^3.0.0", + "jest-circus": "^27.0.6", + "jest-environment-jsdom": "^27.0.6", + "jest-environment-node": "^27.0.6", + "jest-get-type": "^27.0.6", + "jest-jasmine2": "^27.0.6", + "jest-regex-util": "^27.0.6", + "jest-resolve": "^27.0.6", + "jest-runner": "^27.0.6", + "jest-util": "^27.0.6", + "jest-validate": "^27.0.6", + "micromatch": "^4.0.4", + "pretty-format": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz", + "integrity": "sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.0.6.tgz", + "integrity": "sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.0.6.tgz", + "integrity": "sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "chalk": "^4.0.0", + "jest-get-type": "^27.0.6", + "jest-util": "^27.0.6", + "pretty-format": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.0.6.tgz", + "integrity": "sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.0.6", + "@jest/fake-timers": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "jest-mock": "^27.0.6", + "jest-util": "^27.0.6", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.0.6.tgz", + "integrity": "sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.0.6", + "@jest/fake-timers": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "jest-mock": "^27.0.6", + "jest-util": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz", + "integrity": "sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.0.6.tgz", + "integrity": "sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^27.0.6", + "jest-serializer": "^27.0.6", + "jest-util": "^27.0.6", + "jest-worker": "^27.0.6", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.0.6.tgz", + "integrity": "sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^27.0.6", + "@jest/source-map": "^27.0.6", + "@jest/test-result": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.0.6", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.0.6", + "jest-matcher-utils": "^27.0.6", + "jest-message-util": "^27.0.6", + "jest-runtime": "^27.0.6", + "jest-snapshot": "^27.0.6", + "jest-util": "^27.0.6", + "pretty-format": "^27.0.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.0.6.tgz", + "integrity": "sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ==", + "dev": true, + "dependencies": { + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz", + "integrity": "sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.6.tgz", + "integrity": "sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.0.6", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.4", + "pretty-format": "^27.0.6", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.0.6.tgz", + "integrity": "sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.0.6.tgz", + "integrity": "sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.0.6.tgz", + "integrity": "sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "chalk": "^4.0.0", + "escalade": "^3.1.1", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.0.6", + "jest-validate": "^27.0.6", + "resolve": "^1.20.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.6.tgz", + "integrity": "sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "jest-regex-util": "^27.0.6", + "jest-snapshot": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.0.6.tgz", + "integrity": "sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.0.6", + "@jest/environment": "^27.0.6", + "@jest/test-result": "^27.0.6", + "@jest/transform": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-docblock": "^27.0.6", + "jest-environment-jsdom": "^27.0.6", + "jest-environment-node": "^27.0.6", + "jest-haste-map": "^27.0.6", + "jest-leak-detector": "^27.0.6", + "jest-message-util": "^27.0.6", + "jest-resolve": "^27.0.6", + "jest-runtime": "^27.0.6", + "jest-util": "^27.0.6", + "jest-worker": "^27.0.6", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.0.6.tgz", + "integrity": "sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q==", + "dev": true, + "dependencies": { + "@jest/console": "^27.0.6", + "@jest/environment": "^27.0.6", + "@jest/fake-timers": "^27.0.6", + "@jest/globals": "^27.0.6", + "@jest/source-map": "^27.0.6", + "@jest/test-result": "^27.0.6", + "@jest/transform": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.0.6", + "jest-message-util": "^27.0.6", + "jest-mock": "^27.0.6", + "jest-regex-util": "^27.0.6", + "jest-resolve": "^27.0.6", + "jest-snapshot": "^27.0.6", + "jest-util": "^27.0.6", + "jest-validate": "^27.0.6", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^16.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-serializer": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.0.6.tgz", + "integrity": "sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.0.6.tgz", + "integrity": "sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/parser": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.0.6", + "graceful-fs": "^4.2.4", + "jest-diff": "^27.0.6", + "jest-get-type": "^27.0.6", + "jest-haste-map": "^27.0.6", + "jest-matcher-utils": "^27.0.6", + "jest-message-util": "^27.0.6", + "jest-resolve": "^27.0.6", + "jest-util": "^27.0.6", + "natural-compare": "^1.4.0", + "pretty-format": "^27.0.6", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.0.6.tgz", + "integrity": "sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^3.0.0", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.0.6.tgz", + "integrity": "sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.0.6", + "leven": "^3.1.0", + "pretty-format": "^27.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.0.6.tgz", + "integrity": "sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.0.6", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.6.tgz", + "integrity": "sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest/node_modules/jest-cli": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.0.6.tgz", + "integrity": "sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg==", + "dev": true, + "dependencies": { + "@jest/core": "^27.0.6", + "@jest/test-result": "^27.0.6", + "@jest/types": "^27.0.6", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "jest-config": "^27.0.6", + "jest-util": "^27.0.6", + "jest-validate": "^27.0.6", + "prompts": "^2.0.1", + "yargs": "^16.0.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", + "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.5", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "dependencies": { + "tmpl": "1.0.x" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", + "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.32", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", + "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "dev": true, + "dependencies": { + "mime-db": "1.49.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node_modules/node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "dependencies": { + "node-modules-regexp": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pretty-format": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz", + "integrity": "sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==", + "dev": true, + "dependencies": { + "@jest/types": "^27.0.6", + "ansi-regex": "^5.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz", + "integrity": "sha512-LkmXi8UUNxnCC+JlH7/fsfsKr5AU110l+SYGJimWNkWhxbN5EyeOtm1MJ0hhvqMMOhGwBj1Fp70Yv9i+hX0QAg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "dependencies": { + "makeerror": "1.0.x" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + } + }, "dependencies": { "@babel/code-frame": { "version": "7.14.5", @@ -442,6 +5130,57 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -813,6 +5552,13 @@ } } }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", @@ -828,6 +5574,24 @@ "debug": "4" } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -871,6 +5635,12 @@ "sprintf-js": "~1.0.2" } }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1164,6 +5934,11 @@ } } }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=" + }, "data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -1226,6 +6001,15 @@ "integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==", "dev": true }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "domexception": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", @@ -1261,6 +6045,15 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1286,12 +6079,224 @@ "source-map": "~0.6.1" } }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "globals": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, "estraverse": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", @@ -1349,6 +6354,12 @@ } } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1370,6 +6381,15 @@ "bser": "2.1.1" } }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1389,6 +6409,22 @@ "path-exists": "^4.0.0" } }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, "form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -1419,6 +6455,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -1457,6 +6499,15 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -1535,6 +6586,30 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "import-local": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", @@ -1585,6 +6660,12 @@ "has": "^1.0.3" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1597,6 +6678,15 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1946,7 +7036,8 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true + "dev": true, + "requires": {} }, "jest-regex-util": { "version": "27.0.6", @@ -2229,6 +7320,18 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json5": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", @@ -2275,6 +7378,24 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2467,6 +7588,15 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -2527,6 +7657,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true + }, "pretty-format": { "version": "27.0.6", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz", @@ -2547,6 +7683,12 @@ } } }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "prompts": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", @@ -2575,12 +7717,24 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -2675,6 +7829,17 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2756,6 +7921,12 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2781,6 +7952,40 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -2802,6 +8007,12 @@ "minimatch": "^3.0.4" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "throat": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", @@ -2885,6 +8096,21 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "v8-to-istanbul": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz", @@ -3011,7 +8237,8 @@ "version": "7.5.3", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "dev": true + "dev": true, + "requires": {} }, "xml-name-validator": { "version": "3.0.0", diff --git a/packages/quantum-js-util/package.json b/packages/quantum-js-util/package.json index 55c7fe1..181cba9 100644 --- a/packages/quantum-js-util/package.json +++ b/packages/quantum-js-util/package.json @@ -2,10 +2,11 @@ "name": "quantum-js-util", "version": "1.0.0", "description": "", - "main": "Q.js", + "main": "index.js", "scripts": { "test": "jest", - "prettier": "echo 'I am a prettier util!' && exit 0" + "prettier": "npx prettier --write", + "lint": "npx eslint **/*.js" }, "devDependencies": { "cross-env": "^7.0.3", @@ -14,7 +15,9 @@ "jsdom": "^16.6.0", "prettier": "2.3.2" }, - "dependencies": {}, + "dependencies": { + "custom-event": "^1.0.1" + }, "keywords": [], "author": "", "license": "ISC" From 69071180035b2a851925bce3f1f6cb7b79da2ec8 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Sat, 14 Aug 2021 23:52:15 +0000 Subject: [PATCH 17/22] Add default Circuit size for braket() method if arguments invalid --- packages/quantum-js-vis/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/quantum-js-vis/index.js b/packages/quantum-js-vis/index.js index 6838703..e993585 100644 --- a/packages/quantum-js-vis/index.js +++ b/packages/quantum-js-vis/index.js @@ -20,7 +20,8 @@ braket = function(){ circuit = new Q( args[0] ) } else { - circuit = new Q( args[0], args[1] ) + if(args[0] <= 0 || args[1] <= 0) circuit = new Q(4, 8); + else circuit = new Q( args[0], args[1] ) } container = document.createElement( 'div' ) let paletteEl = Editor.createPalette(); From e6b88a629d2c24ad209835f00b21082235c88466 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Sat, 14 Aug 2021 23:54:35 +0000 Subject: [PATCH 18/22] Update bundle.js for braket() function change -- set default Circuit size --- build/bundle.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/bundle.js b/build/bundle.js index 07d46b7..3804631 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -8167,7 +8167,8 @@ braket = function(){ circuit = new Q( args[0] ) } else { - circuit = new Q( args[0], args[1] ) + if(args[0] <= 0 || args[1] <= 0) circuit = new Q(4, 8); + else circuit = new Q( args[0], args[1] ) } container = document.createElement( 'div' ) let paletteEl = Editor.createPalette(); From 54c46c50dfcba85a336ef84de2083ca4a5eabc12 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Tue, 17 Aug 2021 18:58:07 +0000 Subject: [PATCH 19/22] Remove s3_bucket argument when device is local_simulator --- build/bundle.js | 2 +- packages/quantum-js-util/Q-Circuit.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/bundle.js b/build/bundle.js index 3804631..9fdc2da 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -1964,7 +1964,7 @@ device = LocalSimulator()\n\n` variables += '\n' if( this.operations.length === 0 ) circuit += '.i(0)'// Quick fix to avoid an error here! - const footer = `\n\ntask = device.run(qjs_circuit, s3_folder, shots=100) + const footer = `\n\ntask = device.run(qjs_circuit, shots=100) print(task.result().measurement_counts)` return isValidBraketCircuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###` }, diff --git a/packages/quantum-js-util/Q-Circuit.js b/packages/quantum-js-util/Q-Circuit.js index 8d1c4b6..f3d574e 100644 --- a/packages/quantum-js-util/Q-Circuit.js +++ b/packages/quantum-js-util/Q-Circuit.js @@ -1210,7 +1210,7 @@ device = LocalSimulator()\n\n` variables += '\n' if( this.operations.length === 0 ) circuit += '.i(0)'// Quick fix to avoid an error here! - const footer = `\n\ntask = device.run(qjs_circuit, s3_folder, shots=100) + const footer = `\n\ntask = device.run(qjs_circuit, shots=100) print(task.result().measurement_counts)` return isValidBraketCircuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###` }, From bfe30fda80b65393087409484032323618817fa5 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 19 Aug 2021 19:09:22 +0000 Subject: [PATCH 20/22] Add line to import numpy in toAmazonBraket --- build/bundle.js | 1 + packages/quantum-js-util/Q-Circuit.js | 1 + 2 files changed, 2 insertions(+) diff --git a/build/bundle.js b/build/bundle.js index 9fdc2da..f56eb01 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -1875,6 +1875,7 @@ https://cirq.readthedocs.io/en/stable/tutorial.html const header = `import boto3 from braket.aws import AwsDevice from braket.circuits import Circuit +import numpy as np my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket my_prefix = "Your-Folder-Name" # the name of the folder in the bucket diff --git a/packages/quantum-js-util/Q-Circuit.js b/packages/quantum-js-util/Q-Circuit.js index f3d574e..0c4c41c 100644 --- a/packages/quantum-js-util/Q-Circuit.js +++ b/packages/quantum-js-util/Q-Circuit.js @@ -1121,6 +1121,7 @@ https://cirq.readthedocs.io/en/stable/tutorial.html const header = `import boto3 from braket.aws import AwsDevice from braket.circuits import Circuit +import numpy as np my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket my_prefix = "Your-Folder-Name" # the name of the folder in the bucket From e8926c7e2d295bf7a07fd7a0b0f8e3fb4c4cb871 Mon Sep 17 00:00:00 2001 From: AjinkyaGawali <79669399+AjinkyaGawali@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:19:47 -0800 Subject: [PATCH 21/22] Added sv1 device arn usage in placeholder text --- build/bundle.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/bundle.js b/build/bundle.js index f56eb01..bce7980 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -1872,14 +1872,14 @@ https://cirq.readthedocs.io/en/stable/tutorial.html }, toAmazonBraket: function(){ let isValidBraketCircuit = true - const header = `import boto3 -from braket.aws import AwsDevice -from braket.circuits import Circuit + const header = `from braket.circuits import Circuit +from braket.devices import LocalSimulator import numpy as np -my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket -my_prefix = "Your-Folder-Name" # the name of the folder in the bucket -s3_folder = (my_bucket, my_prefix)\n +'''Uncomment following 2 lines to set up and use the on-demand simulator SV1 instead of LocalSimualtor''' +#from braket.aws import AwsDevice +#device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1") + device = LocalSimulator()\n\n` //TODO (ltnln): Syntax is different for simulators and actual quantum computers. Should there be a default? Should there be a way to change? //vs an actual quantum computer? May not be necessary. From 958bbe3cb1c121f24f3cf5f4892286111dd803fd Mon Sep 17 00:00:00 2001 From: AjinkyaGawali <79669399+AjinkyaGawali@users.noreply.github.com> Date: Sun, 2 Apr 2023 20:25:10 -0700 Subject: [PATCH 22/22] Remove unsafe 'innerhtml' usage --- build/bundle.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/bundle.js b/build/bundle.js index bce7980..73b94ff 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -6209,12 +6209,12 @@ Editor = function( circuit, targetEl ){ // How can we interact with this circuit // through code? (How cool is this?!) - const referenceEl = document.createElement( 'p' ) - circuitEl.appendChild( referenceEl ) - referenceEl.innerHTML = ` - This circuit is accessible in your - JavaScript console - as document.getElementById('${ this.domId }').circuit` + // const referenceEl = document.createElement( 'p' ) + // circuitEl.appendChild( referenceEl ) + // referenceEl.innerHTML = ` + // This circuit is accessible in your + // JavaScript console + // as document.getElementById('${ this.domId }').circuit` //document.getElementById('Q-Editor-0').circuit //$('#${ this.domId }')