From 2c44aa0cf54945db19c5e09a4fdec469e7917029 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 14 Jul 2021 17:07:48 +0000 Subject: [PATCH 01/41] Initial update, Q-Circuit.js --- Q/Q-Circuit.js | 149 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 50 deletions(-) diff --git a/Q/Q-Circuit.js b/Q/Q-Circuit.js index 55820bb..1196765 100644 --- a/Q/Q-Circuit.js +++ b/Q/Q-Circuit.js @@ -6,7 +6,6 @@ Q.Circuit = function( bandwidth, timewidth ){ - // What number Circuit is this // that we’re attempting to make here? @@ -60,7 +59,6 @@ Q.Circuit = function( bandwidth, timewidth ){ Object.assign( Q.Circuit, { - index: 0, help: function(){ return Q.help( this )}, constants: {}, @@ -76,7 +74,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. @@ -196,7 +193,6 @@ Object.assign( Q.Circuit, { fromTableTransposed: function( table ){ - const bandwidth = table.length, timewidth = table.reduce( function( max, moments ){ @@ -216,7 +212,6 @@ Object.assign( Q.Circuit, { const momentIndex = m + 1, operation = table[ r ][ m ] - let siblingHasBeenFound = false for( let s = 0; s < r; s ++ ){ @@ -229,7 +224,6 @@ Object.assign( Q.Circuit, { // We’ve found a sibling ! - const operationsIndex = circuit.operations.findIndex( function( operation ){ return ( @@ -377,7 +371,6 @@ Object.assign( Q.Circuit, { if( bitsEqual ){ // console.log( 'bits ARE equal' ) - let istar = 0, jstar = 0, @@ -389,8 +382,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()) @@ -505,11 +496,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() ) + } } @@ -519,8 +514,6 @@ Object.assign( Q.Circuit, { // and wow -- tracking down that bug was painful! const registerIndices = operation.registerIndices.slice() - - state = Q.Circuit.expandMatrix( circuit.bandwidth, @@ -531,7 +524,6 @@ Object.assign( Q.Circuit, { - operationsCompleted ++ const progress = operationsCompleted / operationsTotal @@ -597,7 +589,6 @@ Object.assign( Q.Circuit, { - return matrix } @@ -1114,46 +1105,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 + } - if( operation.gate.symbol === 'X' && - operation.registerIndices.length > 1 ){ + 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 ){ - awsGate = 'cnot' + return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 ) + + }, '' ) + ')' + } + 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(){ @@ -1360,15 +1407,14 @@ 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? @@ -1439,7 +1485,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, @@ -1447,6 +1497,8 @@ print(task.result().measurement_counts)` registerIndices, isControlled } + //perform parameter update here!!! + if(gate.has_parameters) gate.updateMatrix$.apply( gate, Object.values(parameters) ) this.operations.push( operation ) @@ -1459,14 +1511,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: [{ @@ -1873,7 +1926,6 @@ print(task.result().measurement_counts)` // 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 @@ -2008,11 +2060,11 @@ if( bellsAreEqual ){ Q.Circuit.createConstants( - 'BELL', Q` + 'BELL', new 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 @@ -2031,6 +2083,3 @@ Q.Circuit.createConstants( ) - - - From 7b47d038e8c0489e31cdf780aa0ca4a77c0a6264 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 14 Jul 2021 17:12:40 +0000 Subject: [PATCH 02/41] Full update --- Q/Q-Circuit-Editor.css | 49 +- Q/Q-Circuit-Editor.js | 267 +- Q/Q-Gate.js | 763 +++- build/q-old.css | 1376 +++++++ build/q-old.js | 7874 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 10244 insertions(+), 85 deletions(-) create mode 100644 build/q-old.css create mode 100644 build/q-old.js diff --git a/Q/Q-Circuit-Editor.css b/Q/Q-Circuit-Editor.css index af97203..d4c5381 100644 --- a/Q/Q-Circuit-Editor.css +++ b/Q/Q-Circuit-Editor.css @@ -94,7 +94,6 @@ - .Q-circuit, .Q-circuit-palette { @@ -123,8 +122,8 @@ 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; } @@ -275,6 +274,19 @@ 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 { @@ -364,8 +376,16 @@ ); } +.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 { @@ -541,7 +561,6 @@ /* */ /******************/ - .Q-circuit-operation { position: relative; @@ -607,7 +626,7 @@ 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; @@ -719,7 +738,25 @@ 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 ); +} @@ -811,6 +848,8 @@ + + /***************/ /* */ /* Buttons */ diff --git a/Q/Q-Circuit-Editor.js b/Q/Q-Circuit-Editor.js index 4b8549a..f12b243 100644 --- a/Q/Q-Circuit-Editor.js +++ b/Q/Q-Circuit-Editor.js @@ -1,9 +1,6 @@ - // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - Q.Circuit.Editor = function( circuit, targetEl ){ @@ -184,7 +181,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 ) }) @@ -196,7 +192,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. @@ -266,7 +264,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){ } - // Add “Add register” button. + // Add “Add register” button.q const addRegisterEl = createDiv() foregroundEl.appendChild( addRegisterEl ) @@ -323,7 +321,6 @@ Q.Circuit.Editor = function( circuit, targetEl ){ // Add operations. circuit.operations.forEach( function( operation ){ - Q.Circuit.Editor.set( circuitEl, operation ) }) @@ -429,7 +426,6 @@ Object.assign( Q.Circuit.Editor, { y: event.changedTouches[ 0 ][ pageOrClient +'Y' ] } return { - x: event[ pageOrClient +'X' ], y: event[ pageOrClient +'Y' ] } @@ -445,11 +441,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 ) @@ -542,7 +538,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' ), @@ -550,7 +545,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 ) @@ -564,7 +558,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' ) @@ -613,7 +609,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 = '' @@ -718,12 +713,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 ] ++ @@ -753,7 +746,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. @@ -837,7 +829,6 @@ Q.Circuit.Editor.createControl = function( circuitEl ){ ) }) circuit.set$( - targets[ 0 ].getAttribute( 'gate-symbol' ), +control.getAttribute( 'moment-index' ), [ +control.getAttribute( 'register-index' )].concat( @@ -872,7 +863,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 @@ -1071,6 +1061,7 @@ 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 ){ @@ -1170,9 +1161,6 @@ Q.Circuit.Editor.onPointerMove = function( event ){ - - - /////////////////////// // // // Pointer PRESS // @@ -1181,8 +1169,7 @@ Q.Circuit.Editor.onPointerMove = function( event ){ Q.Circuit.Editor.onPointerPress = function( event ){ - - + console.log( event ); // This is just a safety net // in case something terrible has ocurred. // (ex. Did the user click and then their mouse ran @@ -1193,13 +1180,11 @@ Q.Circuit.Editor.onPointerPress = function( event ){ 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 @@ -1207,7 +1192,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 @@ -1222,9 +1206,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 @@ -1329,7 +1312,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. @@ -1358,7 +1340,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! @@ -1392,7 +1373,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){ if( operationsSelectedLength === operations.length ){ operations.forEach( function( el ){ - el.classList.remove( 'Q-circuit-cell-selected' ) }) } @@ -1437,14 +1417,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 @@ -1550,7 +1537,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){ dragEl.registerIndex = registerIndex } else if( paletteEl ){ - const operationEl = targetEl.closest( '.Q-circuit-operation' ) if( !operationEl ) return @@ -1564,6 +1550,30 @@ 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 ] + }) + //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() @@ -1592,13 +1602,9 @@ 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() @@ -1610,13 +1616,17 @@ 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' ) - }), + 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(){ @@ -1761,7 +1771,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ droppedAtRegisterIndex < 1 || droppedAtRegisterIndex > circuit.bandwidth ){ - returnToOrigin() return } @@ -1812,7 +1821,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.) @@ -1896,6 +1904,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([ @@ -1916,7 +1931,8 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex + siblingDelta } return registerIndexTarget - }) + }), + parameters // ) ]) } @@ -1937,7 +1953,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 ){ @@ -1965,7 +1980,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 @@ -1975,7 +1989,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 @@ -2000,34 +2014,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 // ) ]) }) @@ -2037,15 +2064,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 // ) ]) }) @@ -2079,7 +2113,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. @@ -2101,11 +2135,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 // ) ]) } @@ -2149,7 +2196,76 @@ Q.Circuit.Editor.onPointerRelease = function( event ){ } + ///////////////////////// + // // + // Pointer DOUBLECLICK // + // // +///////////////////////// +//ltnln: my trying out an idea for parameterized gates... +Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) { + 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' )) + console.log("param") + //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 { + parameterValue = math.evaluate(textbox.value.toLowerCase()) + } + catch( err ) { + console.log(err) + parameterValue = oldValue + } + 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' +} @@ -2167,10 +2283,3 @@ 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 ) - - - - - - - diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index a1352a6..3d6a888 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -7,7 +7,768 @@ Q.Gate = function( params ){ Object.assign( this, params ) - this.index = Q.Gate.index ++ + this.in + // 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 + }) + + 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( (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. + + + */ + ) + + + dex = Q.Gate.index ++ if( typeof this.symbol !== 'string' ) this.symbol = '?' if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase() diff --git a/build/q-old.css b/build/q-old.css new file mode 100644 index 0000000..e4a6620 --- /dev/null +++ b/build/q-old.css @@ -0,0 +1,1376 @@ +/* + + 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 new file mode 100644 index 0000000..ddcf90e --- /dev/null +++ b/build/q-old.js @@ -0,0 +1,7874 @@ + +// 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 From 3288f31c290cac643ad9121379c0a682393d3ce5 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 14 Jul 2021 21:05:54 +0000 Subject: [PATCH 03/41] Revert Q-Gate.js --- Q/Q-Gate.js | 763 +--------------------------------------------------- 1 file changed, 1 insertion(+), 762 deletions(-) diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index 3d6a888..a1352a6 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -7,768 +7,7 @@ Q.Gate = function( params ){ Object.assign( this, params ) - this.in - // 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 - }) - - 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( (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. - - - */ - ) - - - dex = Q.Gate.index ++ + this.index = Q.Gate.index ++ if( typeof this.symbol !== 'string' ) this.symbol = '?' if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase() From d18a384bb28f80685df7ccee469b2f3662d1d210 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 14 Jul 2021 21:11:06 +0000 Subject: [PATCH 04/41] Update Q-Gate.js --- Q/Q-Gate.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index 3d6a888..1e939eb 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -1,18 +1,6 @@ - // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - -Q.Gate = function( params ){ - - Object.assign( this, params ) - this.in - // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - Q.Gate = function( params ){ Object.assign( this, params ) From 19c4b2eedb70140d3f4428262ee824b1203e53cd Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 14 Jul 2021 21:13:40 +0000 Subject: [PATCH 05/41] Revert Q-Gate.js --- Q/Q-Gate.js | 764 +--------------------------------------------------- 1 file changed, 7 insertions(+), 757 deletions(-) diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index 1e939eb..41d3337 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -1,762 +1,13 @@ + // 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 - }) - - 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( (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. - - - */ - ) - - - dex = Q.Gate.index ++ + + +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() @@ -1046,4 +297,3 @@ Q.Gate.createConstants( ) - From f8e245dd267094380bf8b8de021e68f80de14816 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Mon, 19 Jul 2021 00:01:31 +0000 Subject: [PATCH 06/41] Q-Gate.js update --- Q/Q-Gate.js | 519 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 490 insertions(+), 29 deletions(-) diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index a1352a6..da09f16 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -11,7 +11,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 @@ -66,9 +67,8 @@ Q.Gate = function( params ){ - Object.assign( Q.Gate, { - + index: 0, constants: {}, createConstant: Q.createConstant, @@ -127,7 +127,7 @@ Object.assign( Q.Gate.prototype, { -Q.Gate.createConstants( +Q.Gate.createConstants ( // Operate on a single qubit. @@ -180,8 +180,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', @@ -191,8 +196,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', @@ -202,32 +209,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({ @@ -252,22 +262,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 ){ - // Operate on 2 qubits. + 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( (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({ @@ -280,8 +492,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] + ) + }) /* @@ -294,7 +756,6 @@ Q.Gate.createConstants( */ -) - +) From 263a12474e9c06a4adfde408e3d64415b817118a Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Mon, 19 Jul 2021 00:02:48 +0000 Subject: [PATCH 07/41] Update Q-Gate.js --- Q/Q-Gate.js | 518 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 490 insertions(+), 28 deletions(-) diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index 41d3337..da09f16 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -11,7 +11,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 @@ -66,9 +67,8 @@ Q.Gate = function( params ){ - Object.assign( Q.Gate, { - + index: 0, constants: {}, createConstant: Q.createConstant, @@ -127,7 +127,7 @@ Object.assign( Q.Gate.prototype, { -Q.Gate.createConstants( +Q.Gate.createConstants ( // Operate on a single qubit. @@ -180,8 +180,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', @@ -191,8 +196,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', @@ -202,32 +209,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({ @@ -252,22 +262,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 ){ - // Operate on 2 qubits. + 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( (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({ @@ -280,8 +492,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] + ) + }) /* @@ -294,6 +756,6 @@ Q.Gate.createConstants( */ -) +) From 884344f7a0b608f491116689eb8f3d0d1052b720 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Mon, 19 Jul 2021 00:35:12 +0000 Subject: [PATCH 08/41] Responses to merge-request comments --- Q/Q-Circuit-Editor.js | 1 - Q/Q-Circuit.js | 28 ++++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Q/Q-Circuit-Editor.js b/Q/Q-Circuit-Editor.js index f12b243..f1e2172 100644 --- a/Q/Q-Circuit-Editor.js +++ b/Q/Q-Circuit-Editor.js @@ -1169,7 +1169,6 @@ Q.Circuit.Editor.onPointerMove = function( event ){ Q.Circuit.Editor.onPointerPress = function( event ){ - console.log( event ); // This is just a safety net // in case something terrible has ocurred. // (ex. Did the user click and then their mouse ran diff --git a/Q/Q-Circuit.js b/Q/Q-Circuit.js index 1196765..ba3c156 100644 --- a/Q/Q-Circuit.js +++ b/Q/Q-Circuit.js @@ -3,7 +3,6 @@ - Q.Circuit = function( bandwidth, timewidth ){ // What number Circuit is this @@ -53,6 +52,8 @@ Q.Circuit = function( bandwidth, timewidth ){ // Undo / Redo history. this.history = new Q.History( this ) + + } @@ -293,7 +294,12 @@ Object.assign( Q.Circuit, { } return result }, - + + isControlledOperation: function( operation ) { + return (!operation.gate.is_multi_qubit) //assumption: we won't allow controlled multi-qubit operations + && (operation.registerIndices.length >= 2) + && (operation.gate.can_be_controlled) + }, // Return transformation over entire nqubit register that applies U to @@ -404,8 +410,6 @@ Object.assign( Q.Circuit, { }, - - evaluate: function( circuit ){ @@ -499,7 +503,7 @@ Object.assign( Q.Circuit, { // 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) { + if( Q.Circuit.isControlledOperation(operation) ) { for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){ U = Q.Circuit.controlled( U ) @@ -1105,7 +1109,7 @@ https://cirq.readthedocs.io/en/stable/tutorial.html return headers }, toAmazonBraket: function(){ - let is_valid_braket_circuit = true + let isValidBraketCircuit = true const header = `import boto3 from braket.aws import AwsDevice from braket.circuits import Circuit @@ -1124,12 +1128,12 @@ device = LocalSimulator()\n\n` 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.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 is_valid_braket_circuit = false + else isValidBraketCircuit = false } else if( operation.gate.symbol === 'S' ) { @@ -1149,13 +1153,13 @@ device = LocalSimulator()\n\n` 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 + 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 - is_valid_braket_circuit &= operation.registerIndices.length === 1 + isValidBraketCircuit &= 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), @@ -1182,7 +1186,7 @@ device = LocalSimulator()\n\n` } // 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 ) + 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 ){ @@ -1200,7 +1204,7 @@ device = LocalSimulator()\n\n` 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!###` + return isValidBraketCircuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###` }, toLatex: function(){ From ef2e91900f2e608f373225edd175ad556c92e3a2 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Mon, 19 Jul 2021 00:42:26 +0000 Subject: [PATCH 09/41] Responding to comments on merge request --- Q/Q-Circuit.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Q/Q-Circuit.js b/Q/Q-Circuit.js index ba3c156..8bdc51d 100644 --- a/Q/Q-Circuit.js +++ b/Q/Q-Circuit.js @@ -295,6 +295,7 @@ Object.assign( Q.Circuit, { 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) //assumption: we won't allow controlled multi-qubit operations && (operation.registerIndices.length >= 2) @@ -1125,9 +1126,6 @@ device = LocalSimulator()\n\n` //`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 ) isValidBraketCircuit = false if( operation.gate.symbol === 'X' ) { if( operation.registerIndices.length === 1 ) awsGate = operation.gate.symbolAmazonBraket @@ -1150,7 +1148,7 @@ device = LocalSimulator()\n\n` } //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' ) { + 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 @@ -1162,6 +1160,7 @@ device = LocalSimulator()\n\n` 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 = 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') From 465e08e212e80f1d9ebcecf41d703c9bbcc97a8c Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Tue, 20 Jul 2021 18:21:51 +0000 Subject: [PATCH 10/41] Reverse bit order of measurements in relation to registers --- Q/Q-Circuit.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Q/Q-Circuit.js b/Q/Q-Circuit.js index 8bdc51d..5c045e1 100644 --- a/Q/Q-Circuit.js +++ b/Q/Q-Circuit.js @@ -286,7 +286,6 @@ Object.assign( Q.Circuit, { 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 ) @@ -297,7 +296,8 @@ Object.assign( Q.Circuit, { //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) //assumption: we won't allow controlled multi-qubit operations + 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) }, @@ -310,7 +310,6 @@ Object.assign( Q.Circuit, { // 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()) @@ -329,7 +328,7 @@ Object.assign( Q.Circuit, { for( let i = 0; i < qubitIndices.length; i ++ ){ //qubitIndices[ i ] = ( circuitBandwidth - 1 ) - qubitIndices[ i ] - qubitIndices[ i ] = ( circuitBandwidth - 0 ) - qubitIndices[ i ] + qubitIndices[ i ] -= 1 } // console.log( 'qubits AFTER manipulation', qubitIndices ) @@ -471,7 +470,6 @@ Object.assign( Q.Circuit, { let matrix = circuit.operations.reduce( function( state, operation, i ){ - let U if( operation.registerIndices.length < Infinity ){ @@ -503,12 +501,13 @@ Object.assign( Q.Circuit, { // 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. + // This is a nasty fix, leads to a lot of edge cases. But just experimenting. if( Q.Circuit.isControlledOperation(operation) ) { - for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){ + const scale = operation.registerIndices.length - ( operation.gate.is_multi_qubit ? 2 : 1) + for( let j = 0; j < scale; j ++ ){ U = Q.Circuit.controlled( U ) - //console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() ) + // console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() ) } } @@ -526,7 +525,7 @@ Object.assign( Q.Circuit, { registerIndices ).multiply( state ) - + operationsCompleted ++ @@ -560,7 +559,6 @@ Object.assign( Q.Circuit, { }, state ) - // console.log( 'result matrix', matrix.toTsv() ) @@ -1126,6 +1124,8 @@ device = LocalSimulator()\n\n` //`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 From 150eab1b18daa6581844d96739138a4e83d2d466 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 21 Jul 2021 00:27:25 +0000 Subject: [PATCH 11/41] Add tests for adding parameters to gate operations, 1-by-1 circuit --- build/__test__/Q.test.js | 319 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 build/__test__/Q.test.js diff --git a/build/__test__/Q.test.js b/build/__test__/Q.test.js new file mode 100644 index 0000000..3ae28dd --- /dev/null +++ b/build/__test__/Q.test.js @@ -0,0 +1,319 @@ +/** + * @jest-environment jsdom + */ + + +//TODO: ask for the breadth of these tests; ex. should I be testing functions on every single gate operation? + +var Q = require('../q-old'); + +/* + * Q-Gate.js; File contains all valid gate operations in the Q library. + */ + +/* + * Testing basic getters (.findBySymbol(), .findByName()) for basic correctness. + */ +beforeEach(() => { + Q.Matrix.index = 0 +}); + +test(`Q.Gate.findBySymbol(), with symbol 'I'. Should return Identity gate object.`, () => { + expect(Q.Gate.findBySymbol( 'I' )).toEqual({ + index: 0, + symbol: 'I', + symbolAmazonBraket: 'i', + symbolSvg: '', + name: 'Identity', + nameCss: 'identity', + matrix: Q.Matrix.IDENTITY_2X2, + parameters: {}, + applyToQubit: expect.any(Function) + }) +}) + +test(`Q.Gate.findByName(), with name 'Identity'. Should return Identity gate object.`, () => { + expect(Q.Gate.findByName( 'Identity' )).toEqual({ + index: 0, + symbol: 'I', + symbolAmazonBraket: 'i', + symbolSvg: '', + name: 'Identity', + nameCss: 'identity', + matrix: Q.Matrix.IDENTITY_2X2, + parameters: {}, + applyToQubit: expect.any(Function) + }) +}) + +/* + * Testing basic getters (.findBySymbol(), .findByName())for invalid gate requests + * + */ + +test(`Q.Gate.findBySymbol(), with symbol 'n/a', which should return undefined`, () => { + expect(Q.Gate.findBySymbol( 'n/a' )).toBeUndefined() +}) + +test(`Q.Gate.findByName(), with name 'n/a +', which should return undefined`, () => { + expect(Q.Gate.findByName( 'n/a' )).toBeUndefined() +}) + +/* + * Test that Q.Gate.prototype.clone() returns a deep copy of a gate operation and not a reference. + */ + +test(`Check that Q.Gate.prototype.clone() returns a deep copy of the passed in gate`, () => { + let new_gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( 'U' )) + expect( new_gate ).not.toBe( Q.Gate.findBySymbol( 'U' )) + expect( Q.Gate.findBySymbol( 'U' ) ).toBe( Q.Gate.findBySymbol( 'U' )) +}) + + +/* + * The following series of functions will test the updateMatrix$ function for a few of the + * gate operations included in Q. Not all gate operations need to recalculate their matrix. + * But all parameterized qubits calculate their matrix in the same way. + * + * Traditional edge cases (divide by 0, restricted input domain) are non-existent as + * none of the [currently] includes gates' matrices have restricted domains for inputs. + * + * Checking CSV strings as values should be the same; deep copies will never match due to Q.Matrix.index attribute. + */ + +test(`updateMatrix$ for the phase gate with parameter (phi = -π)`, () => { + const gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( 'P' )); + const expected_result = Q.Matrix.toCsv( new Q.Matrix( + [1, 0], + [0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -Math.PI ))] + )); + gate.updateMatrix$(-Math.PI); + expect( Q.Matrix.toCsv( gate.matrix )).toBe( expected_result ); +}) + +//This test surprisingly&indirectly helped uncover a problem calculating live probability results :D +test(`updateMatrix$ for the unitary gate with parameters (phi = -π,` + + ` theta = π, lambda = -π)`, () => { + const gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( 'U' )); + + //creating expected matrix converted to a Csv string for comparison + const phi = -Math.PI; + const theta = Math.PI; + const lambda = -Math.PI; + 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 expected_result = Q.Matrix.toCsv( new Q.Matrix( + [a, b], + [c, d] + )) + + //updating the current gate with the values phi, theta, lambda + gate.updateMatrix$( phi, theta, lambda ) + expect( Q.Matrix.toCsv( gate.matrix )).toBe( expected_result ); +}); + +/* + * The following methods test simply the creation of the parameter input div when a valid gate operation + * in the circuit is doubleclicked. + * + */ +('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(( element ) => { + let operation = Q.Gate.prototype.clone( Q.Gate.findBySymbol( element ) ); + let test_message = "Check that gate operation with symbol [" + element + "] exists and is found by Q.Gate.findBySymbol()"; + test(test_message, () => { + expect(operation).toBeDefined(); + }) + let description = "Checking circuit set for operation [" + operation.name + "] is valid" + describe(description, () => { + document.body.innerHTML = + '
' + + '
' + + '
'; + let container = document.getElementById( 'container' ); + const circuit = new Q.Circuit(2, 2); + const parameters = {}; + Object.assign(parameters, operation.parameters); + const register_indices = operation.is_multi_qubit ? [1, 2] : [1]; + circuit.set$( operation.symbol, 1, register_indices, parameters ); + container.appendChild(circuit.toDom()); + const operationEl = (container.querySelector('.Q-circuit-operation' )); + test_message = "Check that the operation icon for operation [" + operation.name + "] exists" + test(test_message, () => { + expect(operationEl).toBeDefined(); + }) + + if( operation.is_multi_qubit ) { + test_message = "Check that multi-qubit gate operation [" + operation.name + "] takes up two register indices in the circuit"; + test(test_message, () => { + expect(operationEl.getAttribute( 'register-indices' ).split(',').length).toBe( 2 ); + }) + } + + if( operation.has_parameters ) { + const parameters = operation.parameters; + test_message = "Since operation [" + operation.name + "] 'has_parameters', operation.parameters should NOT be empty" + test(test_message, () => { + expect(parameters).toBeDefined(); + expect(Object.keys(parameters).length) + }) + + let clientRect = operationEl.getBoundingClientRect(); + let clientX = clientRect.left; + let clientY = clientRect.top; + const event = { + 'clientX' : clientX, + 'clientY' : clientY + } + Q.Circuit.Editor.onDoubleclick( event, operationEl ); + expect( document.querySelector( '.Q-parameters-box' ).style.display ).toBe('block'); + // Check that the correct number of textboxes were created. + // Setting the values 1 and 3 as the usual case is 1 parameter with the exception of the unitary gate. + // This can be generalized by using Object.keys(parameters).length but this is more exact for testing purposes. + const textboxes = document.querySelectorAll( '.Q-parameter-box-input' ); + test_message = "The number of parameter-input textboxes generated for operation [" + operation.name + "] on a double click must be " + + operation.symbol === 'U' ? '3' : '1'; + test(test_message, () => { + let expected_result = operation.symbol === 'U' ? 3 : 1; + expect( Object.keys( operation.parameters ).length ).toBe( expected_result ); + expect( Array.from( textboxes ).length ).toBe( expected_result ); + }) + } + }) +}) + +/* + * The following test checks various responses to the parameter textboxes to a change event. + * These tests can be generalized for input for any single-qubit operation. + * + */ + +describe("Checking the responses to adding different inputs to a parameter textbox for the PHASE operation", () => { + beforeEach(() => { + let operation = Q.Gate.findBySymbol( 'P' ); + document.body.innerHTML = + '
' + + '
' + + '
'; + let container = document.getElementById( 'container' ); + const circuit = new Q.Circuit(1, 1); + circuit.set$( operation.symbol, 1, [1], {'phi': 1} ); + container.appendChild(circuit.toDom()); + const operationEl = (container.querySelector('.Q-circuit-operation' )); + let clientRect = operationEl.getBoundingClientRect(); + let clientX = clientRect.left; + let clientY = clientRect.top; + const event = { + 'clientX' : clientX, + 'clientY' : clientY + } + Q.Circuit.Editor.onDoubleclick( event, operationEl ); + }) + test("Check that the textbox default value is equal to 1, the 'phi' value of the operation", () => { + const textbox = document.querySelector( '.Q-parameter-box-input' ); + expect( +( textbox.value ) ).toBe( 1 ); + }) + + test("Check that the textbox retains decimal/number ('1.234') values inputted after a change event", () => { + + const operationEl = (container.querySelector('.Q-circuit-operation' )); + const textbox = document.querySelector( '.Q-parameter-box-input' ); + textbox.value = '1.234'; + let event = new Event('change'); + textbox.dispatchEvent( event ); + expect( textbox.value ).toBe( '1.234' ); + expect( operationEl.getAttribute( 'phi' ) ).toBe( '1.234' ); + }) + + test("Check that the textbox removes invalid values ('4/2') inputted after a change event", () => { + const evaluate = require("mathjs"); + + const operationEl = (container.querySelector('.Q-circuit-operation' )); + const textbox = document.querySelector( '.Q-parameter-box-input' ); + textbox.value = '4/2'; + let event = new Event('change'); + textbox.dispatchEvent( event ); + expect( textbox.value ).toBe( '1' ); + expect( operationEl.getAttribute( 'phi' ) ).toBe( '1' ); + }) + + test("Check that the textbox removes invalid values ('1.23abcd') inputted after a change event", () => { + const operationEl = (container.querySelector('.Q-circuit-operation' )); + const textbox = document.querySelector( '.Q-parameter-box-input' ); + textbox.value = '1.23abcd'; + let event = new Event('change'); + textbox.dispatchEvent( event ); + expect( textbox.value ).toBe( '1' ); + expect( operationEl.getAttribute( 'phi' ) ).toBe( '1' ); + }) + + test("Check that the textbox removes invalid values ('1.23/0') inputted after a change event", () => { + const operationEl = (container.querySelector('.Q-circuit-operation' )); + const textbox = document.querySelector( '.Q-parameter-box-input' ); + textbox.value = '1.23/0'; + let event = new Event('change'); + textbox.dispatchEvent( event ); + expect( textbox.value ).toBe( '1' ); + expect( operationEl.getAttribute( 'phi' ) ).toBe( '1' ); + }) + +}) + +/* + * The following series of tests checks that the circuit and editor are updated upon exiting the parameter input box + * of a gate with a single parameter + * + */ + +describe("Exiting the parameter box after changing the parameter of a single-parameter gate (PHASE)", () => { + let operation = Q.Gate.findBySymbol( 'P' ); + document.body.innerHTML = + '
' + + '
' + + '
'; + let container = document.getElementById( 'container' ); + const circuit = new Q.Circuit(1, 1); + circuit.set$( operation.symbol, 1, [1], {'phi': 1} ); + container.appendChild(circuit.toDom()); + let operationEl = (container.querySelector('.Q-circuit-operation' )); + let clientRect = operationEl.getBoundingClientRect(); + let clientX = clientRect.left; + let clientY = clientRect.top; + let event = { + 'clientX' : clientX, + 'clientY' : clientY + } + // The following sequence is was proven valid by the previous tests. + Q.Circuit.Editor.onDoubleclick( event, operationEl ); + const textbox = document.querySelector( '.Q-parameter-box-input' ); + textbox.value = '1.234'; + textbox.dispatchEvent( new Event( 'change' ) ); + //Mimic an exit event + const exitButton = document.querySelector( '.Q-parameter-box-exit' ); + event = { + target: exitButton + } + Q.Circuit.Editor.onPointerPress( event ); + const parameterEl = document.querySelector( '.Q-parameters-box' ) + test("Check that pressing the exit button exits the parameter box", () => { + expect( parameterEl.style.display ).toBe('none'); + expect( parameterEl.innerHTML ).toBe( "" ); + }) + + //Checking that the circuit && editor were set with the updated values + test("Checking that PHASE circuit operation and editor tile were updated with the new 'phi' parameter", () => { + //checking for operationEl again as .Editor$ is destructive + operationEl = (container.querySelector('.Q-circuit-operation' )); + expect( operationEl.getAttribute( 'gate-symbol' ) ).toBe( 'P' ); + expect( operationEl.getAttribute( 'phi' )).toBe( '1.234' ); + expect( circuit.operations[0].gate.symbol ).toBe( 'P' ); + expect( circuit.operations[0].gate.parameters[ 'phi' ] ).toBe( 1.234 ); + }) +}) + From 36d5fa154fba2921de92312a8921b806e3a9324c Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 21 Jul 2021 01:47:18 +0000 Subject: [PATCH 12/41] Fixed textbox input error --- Q/Q-Circuit-Editor.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Q/Q-Circuit-Editor.js b/Q/Q-Circuit-Editor.js index f1e2172..93178f8 100644 --- a/Q/Q-Circuit-Editor.js +++ b/Q/Q-Circuit-Editor.js @@ -2242,16 +2242,9 @@ Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) { 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 parameterValue = +textbox.value; let oldValue = operationEl.getAttribute( element ) if( !oldValue ) oldValue = operation.parameters[ element ] - try { - parameterValue = math.evaluate(textbox.value.toLowerCase()) - } - catch( err ) { - console.log(err) - parameterValue = oldValue - } if( parameterValue === null || parameterValue === Infinity ) textbox.value = oldValue.toString() else { operationEl.setAttribute( element, parameterValue ) From 354d44ca9b48ebfb3a302284fd46a084418d8837 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 21 Jul 2021 16:24:52 +0000 Subject: [PATCH 13/41] Create short function that creates new DOM element, adds CSS element, and appends to parent --- Q/Q-Circuit-Editor.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Q/Q-Circuit-Editor.js b/Q/Q-Circuit-Editor.js index 93178f8..7247204 100644 --- a/Q/Q-Circuit-Editor.js +++ b/Q/Q-Circuit-Editor.js @@ -21,6 +21,8 @@ Q.Circuit.Editor = function( circuit, targetEl ){ return document.createElement( 'div' ) } + + @@ -430,6 +432,12 @@ Object.assign( Q.Circuit.Editor, { 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 ) @@ -2216,9 +2224,7 @@ Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) { }) 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' ) + const exit = Q.Circuit.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' )) @@ -2230,13 +2236,11 @@ Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) { 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' ) + + const label = Q.Circuit.Editor.createNewElement( "span", input_fields, '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' ) + + const textbox = Q.Circuit.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] ) From 79087021d66c55d49f731edddeca6767e8aec6a3 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 21 Jul 2021 22:46:00 +0000 Subject: [PATCH 14/41] Remove references to Q, Q-Gate --- Q/Q-Gate.js | 427 +++++++++++++++++++++++++++------------------------- 1 file changed, 221 insertions(+), 206 deletions(-) diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index da09f16..4c82e02 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -4,10 +4,10 @@ -Q.Gate = function( params ){ +Gate = function( params ){ Object.assign( this, params ) - this.index = Q.Gate.index ++ + this.index = Gate.index ++ if( typeof this.symbol !== 'string' ) this.symbol = '?' if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase() @@ -23,7 +23,7 @@ Q.Gate = function( params ){ const scope = this, foundConstant = Object - .values( Q.Gate.constants ) + .values( Gate.constants ) .find( function( gate ){ return gate.symbol === scope.symbol @@ -31,7 +31,7 @@ Q.Gate = function( params ){ 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 ) + 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' @@ -51,11 +51,11 @@ Q.Gate = function( params ){ if( typeof this.applyToQubit !== 'function' ){ - if( this.matrix instanceof Q.Matrix === true ){ + if( this.matrix instanceof Matrix === true ){ this.applyToQubit = function( qubit ){ - return new Q.Qubit( this.matrix.multiply( qubit )) + return new Qubit( this.matrix.multiply( qubit )) } } else { @@ -67,18 +67,33 @@ Q.Gate = function( params ){ -Object.assign( Q.Gate, { +Object.assign( Gate, { index: 0, constants: {}, - createConstant: Q.createConstant, - createConstants: Q.createConstants, + createConstant: function( key, value ){ + + this[ key ] = value + 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 ]) + } + }, findBy: function( key, value ){ return ( Object - .values( Q.Gate.constants ) + .values( Gate.constants ) .find( function( item ){ if( typeof value === 'string' && @@ -92,22 +107,22 @@ Object.assign( Q.Gate, { }, findBySymbol: function( symbol ){ - return Q.Gate.findBy( 'symbol', symbol ) + return Gate.findBy( 'symbol', symbol ) }, findByName: function( name ){ - return Q.Gate.findBy( 'name', name ) + return Gate.findBy( 'name', name ) } }) -Object.assign( Q.Gate.prototype, { +Object.assign( Gate.prototype, { clone: function( params ){ - return new Q.Gate( Object.assign( {}, this, params )) + return new Gate( Object.assign( {}, this, params )) }, applyToQubits: function(){ @@ -127,58 +142,58 @@ Object.assign( Q.Gate.prototype, { -Q.Gate.createConstants ( +Gate.createConstants ( // Operate on a single qubit. - 'IDENTITY', new Q.Gate({ + 'IDENTITY', new Gate({ symbol: 'I', symbolAmazonBraket: 'i', symbolSvg: '', name: 'Identity', nameCss: 'identity', - matrix: Q.Matrix.IDENTITY_2X2 + matrix: Matrix.IDENTITY_2X2 }), - 'CURSOR', new Q.Gate({ + 'CURSOR', new Gate({ symbol: '*', symbolAmazonBraket: 'i', symbolSvg: '', name: 'Identity', nameCss: 'identity', - matrix: Q.Matrix.IDENTITY_2X2 + matrix: Matrix.IDENTITY_2X2 }), - 'MEASURE', new Q.Gate({ + 'MEASURE', new Gate({ symbol: 'M', symbolAmazonBraket: 'm', symbolSvg: '', name: 'Measure', nameCss: 'measure', - matrix: Q.Matrix.IDENTITY_2X2, + matrix: Matrix.IDENTITY_2X2, applyToQubit: function( state ){} }), - 'HADAMARD', new Q.Gate({ + 'HADAMARD', new Gate({ symbol: 'H', symbolAmazonBraket: 'h', symbolSvg: '', name: 'Hadamard', nameCss: 'hadamard', - matrix: new Q.Matrix( + matrix: new Matrix( [ Math.SQRT1_2, Math.SQRT1_2 ], [ Math.SQRT1_2, -Math.SQRT1_2 ]) }), - 'PAULI_X', new Q.Gate({ + 'PAULI_X', new Gate({ symbol: 'X', symbolAmazonBraket: 'x', symbolSvg: '', name: 'Pauli X', nameCss: 'pauli-x', - matrix: new Q.Matrix( + matrix: new Matrix( [ 0, 1 ], [ 1, 0 ]), //ltnln: NOTE! can_be_controlled refers to whether or not the Braket SDK supports a controlled @@ -187,33 +202,33 @@ Q.Gate.createConstants ( can_be_controlled: true }, ), - 'PAULI_Y', new Q.Gate({ + 'PAULI_Y', new 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 ]), + matrix: new Matrix( + [ 0, new ComplexNumber( 0, -1 )], + [ new ComplexNumber( 0, 1 ), 0 ]), can_be_controlled: true }, ), - 'PAULI_Z', new Q.Gate({ + 'PAULI_Z', new Gate({ symbol: 'Z', symbolAmazonBraket: 'z', symbolSvg: '', name: 'Pauli Z', nameCss: 'pauli-z', - matrix: new Q.Matrix( + matrix: new Matrix( [ 1, 0 ], [ 0, -1 ]), can_be_controlled: true }, ), - 'PHASE', new Q.Gate({ + 'PHASE', new Gate({ symbol: 'P', symbolAmazonBraket: 'phaseshift',// ltnln edit: change from 'p' to 'phaseshift' @@ -222,35 +237,35 @@ Q.Gate.createConstants ( nameCss: 'phase', parameters: { "phi" : 1 }, updateMatrix$: function( phi ){ - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi; - this.matrix = new Q.Matrix( + if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi; + this.matrix = new Matrix( [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] ))]) + [ 0, ComplexNumber.E.power( new 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( + if( isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] + const matrix = new Matrix( [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi ))]) - return new Q.Qubit( matrix.multiply( qubit )) + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, phi ))]) + return new Qubit( matrix.multiply( qubit )) }, can_be_controlled: true, has_parameters: true }), - 'PI_8', new Q.Gate({ + 'PI_8', new Gate({ symbol: 'T', symbolAmazonBraket: 't',// !!! Double check this !!! symbolSvg: '', name: 'π ÷ 8', nameCss: 'pi8', - matrix: new Q.Matrix( + matrix: new Matrix( [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, Math.PI / 4 )) ]) + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, Math.PI / 4 )) ]) }), - 'BLOCH', new Q.Gate({ + 'BLOCH', new Gate({ symbol: 'B', //symbolAmazonBraket: Does not exist. @@ -262,7 +277,7 @@ Q.Gate.createConstants ( // Create Bloch sphere visualizer instance. } }), - 'RX', new Q.Gate({ + 'RX', new Gate({ symbol: 'Rx', symbolAmazonBraket: 'rx', @@ -272,23 +287,23 @@ Q.Gate.createConstants ( 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 )]) + if( 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 }, 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 )) + if( isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] + const matrix = new Matrix( + [ Math.cos( phi / 2 ), new ComplexNumber( 0, -Math.sin( phi / 2 )) ], + [ new ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 )]) + return new Qubit( matrix.multiply( qubit )) }, has_parameters: true }), - 'RY', new Q.Gate({ + 'RY', new Gate({ symbol: 'Ry', symbolAmazonBraket: 'ry', @@ -298,23 +313,23 @@ Q.Gate.createConstants ( parameters: { "phi" : Math.PI / 2 }, updateMatrix$: function( phi ){ - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( + if( 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 }, applyToQubit: function( qubit, phi ){ - if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( + if( isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] + const matrix = new Matrix( [ Math.cos( phi / 2 ), -Math.sin( phi / 2 ) ], [ Math.sin( phi / 2 ), Math.cos( phi / 2 )]) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, has_parameters: true }), - 'RZ', new Q.Gate({ + 'RZ', new Gate({ symbol: 'Rz', symbolAmazonBraket: 'rz', @@ -324,23 +339,23 @@ Q.Gate.createConstants ( 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 ))]) + if( 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 }, 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 )) + if( isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] + const matrix = new Matrix( + [ ComplexNumber.E.power( new ComplexNumber( 0, -phi / 2 )), 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, phi / 2 ))]) + return new Qubit( matrix.multiply( qubit )) }, has_parameters: true }), - 'UNITARY', new Q.Gate({ + 'UNITARY', new Gate({ symbol: 'U', symbolAmazonBraket: 'unitary', @@ -353,102 +368,102 @@ Q.Gate.createConstants ( "lambda" : Math.PI / 2 }, updateMatrix$: function( phi, theta, lambda ){ - if( (Q.isUsefulNumber( +phi ) === true) && (Q.isUsefulNumber( +theta ) === true) && (Q.isUsefulNumber( +lambda ) === true) ) { + if( (isUsefulNumber( +phi ) === true) && (isUsefulNumber( +theta ) === true) && (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( + 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 }, 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( + if( isUsefulNumber( phi ) === true ) phi = this.parameters[ "phi" ] + if( isUsefulNumber( theta ) === true ) theta = this.parameters[ "theta" ] + if( isUsefulNumber( lambda ) === true ) lambda = this.parameters[ "lambda" ] + const a = ComplexNumber.multiply( + ComplexNumber.E.power( new ComplexNumber( 0, -( phi + lambda ) / 2 )), Math.cos( theta / 2 )); + const b = ComplexNumber.multiply( + ComplexNumber.E.power( new ComplexNumber( 0, -( phi - lambda ) / 2 )), -Math.sin( theta / 2 )); + const c = ComplexNumber.multiply( + ComplexNumber.E.power( new ComplexNumber( 0, ( phi - lambda ) / 2 )), Math.sin( theta / 2 )); + const d = ComplexNumber.multiply( + ComplexNumber.E.power( new ComplexNumber( 0, ( phi + lambda ) / 2 )), Math.cos( theta / 2 )); + const matrix = new Matrix( [ a, b ], [ c, d ]) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, has_parameters: true }), - 'NOT1_2', new Q.Gate({ + 'NOT1_2', new 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 ]) + 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 Q.Gate({ + 'PI_8_Dagger', new Gate({ symbol: 'T†', symbolAmazonBraket: 'ti', symbolSvg: '', name: 'PI_8_Dagger', nameCss: 'pi8-dagger', - matrix: new Q.Matrix( + matrix: new Matrix( [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -Math.PI / 4 )) ]) + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, -Math.PI / 4 )) ]) }), - 'NOT1_2_Dagger', new Q.Gate({ + 'NOT1_2_Dagger', new 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 ]) + 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 Q.Gate({ + 'S', new Gate({ symbol: 'S*', //Gotta think of a better symbol name... symbolAmazonBraket: 's', symbolSvg: '', name: 'π ÷ 4', nameCss: 'pi4', - matrix: new Q.Matrix( + matrix: new Matrix( [ 1, 0 ], - [ 0, new Q.ComplexNumber( 0, 1 ) ]) + [ 0, new ComplexNumber( 0, 1 ) ]) }), - 'S_Dagger', new Q.Gate({ + 'S_Dagger', new Gate({ symbol: 'S†', symbolAmazonBraket: 'si', symbolSvg: '', name: 'π ÷ 4 Dagger', nameCss: 'pi4-dagger', - matrix: new Q.Matrix( + matrix: new Matrix( [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -1 )) ]) + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, -1 )) ]) }), // Operate on 2 qubits. - 'SWAP', new Q.Gate({ + 'SWAP', new Gate({ symbol: 'S', symbolAmazonBraket: 'swap', @@ -458,58 +473,58 @@ Q.Gate.createConstants ( parameters: { "phi" : 0.0 }, updateMatrix$: function( phi ) { - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( + if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new 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, 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 }, applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( + if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new 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, ComplexNumber.E.power( new ComplexNumber( 0, phi )), 0 ], + [ 0, new ComplexNumber( 0, 1 ), 0, 0 ], [ 0, 0, 0, 1 ] ) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, can_be_controlled: true, has_parameters: true, is_multi_qubit: true }), - 'SWAP1_2', new Q.Gate({ + 'SWAP1_2', new Gate({ symbol: '√S', //symbolAmazonBraket: !!! UNKNOWN !!! symbolSvg: '', name: '√Swap', nameCss: 'swap1-2', - matrix: new Q.Matrix( + matrix: new 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, 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 Q.Gate({ + 'ISWAP', new Gate({ symbol: 'iS', symbolAmazonBraket: 'iswap', symbolSvg: '', name: 'Imaginary Swap', nameCss: 'iswap', - matrix: new Q.Matrix( + matrix: new Matrix( [ 1, 0, 0, 0 ], - [ 0, 0, new Q.ComplexNumber( 0, 1 ), 0 ], - [ 0, new Q.ComplexNumber( 0, 1 ), 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 Q.Gate({ + 'ISING-XX', new Gate({ symbol: 'XX', symbolAmazonBraket: 'xx', @@ -519,28 +534,28 @@ Q.Gate.createConstants ( 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 ) ]) + if( 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 }, 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 ) ] + if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Matrix( + [ Math.cos( phi / 2 ), 0, 0, new ComplexNumber( 0, -Math.sin( phi / 2 )) ], + [ 0, Math.cos( phi / 2 ), new ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ], + [ 0, new ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], + [ new ComplexNumber( 0, -Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ] ) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, is_multi_qubit: true, has_parameters: true }), - 'ISING-XY', new Q.Gate({ + 'ISING-XY', new Gate({ symbol: 'XY', symbolAmazonBraket: 'xy', @@ -550,28 +565,28 @@ Q.Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( + if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new 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, 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 }, applyToQubit: function( qubit, phi ) { - if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Q.Matrix( + if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new 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, Math.cos( phi / 2 ), new ComplexNumber( 0, Math.sin( phi / 2 )), 0 ], + [ 0, new ComplexNumber( 0, Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], [ 0, 0, 0, 1 ] ) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, is_multi_qubit: true, has_parameters: true }), - 'ISING-YY', new Q.Gate({ + 'ISING-YY', new Gate({ symbol: 'YY', symbolAmazonBraket: 'yy', @@ -581,28 +596,28 @@ Q.Gate.createConstants ( 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 ) ]) + if( 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 }, 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 ) ] + if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Matrix( + [ Math.cos( phi / 2 ), 0, 0, new ComplexNumber( 0, -Math.sin( phi / 2 )) ], + [ 0, Math.cos( phi / 2 ), new ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ], + [ 0, new ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], + [ new ComplexNumber( 0, Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ] ) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, is_multi_qubit: true, has_parameters: true }), - 'ISING-ZZ', new Q.Gate({ + 'ISING-ZZ', new Gate({ symbol: 'ZZ', symbolAmazonBraket: 'zz', @@ -612,28 +627,28 @@ Q.Gate.createConstants ( 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 )) ]) + if( 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 }, 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 )) ] + if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Matrix( + [ ComplexNumber.E.power( new ComplexNumber( 0, phi / 2 )), 0, 0, 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, phi / 2 )), 0, 0 ], + [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, phi / 2 )), 0], + [ 0, 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, -phi / 2 )) ] ) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, is_multi_qubit: true, has_parameters: true }), - 'CPhase00', new Q.Gate({ + 'CPhase00', new Gate({ symbol: '00', //placeholder symbolAmazonBraket: 'cphaseshift00', @@ -643,28 +658,28 @@ Q.Gate.createConstants ( 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 ], + if( 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 }, 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 ], + if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Matrix( + [ ComplexNumber.E.power( new 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 )) + return new Qubit( matrix.multiply( qubit )) }, is_multi_qubit: true, has_parameters: true }), - 'CPhase01', new Q.Gate({ + 'CPhase01', new Gate({ symbol: '01', //placeholder symbolAmazonBraket: 'cphaseshift01', @@ -674,28 +689,28 @@ Q.Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( + if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new Matrix( [ 1, 0, 0, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ], + [ 0, ComplexNumber.E.power( new 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( + if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Matrix( [ 1, 0, 0, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0, 0 ], + [ 0, ComplexNumber.E.power( new ComplexNumber( 0, phi)), 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ] ) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, is_multi_qubit: true, has_parameters: true }), - 'CPhase10', new Q.Gate({ + 'CPhase10', new Gate({ symbol: '10', //placeholder symbolAmazonBraket: 'cphaseshift10', @@ -705,35 +720,35 @@ Q.Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi - this.matrix = new Q.Matrix( + if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + this.matrix = new 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, ComplexNumber.E.power( new 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( + if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] + const matrix = new Matrix( [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], - [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0 ], + [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, phi)), 0 ], [ 0, 0, 0, 1 ] ) - return new Q.Qubit( matrix.multiply( qubit )) + return new Qubit( matrix.multiply( qubit )) }, is_multi_qubit: true, has_parameters: true }), - 'CSWAP', new Q.Gate({ + 'CSWAP', new Gate({ symbol: 'CSWAP', symbolAmazonBraket: 'cswap', symbolSvg: '', name: 'Controlled Swap', nameCss: 'controlled-swap', - matrix: new Q.Matrix( + 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], From d17dfa10a2fa69bbf928bb983775c031d5a6c5ae Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 21 Jul 2021 22:46:33 +0000 Subject: [PATCH 15/41] Remove references to Q, Q-Matrix --- Q/Q-Matrix.js | 168 +++++++++++++++++++++++++------------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/Q/Q-Matrix.js b/Q/Q-Matrix.js index a4ebbf9..4e4ba8f 100644 --- a/Q/Q-Matrix.js +++ b/Q/Q-Matrix.js @@ -4,13 +4,13 @@ -Q.Matrix = function(){ +Matrix = function(){ // We’re keeping track of how many matrices are // actually being generated. Just curiosity. - this.index = Q.Matrix.index ++ + this.index = Matrix.index ++ let matrixWidth = null @@ -21,7 +21,7 @@ Q.Matrix = function(){ // with dimensions of those values. if( arguments.length == 1 && - Q.ComplexNumber.isNumberLike( arguments[ 0 ])){ + ComplexNumber.isNumberLike( arguments[ 0 ])){ matrixWidth = arguments[ 0 ] this.rows = new Array( matrixWidth ).fill( 0 ).map( function(){ @@ -30,8 +30,8 @@ Q.Matrix = function(){ }) } else if( arguments.length == 2 && - Q.ComplexNumber.isNumberLike( arguments[ 0 ]) && - Q.ComplexNumber.isNumberLike( arguments[ 1 ])){ + ComplexNumber.isNumberLike( arguments[ 0 ]) && + ComplexNumber.isNumberLike( arguments[ 1 ])){ matrixWidth = arguments[ 0 ] this.rows = new Array( arguments[ 1 ]).fill( 0 ).map( function(){ @@ -56,7 +56,7 @@ Q.Matrix = function(){ 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 ) + return error( `Matrix found upon initialization that matrix#${this.index} row lengths were not equal. You are going to have a bad time.`, this ) } @@ -82,10 +82,10 @@ Q.Matrix = function(){ if( typeof value === 'number' ){ // console.log('Created a complex number!') - matrix.rows[ y ][ x ] = new Q.ComplexNumber( value ) + matrix.rows[ y ][ x ] = new 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 ) + else if( value instanceof ComplexNumber === false ){ + return 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 ]) @@ -113,18 +113,18 @@ Q.Matrix = function(){ /////////////////////////// -Object.assign( Q.Matrix, { +Object.assign( Matrix, { index: 0, - help: function(){ return Q.help( this )}, + help: function(){ return help( this )}, constants: {},// Only holds references; an easy way to look up what constants exist. - createConstant: Q.createConstant, - createConstants: Q.createConstants, + createConstant: createConstant, + createConstants: createConstants, isMatrixLike: function( obj ){ - //return obj instanceof Q.Matrix || Q.Matrix.prototype.isPrototypeOf( obj ) + //return obj instanceof Matrix || Matrix.prototype.isPrototypeOf( obj ) return obj instanceof this || this.prototype.isPrototypeOf( obj ) }, isWithinRange: function( n, minimum, maximum ){ @@ -152,9 +152,9 @@ Object.assign( Q.Matrix, { }, 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 + 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 ){ @@ -183,19 +183,19 @@ Object.assign( Q.Matrix, { } data.push( row ) } - return new Q.Matrix( ...data ) + return new Matrix( ...data ) }, createZero: function( size ){ - return new Q.Matrix.createSquare( size ) + return new Matrix.createSquare( size ) }, createOne: function( size ){ - return new Q.Matrix.createSquare( size, function(){ return 1 }) + return new Matrix.createSquare( size, function(){ return 1 }) }, createIdentity: function( size ){ - return new Q.Matrix.createSquare( size, function( x, y ){ return x === y ? 1 : 0 }) + return new Matrix.createSquare( size, function( x, y ){ return x === y ? 1 : 0 }) }, @@ -206,15 +206,15 @@ Object.assign( Q.Matrix, { from: function( format ){ if( typeof format !== 'string' ) format = 'Array' - const f = Q.Matrix[ 'from'+ format ] + const f = Matrix[ 'from'+ format ] format = format.toLowerCase() if( typeof f !== 'function' ) - return Q.error( `Q.Matrix could not find an importer for “${format}” data.` ) + return error( `Matrix could not find an importer for “${format}” data.` ) return f }, fromArray: function( array ){ - return new Q.Matrix( ...array ) + return new Matrix( ...array ) }, fromXsv: function( input, rowSeparator, valueSeparator ){ @@ -245,19 +245,19 @@ Object.assign( Q.Matrix, { }) outputRows.push( outputRow ) }) - return new Q.Matrix( ...outputRows ) + return new Matrix( ...outputRows ) }, fromCsv: function( csv ){ - return Q.Matrix.fromXsv( csv.replace( /\r/g, '\n' ), '\n', ',' ) + return Matrix.fromXsv( csv.replace( /\r/g, '\n' ), '\n', ',' ) }, fromTsv: function( tsv ){ - return Q.Matrix.fromXsv( tsv, '\n', '\t' ) + return Matrix.fromXsv( tsv, '\n', '\t' ) }, fromHtml: function( html ){ - return Q.Matrix.fromXsv( + return Matrix.fromXsv( html .replace( /\r?\n|\r||/g, '' ) @@ -287,11 +287,11 @@ Object.assign( Q.Matrix, { }, toCsv: function( matrix ){ - return Q.Matrix.toXsv( matrix, '\n', ',' ) + return Matrix.toXsv( matrix, '\n', ',' ) }, toTsv: function( matrix ){ - return Q.Matrix.toXsv( matrix, '\n', '\t' ) + return Matrix.toXsv( matrix, '\n', '\t' ) }, @@ -301,15 +301,15 @@ Object.assign( Q.Matrix, { add: function( matrix0, matrix1 ){ - if( Q.Matrix.isMatrixLike( matrix0 ) !== true || - Q.Matrix.isMatrixLike( matrix1 ) !== true ){ + if( Matrix.isMatrixLike( matrix0 ) !== true || + Matrix.isMatrixLike( matrix1 ) !== true ){ - return Q.error( `Q.Matrix attempted to add something that was not a matrix.` ) + return error( `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}.`) + if( Matrix.haveEqualDimensions( matrix0, matrix1 ) !== true ) + return 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 Q.Matrix( ...matrix0.rows.reduce( function( resultMatrixRow, row, r ){ + return new Matrix( ...matrix0.rows.reduce( function( resultMatrixRow, row, r ){ resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue, c ){ @@ -324,15 +324,15 @@ Object.assign( Q.Matrix, { }, multiplyScalar: function( matrix, scalar ){ - if( Q.Matrix.isMatrixLike( matrix ) !== true ){ + if( Matrix.isMatrixLike( matrix ) !== true ){ - return Q.error( `Q.Matrix attempted to scale something that was not a matrix.` ) + return error( `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 error( `Matrix attempted to scale this matrix#${matrix.index} by an invalid scalar: ${scalar}.` ) } - return new Q.Matrix( ...matrix.rows.reduce( function( resultMatrixRow, row ){ + return new Matrix( ...matrix.rows.reduce( function( resultMatrixRow, row ){ resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue ){ @@ -360,14 +360,14 @@ Object.assign( Q.Matrix, { https://en.wikipedia.org/wiki/Matrix_multiplication ` - if( Q.Matrix.isMatrixLike( matrix0 ) !== true || - Q.Matrix.isMatrixLike( matrix1 ) !== true ){ + if( Matrix.isMatrixLike( matrix0 ) !== true || + Matrix.isMatrixLike( matrix1 ) !== true ){ - return Q.error( `Q.Matrix attempted to multiply something that was not a matrix.` ) + return error( `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.` ) + return 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 @@ -375,7 +375,7 @@ Object.assign( Q.Matrix, { const resultMatrixRow = [] matrix1.columns.forEach( function( matrix1Column ){// Each column of OTHER matrix - const sum = new Q.ComplexNumber() + const sum = new ComplexNumber() matrix1Column.forEach( function( matrix1CellValue, index ){// Work down the column of OTHER matrix sum.add$( matrix0Row[ index ].multiply( matrix1CellValue )) @@ -384,7 +384,7 @@ Object.assign( Q.Matrix, { }) resultMatrix.push( resultMatrixRow ) }) - //return new Q.Matrix( ...resultMatrix ) + //return new Matrix( ...resultMatrix ) return new this( ...resultMatrix ) }, multiplyTensor: function( matrix0, matrix1 ){ @@ -394,10 +394,10 @@ Object.assign( Q.Matrix, { https://en.wikipedia.org/wiki/Tensor_product ` - if( Q.Matrix.isMatrixLike( matrix0 ) !== true || - Q.Matrix.isMatrixLike( matrix1 ) !== true ){ + if( Matrix.isMatrixLike( matrix0 ) !== true || + Matrix.isMatrixLike( matrix1 ) !== true ){ - return Q.error( `Q.Matrix attempted to tensor something that was not a matrix.` ) + return error( `Matrix attempted to tensor something that was not a matrix.` ) } const @@ -424,7 +424,7 @@ Object.assign( Q.Matrix, { } resultMatrix.push( resultMatrixRow ) } - return new Q.Matrix( ...resultMatrix ) + return new Matrix( ...resultMatrix ) } }) @@ -440,15 +440,15 @@ Object.assign( Q.Matrix, { ////////////////////////////// -Object.assign( Q.Matrix.prototype, { +Object.assign( Matrix.prototype, { isValidRow: function( r ){ - return Q.Matrix.isWithinRange( r, 0, this.rows.length - 1 ) + return Matrix.isWithinRange( r, 0, this.rows.length - 1 ) }, isValidColumn: function( c ){ - return Q.Matrix.isWithinRange( c, 0, this.columns.length - 1 ) + return Matrix.isWithinRange( c, 0, this.columns.length - 1 ) }, isValidAddress: function( x, y ){ @@ -456,11 +456,11 @@ Object.assign( Q.Matrix.prototype, { }, getWidth: function(){ - return Q.Matrix.getWidth( this ) + return Matrix.getWidth( this ) }, getHeight: function(){ - return Q.Matrix.getHeight( this ) + return Matrix.getHeight( this ) }, @@ -479,15 +479,15 @@ Object.assign( Q.Matrix.prototype, { ` 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 ) + return error( `Matrix could not read from cell address (x=${x}, y=${y}) in matrix#${this.index}.`, this ) }, clone: function(){ - return new Q.Matrix( ...this.rows ) + return new Matrix( ...this.rows ) }, isEqualTo: function( otherMatrix ){ - return Q.Matrix.areEqual( this, otherMatrix ) + return Matrix.areEqual( this, otherMatrix ) }, @@ -497,15 +497,15 @@ Object.assign( Q.Matrix.prototype, { }, toXsv: function( rowSeparator, valueSeparator ){ - return Q.Matrix.toXsv( this, rowSeparator, valueSeparator ) + return Matrix.toXsv( this, rowSeparator, valueSeparator ) }, toCsv: function(){ - return Q.Matrix.toXsv( this, '\n', ',' ) + return Matrix.toXsv( this, '\n', ',' ) }, toTsv: function(){ - return Q.Matrix.toXsv( this, '\n', '\t' ) + return Matrix.toXsv( this, '\n', '\t' ) }, toHtml: function(){ @@ -537,20 +537,20 @@ Object.assign( Q.Matrix.prototype, { 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 ) + if( ComplexNumber.isNumberLike( n )) n = new ComplexNumber( n ) + if( n instanceof ComplexNumber !== true ) return 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 ) + return 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( Matrix.isMatrixLike( matrix ) !== true ) + return error( `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 ) + if( Matrix.haveEqualDimensions( matrix, this ) !== true ) + return 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 ){ @@ -564,19 +564,19 @@ Object.assign( Q.Matrix.prototype, { }, fromArray$: function( array ){ - return this.copy$( Q.Matrix.fromArray( array )) + return this.copy$( Matrix.fromArray( array )) }, fromCsv$: function( csv ){ - return this.copy$( Q.Matrix.fromCsv( csv )) + return this.copy$( Matrix.fromCsv( csv )) }, fromTsv$: function( tsv ){ - return this.copy$( Q.Matrix.fromTsv( tsv )) + return this.copy$( Matrix.fromTsv( tsv )) }, fromHtml$: function( html ){ - return this.copy$( Q.Matrix.fromHtml( html )) + return this.copy$( Matrix.fromHtml( html )) }, @@ -586,19 +586,19 @@ Object.assign( Q.Matrix.prototype, { add: function( otherMatrix ){ - return Q.Matrix.add( this, otherMatrix ) + return Matrix.add( this, otherMatrix ) }, multiplyScalar: function( scalar ){ - return Q.Matrix.multiplyScalar( this, scalar ) + return Matrix.multiplyScalar( this, scalar ) }, multiply: function( otherMatrix ){ - return Q.Matrix.multiply( this, otherMatrix ) + return Matrix.multiply( this, otherMatrix ) }, multiplyTensor: function( otherMatrix ){ - return Q.Matrix.multiplyTensor( this, otherMatrix ) + return Matrix.multiplyTensor( this, otherMatrix ) }, @@ -628,25 +628,25 @@ Object.assign( Q.Matrix.prototype, { ////////////////////////// -Q.Matrix.createConstants( +Matrix.createConstants( - 'IDENTITY_2X2', Q.Matrix.createIdentity( 2 ), - 'IDENTITY_3X3', Q.Matrix.createIdentity( 3 ), - 'IDENTITY_4X4', Q.Matrix.createIdentity( 4 ), + 'IDENTITY_2X2', Matrix.createIdentity( 2 ), + 'IDENTITY_3X3', Matrix.createIdentity( 3 ), + 'IDENTITY_4X4', Matrix.createIdentity( 4 ), - 'CONSTANT0_2X2', new Q.Matrix( + 'CONSTANT0_2X2', new Matrix( [ 1, 1 ], [ 0, 0 ]), - 'CONSTANT1_2X2', new Q.Matrix( + 'CONSTANT1_2X2', new Matrix( [ 0, 0 ], [ 1, 1 ]), - 'NEGATION_2X2', new Q.Matrix( + 'NEGATION_2X2', new Matrix( [ 0, 1 ], [ 1, 0 ]), - 'TEST_MAP_9X9', new Q.Matrix( + '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 ], From 542bf9034ce03637b401b7d03b60a7912c1df948 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 22 Jul 2021 22:39:34 +0000 Subject: [PATCH 16/41] Split out helper functions for Q.js to Misc.js --- Q/Misc.js | 550 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 550 insertions(+) create mode 100644 Q/Misc.js diff --git a/Q/Misc.js b/Q/Misc.js new file mode 100644 index 0000000..960c8cc --- /dev/null +++ b/Q/Misc.js @@ -0,0 +1,550 @@ +//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); +} + +//Helper functions +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); +} + +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 + // }) + this.constants[key] = this[key]; + Object.freeze(this[key]); +} + +function createConstants() { + 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]); + } +} + +//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; +} + +COLORS = []; +ANIMALS = []; +constants = {}; +function loop() {} + +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; +} + +const namesIndex = 0; +const 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 + ] +); + +//Yes you can instead just define the functions in here using () => {} expressions, this is just seems neater... +module.exports = { + log: log, + error: error, + help: help, + warn: warn, + isUsefulNumber: isUsefulNumber, + isUsefulInteger: isUsefulInteger, + hypotenuse: hypotenuse, + logHypotenuse: logHypotenuse, + hyperbolicSine: hyperbolicSine, + hyperbolicCosine: hyperbolicCosine, + round: round, + createConstant: createConstant, + createConstants: createConstants, + toTitleCase: toTitleCase, + centerText: centerText, + getRandomName$: getRandomName$, + hueToColorName: hueToColorName, + colorIndexToHue: colorIndexToHue, + constants: constants, +}; From cc7ecd513a64b9e3b8ce0c6faed739e608edce2b Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Fri, 23 Jul 2021 20:29:58 +0000 Subject: [PATCH 17/41] Split up helper functions into categorized modules --- Q/Logging.js | 96 ++++++++++++++++++++++++++ Q/Math-Functions.js | 63 +++++++++++++++++ Q/Misc.js | 161 +------------------------------------------- 3 files changed, 162 insertions(+), 158 deletions(-) create mode 100644 Q/Logging.js create mode 100644 Q/Math-Functions.js diff --git a/Q/Logging.js b/Q/Logging.js new file mode 100644 index 0000000..7ec7cc3 --- /dev/null +++ b/Q/Logging.js @@ -0,0 +1,96 @@ +//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: log, + error: error, + help: help, + warn: warn, + toTitleCase: toTitleCase, + centerText: centerText, +}; diff --git a/Q/Math-Functions.js b/Q/Math-Functions.js new file mode 100644 index 0000000..731f7ea --- /dev/null +++ b/Q/Math-Functions.js @@ -0,0 +1,63 @@ +//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: isUsefulNumber, + isUsefulInteger: isUsefulInteger, + hypotenuse: hypotenuse, + logHypotenuse: logHypotenuse, + hyperbolicSine: hyperbolicSine, + hyperbolicCosine: hyperbolicCosine, + round: round, +}; diff --git a/Q/Misc.js b/Q/Misc.js index 960c8cc..d6bcae5 100644 --- a/Q/Misc.js +++ b/Q/Misc.js @@ -1,80 +1,7 @@ -//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); -} +COLORS = []; +ANIMALS = []; +constants = {}; -//Helper functions -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); -} function createConstant(key, value) { //Object.freeze( value ) @@ -103,77 +30,8 @@ function createConstants() { this.createConstant(arguments[i], arguments[i + 1]); } } - -//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; -} - -COLORS = []; -ANIMALS = []; -constants = {}; function loop() {} -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; -} - const namesIndex = 0; const shuffledNames = []; function shuffleNames$() { @@ -528,21 +386,8 @@ createConstants( //Yes you can instead just define the functions in here using () => {} expressions, this is just seems neater... module.exports = { - log: log, - error: error, - help: help, - warn: warn, - isUsefulNumber: isUsefulNumber, - isUsefulInteger: isUsefulInteger, - hypotenuse: hypotenuse, - logHypotenuse: logHypotenuse, - hyperbolicSine: hyperbolicSine, - hyperbolicCosine: hyperbolicCosine, - round: round, createConstant: createConstant, createConstants: createConstants, - toTitleCase: toTitleCase, - centerText: centerText, getRandomName$: getRandomName$, hueToColorName: hueToColorName, colorIndexToHue: colorIndexToHue, From 38728274e9b1530f42050c651b926c71b1b57f67 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Mon, 26 Jul 2021 19:37:00 +0000 Subject: [PATCH 18/41] Remove references to Q from Q-ComplexNumber.js --- Q/Q-ComplexNumber.js | 246 ++++++++++++++++++++++++------------------- 1 file changed, 137 insertions(+), 109 deletions(-) diff --git a/Q/Q-ComplexNumber.js b/Q/Q-ComplexNumber.js index 36ccb35..c57f55a 100644 --- a/Q/Q-ComplexNumber.js +++ b/Q/Q-ComplexNumber.js @@ -1,10 +1,12 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +const {warn, error} = require('./Logging'); +const misc = require('./Misc'); +const mathf = require('./Math-Functions'); - -Q.ComplexNumber = function( real, imaginary ){ +ComplexNumber = function( real, imaginary ){ ` The set of “real numbers” (ℝ) contains any number that can be expressed @@ -31,39 +33,63 @@ Q.ComplexNumber = function( real, imaginary ){ component (𝕀). https://en.wikipedia.org/wiki/Complex_number - Operation functions on Q.ComplexNumber instances generally accept as + 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 Q.ComplexNumber. + value returned is always an instance of ComplexNumber. ` - if( real instanceof Q.ComplexNumber ){ + if( real instanceof 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!' ) + 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(( 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.' ) + 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 = Q.ComplexNumber.index ++ + this.index = ComplexNumber.index ++ } -Object.assign( Q.ComplexNumber, { +Object.assign( ComplexNumber, { index: 0, - help: function(){ return Q.help( this )}, + help: function(){ return help( this )}, constants: {}, - createConstant: Q.createConstant, - createConstants: Q.createConstants, + 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) { + ComplexNumber.createConstant(arguments[i], arguments[i + 1]); + } + }, @@ -164,35 +190,35 @@ Object.assign( Q.ComplexNumber, { }, areEqual: function( a, b ){ - return Q.ComplexNumber.operate( + return ComplexNumber.operate( 'areEqual', a, b, function( a, b ){ - return Math.abs( a - b ) < Q.EPSILON + return Math.abs( a - b ) < EPSILON }, function( a, b ){ return ( - Math.abs( a - b.real ) < Q.EPSILON && - Math.abs( b.imaginary ) < Q.EPSILON + Math.abs( a - b.real ) < EPSILON && + Math.abs( b.imaginary ) < EPSILON ) }, function( a, b ){ return ( - Math.abs( a.real - b ) < Q.EPSILON && - Math.abs( a.imaginary ) < Q.EPSILON + Math.abs( a.real - b ) < EPSILON && + Math.abs( a.imaginary ) < EPSILON ) }, function( a, b ){ return ( - Math.abs( a.real - b.real ) < Q.EPSILON && - Math.abs( a.imaginary - b.imaginary ) < Q.EPSILON + Math.abs( a.real - b.real ) < EPSILON && + Math.abs( a.imaginary - b.imaginary ) < EPSILON ) } ) @@ -203,11 +229,11 @@ Object.assign( Q.ComplexNumber, { absolute: function( n ){ - return Q.hypotenuse( n.real, n.imaginary ) + return mathf.hypotenuse( n.real, n.imaginary ) }, conjugate: function( n ){ - return new Q.ComplexNumber( n.real, n.imaginary * -1 ) + return new ComplexNumber( n.real, n.imaginary * -1 ) }, operate: function( @@ -219,19 +245,19 @@ Object.assign( Q.ComplexNumber, { complexAndNumber, complexAndComplex ){ - if( Q.ComplexNumber.isNumberLike( a )){ + if( 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 ) + 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 Q.ComplexNumber ){ + else if( a instanceof 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 ) + 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 Q.error( 'Q.ComplexNumber attempted to', name, 'with something that is neither a Number or Q.ComplexNumber:', a ) + else return error( 'ComplexNumber attempted to', name, 'with something that is neither a Number or ComplexNumber:', a ) }, @@ -243,10 +269,10 @@ Object.assign( Q.ComplexNumber, { a = n.real, b = n.imaginary - return new Q.ComplexNumber( + return new ComplexNumber( - Math.sin( a ) * Q.hyperbolicCosine( b ), - Math.cos( a ) * Q.hyperbolicSine( b ) + Math.sin( a ) * mathf.hyperbolicCosine( b ), + Math.cos( a ) * mathf.hyperbolicSine( b ) ) }, cosine: function( n ){ @@ -255,10 +281,10 @@ Object.assign( Q.ComplexNumber, { a = n.real, b = n.imaginary - return new Q.ComplexNumber( + return new ComplexNumber( - Math.cos( a ) * Q.hyperbolicCosine( b ), - -Math.sin( a ) * Q.hyperbolicSine( b ) + Math.cos( a ) * mathf.hyperbolicCosine( b ), + -Math.sin( a ) * mathf.hyperbolicSine( b ) ) }, arcCosine: function( n ){ @@ -266,18 +292,18 @@ Object.assign( Q.ComplexNumber, { const a = n.real, b = n.imaginary, - t1 = Q.ComplexNumber.squareRoot( new Q.ComplexNumber( + t1 = ComplexNumber.squareRoot( new ComplexNumber( b * b - a * a + 1, a * b * -2 )), - t2 = Q.ComplexNumber.log( new Q.ComplexNumber( + t2 = ComplexNumber.log( new ComplexNumber( t1.real - b, t1.imaginary + a )) - return new Q.ComplexNumber( Math.PI / 2 - t2.imaginary, t2.real ) + return new ComplexNumber( Math.PI / 2 - t2.imaginary, t2.real ) }, arcTangent: function( n ){ @@ -287,19 +313,19 @@ Object.assign( Q.ComplexNumber, { if( a === 0 ){ - if( b === 1 ) return new Q.ComplexNumber( 0, Infinity ) - if( b === -1 ) return new Q.ComplexNumber( 0, -Infinity ) + 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 = Q.ComplexNumber.log( new Q.ComplexNumber( + t = ComplexNumber.log( new ComplexNumber( ( 1 - b * b - a * a ) / d, a / d * -2 )) - return new Q.ComplexNumber( t.imaginary / 2, t.real / 2 ) + return new ComplexNumber( t.imaginary / 2, t.real / 2 ) }, @@ -307,13 +333,13 @@ Object.assign( Q.ComplexNumber, { power: function( a, b ){ - if( Q.ComplexNumber.isNumberLike( a )) a = new Q.ComplexNumber( a ) - if( Q.ComplexNumber.isNumberLike( b )) b = new Q.ComplexNumber( 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 Q.ComplexNumber.ONE + if( b.isZero() ) return ComplexNumber.ONE // Zero raised to any power is 0. @@ -325,7 +351,7 @@ Object.assign( Q.ComplexNumber, { b.real > 0 && b.imaginary >= 0 ){ - return Q.ComplexNumber.ZERO + return ComplexNumber.ZERO } @@ -336,20 +362,20 @@ Object.assign( Q.ComplexNumber, { if( a.real >= 0 && a.imaginary === 0 ){ - return new Q.ComplexNumber( Math.pow( a.real, b.real ), 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 Q.ComplexNumber( Math.pow( a.imaginary, b.real ), 0 ) + return new ComplexNumber( Math.pow( a.imaginary, b.real ), 0 ) case 1: - return new Q.ComplexNumber( 0, Math.pow( a.imaginary, b.real )) + return new ComplexNumber( 0, Math.pow( a.imaginary, b.real )) case 2: - return new Q.ComplexNumber( -Math.pow( a.imaginary, b.real ), 0 ) + return new ComplexNumber( -Math.pow( a.imaginary, b.real ), 0 ) case 3: - return new Q.ComplexNumber( 0, -Math.pow( a.imaginary, b.real )) + return new ComplexNumber( 0, -Math.pow( a.imaginary, b.real )) } } } @@ -357,11 +383,11 @@ Object.assign( Q.ComplexNumber, { const arctangent2 = Math.atan2( a.imaginary, a.real ), - logHypotenuse = Q.logHypotenuse( a.real, a.imaginary ), + 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 Q.ComplexNumber( + return new ComplexNumber( x * Math.cos( y ), x * Math.sin( y ) @@ -370,8 +396,8 @@ Object.assign( Q.ComplexNumber, { squareRoot: function( a ){ const - result = new Q.ComplexNumber( 0, 0 ), - absolute = Q.ComplexNumber.absolute( a ) + result = new ComplexNumber( 0, 0 ), + absolute = ComplexNumber.absolute( a ) if( a.real >= 0 ){ @@ -401,24 +427,24 @@ Object.assign( Q.ComplexNumber, { }, log: function( a ){ - return new Q.ComplexNumber( + return new ComplexNumber( - Q.logHypotenuse( a.real, a.imaginary ), + mathf.logHypotenuse( a.real, a.imaginary ), Math.atan2( a.imaginary, a.real ) ) }, multiply: function( a, b ){ - return Q.ComplexNumber.operate( + return ComplexNumber.operate( 'multiply', a, b, function( a, b ){ - return new Q.ComplexNumber( a * b ) + return new ComplexNumber( a * b ) }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( a * b.real, a * b.imaginary @@ -426,7 +452,7 @@ Object.assign( Q.ComplexNumber, { }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( a.real * b, a.imaginary * b @@ -444,7 +470,7 @@ Object.assign( Q.ComplexNumber, { inners = a.imaginary * b.real, lasts = a.imaginary * b.imaginary * -1// Because i² = -1. - return new Q.ComplexNumber( + return new ComplexNumber( firsts + lasts, outers + inners @@ -454,20 +480,20 @@ Object.assign( Q.ComplexNumber, { }, divide: function( a, b ){ - return Q.ComplexNumber.operate( + return ComplexNumber.operate( 'divide', a, b, function( a, b ){ - return new Q.ComplexNumber( a / b ) + return new ComplexNumber( a / b ) }, function( a, b ){ - return new Q.ComplexNumber( a ).divide( b ) + return new ComplexNumber( a ).divide( b ) }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( a.real / b, a.imaginary / b @@ -495,16 +521,16 @@ Object.assign( Q.ComplexNumber, { }, add: function( a, b ){ - return Q.ComplexNumber.operate( + return ComplexNumber.operate( 'add', a, b, function( a, b ){ - return new Q.ComplexNumber( a + b ) + return new ComplexNumber( a + b ) }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( b.real + a, b.imaginary @@ -512,7 +538,7 @@ Object.assign( Q.ComplexNumber, { }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( a.real + b, a.imaginary @@ -520,7 +546,7 @@ Object.assign( Q.ComplexNumber, { }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( a.real + b.real, a.imaginary + b.imaginary @@ -530,16 +556,16 @@ Object.assign( Q.ComplexNumber, { }, subtract: function( a, b ){ - return Q.ComplexNumber.operate( + return ComplexNumber.operate( 'subtract', a, b, function( a, b ){ - return new Q.ComplexNumber( a - b ) + return new ComplexNumber( a - b ) }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( b.real - a, b.imaginary @@ -547,7 +573,7 @@ Object.assign( Q.ComplexNumber, { }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( a.real - b, a.imaginary @@ -555,7 +581,7 @@ Object.assign( Q.ComplexNumber, { }, function( a, b ){ - return new Q.ComplexNumber( + return new ComplexNumber( a.real - b.real, a.imaginary - b.imaginary @@ -568,29 +594,29 @@ Object.assign( Q.ComplexNumber, { -Q.ComplexNumber.createConstants( +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 ) + '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( Q.ComplexNumber.prototype, { +Object.assign( ComplexNumber.prototype, { // NON-destructive operations. clone: function(){ - return new Q.ComplexNumber( this.real, this.imaginary ) + return new ComplexNumber( this.real, this.imaginary ) }, reduce: function(){ @@ -605,69 +631,69 @@ Object.assign( Q.ComplexNumber.prototype, { // Note: this will kill function chaining. - return Q.ComplexNumber.toText( this.real, this.imaginary, roundToDecimal, padPositive ) + return ComplexNumber.toText( this.real, this.imaginary, roundToDecimal, padPositive ) }, isNaN: function( n ){ - return Q.ComplexNumber.isNaN( this )// Returned boolean will kill function chaining. + return ComplexNumber.isNaN( this )// Returned boolean will kill function chaining. }, isZero: function( n ){ - return Q.ComplexNumber.isZero( this )// Returned boolean will kill function chaining. + return ComplexNumber.isZero( this )// Returned boolean will kill function chaining. }, isFinite: function( n ){ - return Q.ComplexNumber.isFinite( this )// Returned boolean will kill function chaining. + return ComplexNumber.isFinite( this )// Returned boolean will kill function chaining. }, isInfinite: function( n ){ - return Q.ComplexNumber.isInfinite( this )// Returned boolean will kill function chaining. + return ComplexNumber.isInfinite( this )// Returned boolean will kill function chaining. }, isEqualTo: function( b ){ - return Q.ComplexNumber.areEqual( this, b )// Returned boolean will kill function chaining. + return ComplexNumber.areEqual( this, b )// Returned boolean will kill function chaining. }, absolute: function(){ - return Q.ComplexNumber.absolute( this )// Returned number will kill function chaining. + return ComplexNumber.absolute( this )// Returned number will kill function chaining. }, conjugate: function(){ - return Q.ComplexNumber.conjugate( this ) + return ComplexNumber.conjugate( this ) }, power: function( b ){ - return Q.ComplexNumber.power( this, b ) + return ComplexNumber.power( this, b ) }, squareRoot: function(){ - return Q.ComplexNumber.squareRoot( this ) + return ComplexNumber.squareRoot( this ) }, log: function(){ - return Q.ComplexNumber.log( this ) + return ComplexNumber.log( this ) }, multiply: function( b ){ - return Q.ComplexNumber.multiply( this, b ) + return ComplexNumber.multiply( this, b ) }, divide: function( b ){ - return Q.ComplexNumber.divide( this, b ) + return ComplexNumber.divide( this, b ) }, add: function( b ){ - return Q.ComplexNumber.add( this, b ) + return ComplexNumber.add( this, b ) }, subtract: function( b ){ - return Q.ComplexNumber.subtract( this, b ) + return ComplexNumber.subtract( this, b ) }, @@ -677,8 +703,8 @@ Object.assign( Q.ComplexNumber.prototype, { 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 ) + 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 @@ -719,4 +745,6 @@ Object.assign( Q.ComplexNumber.prototype, { }) - +module.exports = { + ComplexNumber: ComplexNumber, +} \ No newline at end of file From 35577c5382fd2bdd0442b03281013549994e793e Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Mon, 26 Jul 2021 22:46:18 +0000 Subject: [PATCH 19/41] Modularize utility classes of Q.js --- Q/Misc.js | 9 +- Q/Q-Circuit.js | 174 +++--- Q/Q-ComplexNumber.js | 1270 ++++++++++++++++++------------------------ Q/Q-Gate.js | 197 +------ Q/Q-History.js | 13 +- Q/Q-Matrix.js | 1182 +++++++++++++++++++-------------------- Q/Q-Qubit.js | 706 +++++++++++------------ Q/Q.js | 620 +-------------------- 8 files changed, 1600 insertions(+), 2571 deletions(-) diff --git a/Q/Misc.js b/Q/Misc.js index d6bcae5..cf0f526 100644 --- a/Q/Misc.js +++ b/Q/Misc.js @@ -1,3 +1,5 @@ +const {warn, log, error} = require('./Logging'); + COLORS = []; ANIMALS = []; constants = {}; @@ -27,13 +29,13 @@ function createConstants() { ); } for (let i = 0; i < arguments.length; i += 2) { - this.createConstant(arguments[i], arguments[i + 1]); + createConstant(arguments[i], arguments[i + 1]); } } function loop() {} -const namesIndex = 0; -const shuffledNames = []; +let namesIndex = 0; +let shuffledNames = []; function shuffleNames$() { let m = []; for (let c = 0; c < COLORS.length; c++) { @@ -82,6 +84,7 @@ createConstants( "RADIANS_TO_DEGREES", 180 / Math.PI, + "ANIMALS", [ "Aardvark", diff --git a/Q/Q-Circuit.js b/Q/Q-Circuit.js index 5c045e1..8089822 100644 --- a/Q/Q-Circuit.js +++ b/Q/Q-Circuit.js @@ -2,32 +2,42 @@ // 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'); -Q.Circuit = function( bandwidth, timewidth ){ + +Circuit = function( bandwidth, timewidth ){ // What number Circuit is this // that we’re attempting to make here? - this.index = Q.Circuit.index ++ + this.index = Circuit.index ++ // How many qubits (registers) shall we use? - if( !Q.isUsefulInteger( bandwidth )) bandwidth = 3 + 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( !Q.isUsefulInteger( timewidth )) timewidth = 5 + 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( Q.Qubit.HORIZONTAL ) + this.qubits = new Array( bandwidth ).fill( Qubit.HORIZONTAL ) // What operations will we perform on our qubits? @@ -50,37 +60,35 @@ Q.Circuit = function( bandwidth, timewidth ){ // Undo / Redo history. + this.history = new History( this ) - this.history = new Q.History( this ) - - } -Object.assign( Q.Circuit, { +Object.assign( Circuit, { index: 0, - help: function(){ return Q.help( this )}, + help: function(){ return logger.help( this )}, constants: {}, - createConstant: Q.createConstant, - createConstants: Q.createConstants, + createConstant: misc.createConstant, + createConstants: misc.createConstants, fromText: function( text ){ // This is a quick way to enable `fromText()` - // to return a default new Q.Circuit(). + // to return a default new Circuit(). - if( text === undefined ) return new Q.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 Q.Circuit.fromTableTransposed( + return Circuit.fromTableTransposed( text .trim() @@ -136,9 +144,9 @@ Object.assign( Q.Circuit, { // This is a quick way to enable `fromText()` - // to return a default new Q.Circuit(). + // to return a default new Circuit(). - if( text === undefined ) return new Q.Circuit() + if( text === undefined ) return new Circuit() // Is this a String Template -- as opposed to a regular String? @@ -201,7 +209,7 @@ Object.assign( Q.Circuit, { return Math.max( max, moments.length ) }, 0 ), - circuit = new Q.Circuit( bandwidth, timewidth ) + circuit = new Circuit( bandwidth, timewidth ) circuit.bandwidth = bandwidth circuit.timewidth = timewidth @@ -219,8 +227,8 @@ Object.assign( Q.Circuit, { const sibling = table[ s ][ m ] if( operation.gateSymbol === sibling.gateSymbol && operation.operationMomentId === sibling.operationMomentId && - Q.isUsefulInteger( operation.mappingIndex ) && - Q.isUsefulInteger( sibling.mappingIndex ) && + mathf.isUsefulInteger( operation.mappingIndex ) && + mathf.isUsefulInteger( sibling.mappingIndex ) && operation.mappingIndex !== sibling.mappingIndex ){ @@ -242,10 +250,10 @@ Object.assign( Q.Circuit, { if( siblingHasBeenFound === false && operation.gateSymbol !== 'I' ){ const - gate = Q.Gate.findBySymbol( operation.gateSymbol ), + gate = Gate.findBySymbol( operation.gateSymbol ), registerIndices = [] - if( Q.isUsefulInteger( operation.mappingIndex )){ + if( mathf.isUsefulInteger( operation.mappingIndex )){ registerIndices[ operation.mappingIndex ] = registerIndex } @@ -277,7 +285,7 @@ Object.assign( Q.Circuit, { const size = U.getWidth(), - result = Q.Matrix.createIdentity( size * 2 ) + result = Matrix.createIdentity( size * 2 ) // console.log( 'U', U.toTsv() ) // console.log( 'size', size ) @@ -349,7 +357,7 @@ Object.assign( Q.Circuit, { - const result = new Q.Matrix.createZero( n ) + const result = new Matrix.createZero( n ) // const X = numeric.rep([n, n], 0); @@ -418,7 +426,7 @@ Object.assign( Q.Circuit, { window.dispatchEvent( new CustomEvent( - 'Q.Circuit.evaluate began', { + 'Circuit.evaluate began', { detail: { circuit } } @@ -444,7 +452,7 @@ Object.assign( Q.Circuit, { // │ . │ // └ ┘ - const state = new Q.Matrix( 1, Math.pow( 2, circuit.bandwidth )) + const state = new Matrix( 1, Math.pow( 2, circuit.bandwidth )) state.write$( 0, 0, 1 ) @@ -502,12 +510,12 @@ Object.assign( Q.Circuit, { // 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( Q.Circuit.isControlledOperation(operation) ) { + if( Circuit.isControlledOperation(operation) ) { const scale = operation.registerIndices.length - ( operation.gate.is_multi_qubit ? 2 : 1) for( let j = 0; j < scale; j ++ ){ - U = Q.Circuit.controlled( U ) - // console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() ) + U = Circuit.controlled( U ) + // console.log( 'qubitIndex #', j, 'U = Circuit.controlled( U )', U.toTsv() ) } } @@ -518,7 +526,7 @@ Object.assign( Q.Circuit, { // and wow -- tracking down that bug was painful! const registerIndices = operation.registerIndices.slice() - state = Q.Circuit.expandMatrix( + state = Circuit.expandMatrix( circuit.bandwidth, U, @@ -532,7 +540,7 @@ Object.assign( Q.Circuit, { const progress = operationsCompleted / operationsTotal - window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate progressed', { detail: { + window.dispatchEvent( new CustomEvent( 'Circuit.evaluate progressed', { detail: { circuit, progress, @@ -582,7 +590,7 @@ Object.assign( Q.Circuit, { - window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate completed', { detail: { + window.dispatchEvent( new CustomEvent( 'Circuit.evaluate completed', { detail: { // circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: { circuit, @@ -603,7 +611,7 @@ Object.assign( Q.Circuit, { -Object.assign( Q.Circuit.prototype, { +Object.assign( Circuit.prototype, { clone: function(){ @@ -619,13 +627,13 @@ Object.assign( Q.Circuit.prototype, { }, evaluate$: function(){ - Q.Circuit.evaluate( this ) + Circuit.evaluate( this ) return this }, report$: function( length ){ if( this.needsEvaluation ) this.evaluate$() - if( !Q.isUsefulInteger( length )) length = 20 + if( !mathf.isUsefulInteger( length )) length = 20 const circuit = this, @@ -640,7 +648,7 @@ Object.assign( Q.Circuit.prototype, { + outcome.state +' ' + ''.padStart( probabilityPositive, '█' ) + ''.padStart( probabilityNegative, '░' ) - + Q.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance' + + mathf.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance' }, '' ) + '\n' return text @@ -1009,7 +1017,7 @@ because there’s another stand-alone X there tripping the logic!!! ? table.maximumCharacterWidth : table[ x ].maximumCharacterWidth - output[ 0 ] += Q.centerText( 'm'+ ( x + 1 ), padToLength + 4 ) + output[ 0 ] += logger.centerText( 'm'+ ( x + 1 ), padToLength + 4 ) for( let y = 0; y < table.bandwidth; y ++ ){ let @@ -1025,7 +1033,7 @@ because there’s another stand-alone X there tripping the logic!!! third += ' ' first += ' '.padEnd( padToLength ) - second += Q.centerText( '○', padToLength, '─' ) + second += logger.centerText( '○', padToLength, '─' ) third += ' '.padEnd( padToLength ) first += ' ' @@ -1048,7 +1056,7 @@ because there’s another stand-alone X there tripping the logic!!! second += '┤ ' first += '─'.padEnd( padToLength, '─' ) - second += Q.centerText( operation.symbolDisplay, padToLength ) + second += logger.centerText( operation.symbolDisplay, padToLength ) third += '─'.padEnd( padToLength, '─' ) @@ -1154,23 +1162,23 @@ device = LocalSimulator()\n\n` 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 + //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 = Q.ComplexNumber.toText(Math.cos(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), + 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 = Q.ComplexNumber.toText(-Math.cos(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), + 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 = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), + 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 = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), + 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(` + @@ -1290,12 +1298,12 @@ print(task.result().measurement_counts)` // 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 ] + 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 Q.error( `Q.Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid register indices array:`, registerIndices ) + 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 @@ -1381,7 +1389,7 @@ print(task.result().measurement_counts)` window.dispatchEvent( new CustomEvent( - 'Q.Circuit.clear$', { detail: { + 'Circuit.clear$', { detail: { circuit, momentIndex, @@ -1416,25 +1424,25 @@ print(task.result().measurement_counts)` // 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 ) + 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( Q.isUsefulNumber( momentIndex ) !== true || + if( mathf.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 ) + 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 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 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 && @@ -1449,7 +1457,7 @@ print(task.result().measurement_counts)` }, 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 ) + return logger.warn( `Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } with some out of range qubit indices:`, registerIndices ) } @@ -1492,7 +1500,7 @@ print(task.result().measurement_counts)` // 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 + isControlled = registerIndices.length > 1 && gate !== Gate.SWAP && gate.can_be_controlled !== undefined operation = { gate, @@ -1538,7 +1546,7 @@ print(task.result().measurement_counts)` window.dispatchEvent( new CustomEvent( - 'Q.Circuit.set$', { detail: { + 'Circuit.set$', { detail: { circuit, operation @@ -1569,17 +1577,17 @@ print(task.result().measurement_counts)` 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.` ) + 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 Q.error( `Q.Circuit attempted to copy a circuit but could not understand what moments to copy.` ) + else return logger.error( `Circuit attempted to copy a circuit but could not understand what moments to copy.` ) - Q.log( 0.8, + logger.log( 0.8, - '\nQ.Circuit copy operation:', + '\nCircuit copy operation:', '\n\n qubitFirstIndex', qubitFirstIndex, '\n qubitLastIndex ', qubitLastIndex, '\n qubitRange ', qubitRange, @@ -1615,7 +1623,7 @@ print(task.result().measurement_counts)` } = this.determineRanges( options ) - const copy = new Q.Circuit( registerRange, momentRange ) + const copy = new Circuit( registerRange, momentRange ) original.operations .filter( function( operation ){ @@ -1724,7 +1732,7 @@ print(task.result().measurement_counts)` 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.` ) + return logger.error( `Circuit attempted to splice circuit #${this.index} by an area that did not include all qubits _or_ all moments.` ) } @@ -1848,7 +1856,7 @@ print(task.result().measurement_counts)` 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.' ) + // 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.' ) @@ -1929,7 +1937,7 @@ print(task.result().measurement_counts)` // 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 ){ +Object.entries( Gate.constants ).forEach( function( entry ){ const gateConstantName = entry[ 0 ], @@ -1939,9 +1947,9 @@ Object.entries( Q.Gate.constants ).forEach( function( entry ){ 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$ + Circuit.prototype[ gateConstantName ] = set$ + Circuit.prototype[ gate.symbol ] = set$ + Circuit.prototype[ gate.symbol.toLowerCase() ] = set$ }) @@ -1952,55 +1960,55 @@ const bells = [ // Verbose without shortcuts. - new Q.Circuit( 2, 2 ) + new Circuit( 2, 2 ) .set$( Q.Gate.HADAMARD, 1, [ 1 ]) .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]), - new Q.Circuit( 2, 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 Q.Circuit( 2, 2 ) + new Circuit( 2, 2 ) .set$( 'H', 1, [ 1 ]) .set$( 'X', 2, [ 1 , 2 ]), - new Q.Circuit( 2, 2 ) + new Circuit( 2, 2 ) .set$( 'H', 1, 1 ) .set$( 'X', 2, [ 1 , 2 ]), // Convenience gate functions -- constant name. - new Q.Circuit( 2, 2 ) + new Circuit( 2, 2 ) .HADAMARD( 1, [ 1 ]) .PAULI_X( 2, [ 1, 2 ]), - new Q.Circuit( 2, 2 ) + new Circuit( 2, 2 ) .HADAMARD( 1, 1 ) .PAULI_X( 2, [ 1, 2 ]), // Convenience gate functions -- uppercase symbol. - new Q.Circuit( 2, 2 ) + new Circuit( 2, 2 ) .H( 1, [ 1 ]) .X( 2, [ 1, 2 ]), - new Q.Circuit( 2, 2 ) + new Circuit( 2, 2 ) .H( 1, 1 ) .X( 2, [ 1, 2 ]), // Convenience gate functions -- lowercase symbol. - new Q.Circuit( 2, 2 ) + new Circuit( 2, 2 ) .h( 1, [ 1 ]) .x( 2, [ 1, 2 ]), - new Q.Circuit( 2, 2 )// Perhaps the closest to Braket style. + new Circuit( 2, 2 )// Perhaps the closest to Braket style. .h( 1, 1 ) .x( 2, [ 1, 2 ]), @@ -2061,9 +2069,9 @@ if( bellsAreEqual ){ -Q.Circuit.createConstants( +Circuit.createConstants( - 'BELL', new Q(` + 'BELL', new Circuit.fromText(` H X#0 I X#1 @@ -2086,3 +2094,5 @@ Q.Circuit.createConstants( ) +module.exports = { Circuit } + diff --git a/Q/Q-ComplexNumber.js b/Q/Q-ComplexNumber.js index c57f55a..46f4dee 100644 --- a/Q/Q-ComplexNumber.js +++ b/Q/Q-ComplexNumber.js @@ -1,14 +1,11 @@ - // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const {warn, error} = require('./Logging'); -const misc = require('./Misc'); -const mathf = require('./Math-Functions'); - +const { warn, error } = require("./Logging"); +const misc = require("./Misc"); +const mathf = require("./Math-Functions"); -ComplexNumber = function( real, imaginary ){ - - ` +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 @@ -37,714 +34,553 @@ ComplexNumber = function( real, imaginary ){ 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) { - ComplexNumber.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 - ) - } - ) - } -}) - - - + `; + + 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: ComplexNumber, -} \ No newline at end of file + "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 }; diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index 4c82e02..26b33b5 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -1,8 +1,10 @@ // 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 ){ @@ -31,7 +33,7 @@ Gate = function( params ){ if( foundConstant ){ - warn( `Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, 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' @@ -48,21 +50,8 @@ Gate = function( params ){ // 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 Matrix === true ){ - - this.applyToQubit = function( qubit ){ - - return new Qubit( this.matrix.multiply( qubit )) - } - } - else { - - this.applyToQubit = function( qubit ){ return qubit } - } - } + + //Hi there. LTNLN here. We're just gonna toss the applyToQubit function entirely...Gate from here on is independent of Qubit! :).. } @@ -72,7 +61,6 @@ Object.assign( Gate, { index: 0, constants: {}, createConstant: function( key, value ){ - this[ key ] = value this.constants[ key ] = this[ key ] Object.freeze( this[ key ]) @@ -81,7 +69,7 @@ Object.assign( Gate, { if( arguments.length % 2 !== 0 ){ - return error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' ) + return logger.error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' ) } for( let i = 0; i < arguments.length; i += 2 ){ @@ -124,10 +112,6 @@ Object.assign( Gate.prototype, { return new Gate( Object.assign( {}, this, params )) }, - applyToQubits: function(){ - - return Array.from( arguments ).map( this.applyToQubit.bind( this )) - }, set$: function( key, value ){ this[ key ] = value @@ -173,7 +157,6 @@ Gate.createConstants ( name: 'Measure', nameCss: 'measure', matrix: Matrix.IDENTITY_2X2, - applyToQubit: function( state ){} }), 'HADAMARD', new Gate({ @@ -237,20 +220,12 @@ Gate.createConstants ( nameCss: 'phase', parameters: { "phi" : 1 }, updateMatrix$: function( phi ){ - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 }, - applyToQubit: function( qubit, phi ){ - - if( isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ 1, 0 ], - [ 0, ComplexNumber.E.power( new ComplexNumber( 0, phi ))]) - return new Qubit( matrix.multiply( qubit )) - }, can_be_controlled: true, has_parameters: true }), @@ -272,10 +247,10 @@ Gate.createConstants ( symbolSvg: '', name: 'Bloch sphere', nameCss: 'bloch', - applyToQubit: function( qubit ){ + // applyToQubit: function( qubit ){ - // Create Bloch sphere visualizer instance. - } + // // Create Bloch sphere visualizer instance. + // } }), 'RX', new Gate({ @@ -287,20 +262,12 @@ Gate.createConstants ( parameters: { "phi" : Math.PI / 2 }, updateMatrix$: function( phi ){ - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 }, - applyToQubit: function( qubit, phi ){ - - if( isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ Math.cos( phi / 2 ), new ComplexNumber( 0, -Math.sin( phi / 2 )) ], - [ new ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 )]) - return new Qubit( matrix.multiply( qubit )) - }, has_parameters: true }), 'RY', new Gate({ @@ -313,20 +280,12 @@ Gate.createConstants ( parameters: { "phi" : Math.PI / 2 }, updateMatrix$: function( phi ){ - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 }, - applyToQubit: function( qubit, phi ){ - - if( isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ Math.cos( phi / 2 ), -Math.sin( phi / 2 ) ], - [ Math.sin( phi / 2 ), Math.cos( phi / 2 )]) - return new Qubit( matrix.multiply( qubit )) - }, has_parameters: true }), 'RZ', new Gate({ @@ -339,20 +298,12 @@ Gate.createConstants ( parameters: { "phi" : Math.PI / 2 }, updateMatrix$: function( phi ){ - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 }, - applyToQubit: function( qubit, phi ){ - - if( isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ ComplexNumber.E.power( new ComplexNumber( 0, -phi / 2 )), 0 ], - [ 0, ComplexNumber.E.power( new ComplexNumber( 0, phi / 2 ))]) - return new Qubit( matrix.multiply( qubit )) - }, has_parameters: true }), 'UNITARY', new Gate({ @@ -368,7 +319,7 @@ Gate.createConstants ( "lambda" : Math.PI / 2 }, updateMatrix$: function( phi, theta, lambda ){ - if( (isUsefulNumber( +phi ) === true) && (isUsefulNumber( +theta ) === true) && (isUsefulNumber( +lambda ) === true) ) { + 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; @@ -386,23 +337,6 @@ Gate.createConstants ( [ c, d ]) return this }, - applyToQubit: function( qubit, phi, theta, lambda ){ - if( isUsefulNumber( phi ) === true ) phi = this.parameters[ "phi" ] - if( isUsefulNumber( theta ) === true ) theta = this.parameters[ "theta" ] - if( isUsefulNumber( lambda ) === true ) lambda = this.parameters[ "lambda" ] - const a = ComplexNumber.multiply( - ComplexNumber.E.power( new ComplexNumber( 0, -( phi + lambda ) / 2 )), Math.cos( theta / 2 )); - const b = ComplexNumber.multiply( - ComplexNumber.E.power( new ComplexNumber( 0, -( phi - lambda ) / 2 )), -Math.sin( theta / 2 )); - const c = ComplexNumber.multiply( - ComplexNumber.E.power( new ComplexNumber( 0, ( phi - lambda ) / 2 )), Math.sin( theta / 2 )); - const d = ComplexNumber.multiply( - ComplexNumber.E.power( new ComplexNumber( 0, ( phi + lambda ) / 2 )), Math.cos( theta / 2 )); - const matrix = new Matrix( - [ a, b ], - [ c, d ]) - return new Qubit( matrix.multiply( qubit )) - }, has_parameters: true }), 'NOT1_2', new Gate({ @@ -473,7 +407,7 @@ Gate.createConstants ( parameters: { "phi" : 0.0 }, updateMatrix$: function( phi ) { - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 ], @@ -481,17 +415,6 @@ Gate.createConstants ( [ 0, 0, 0, 1 ]) return this }, - applyToQubit: function( qubit, phi ) { - - if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ 1, 0, 0, 0 ], - [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, phi )), 0 ], - [ 0, new ComplexNumber( 0, 1 ), 0, 0 ], - [ 0, 0, 0, 1 ] - ) - return new Qubit( matrix.multiply( qubit )) - }, can_be_controlled: true, has_parameters: true, is_multi_qubit: true @@ -534,7 +457,7 @@ Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 ], @@ -542,16 +465,6 @@ Gate.createConstants ( [ new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ]) return this }, - applyToQubit: function( qubit, phi ) { - if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ Math.cos( phi / 2 ), 0, 0, new ComplexNumber( 0, -Math.sin( phi / 2 )) ], - [ 0, Math.cos( phi / 2 ), new ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ], - [ 0, new ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], - [ new ComplexNumber( 0, -Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ] - ) - return new Qubit( matrix.multiply( qubit )) - }, is_multi_qubit: true, has_parameters: true }), @@ -565,7 +478,7 @@ Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 ], @@ -573,16 +486,6 @@ Gate.createConstants ( [ 0, 0, 0, 1 ]) return this }, - applyToQubit: function( qubit, phi ) { - if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ 1, 0, 0, 0 ], - [ 0, Math.cos( phi / 2 ), new ComplexNumber( 0, Math.sin( phi / 2 )), 0 ], - [ 0, new ComplexNumber( 0, Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], - [ 0, 0, 0, 1 ] - ) - return new Qubit( matrix.multiply( qubit )) - }, is_multi_qubit: true, has_parameters: true }), @@ -596,7 +499,7 @@ Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 ], @@ -604,16 +507,6 @@ Gate.createConstants ( [ new ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ]) return this }, - applyToQubit: function( qubit, phi ) { - if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ Math.cos( phi / 2 ), 0, 0, new ComplexNumber( 0, -Math.sin( phi / 2 )) ], - [ 0, Math.cos( phi / 2 ), new ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ], - [ 0, new ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ], - [ new ComplexNumber( 0, Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ] - ) - return new Qubit( matrix.multiply( qubit )) - }, is_multi_qubit: true, has_parameters: true }), @@ -627,7 +520,7 @@ Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 ], @@ -635,16 +528,6 @@ Gate.createConstants ( [ 0, 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )) ]) return this }, - applyToQubit: function( qubit, phi ) { - if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ ComplexNumber.E.power( new ComplexNumber( 0, phi / 2 )), 0, 0, 0 ], - [ 0, ComplexNumber.E.power( new ComplexNumber( 0, phi / 2 )), 0, 0 ], - [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, phi / 2 )), 0], - [ 0, 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, -phi / 2 )) ] - ) - return new Qubit( matrix.multiply( qubit )) - }, is_multi_qubit: true, has_parameters: true }), @@ -658,7 +541,7 @@ Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 ], @@ -666,16 +549,6 @@ Gate.createConstants ( [ 0, 0, 0, 1 ]) return this }, - applyToQubit: function( qubit, phi ) { - if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ ComplexNumber.E.power( new ComplexNumber( 0, phi )), 0, 0, 0 ], - [ 0, 1, 0, 0 ], - [ 0, 0, 1, 0 ], - [ 0, 0, 0, 1 ] - ) - return new Qubit( matrix.multiply( qubit )) - }, is_multi_qubit: true, has_parameters: true }), @@ -689,7 +562,7 @@ Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +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 ], @@ -697,16 +570,6 @@ Gate.createConstants ( [ 0, 0, 0, 1 ]) return this }, - applyToQubit: function( qubit, phi ) { - if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ 1, 0, 0, 0 ], - [ 0, ComplexNumber.E.power( new ComplexNumber( 0, phi)), 0, 0 ], - [ 0, 0, 1, 0 ], - [ 0, 0, 0, 1 ] - ) - return new Qubit( matrix.multiply( qubit )) - }, is_multi_qubit: true, has_parameters: true }), @@ -720,7 +583,7 @@ Gate.createConstants ( parameters: { "phi" : 1 }, updateMatrix$: function( phi ) { - if( isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi + if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi this.matrix = new Matrix( [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], @@ -728,16 +591,6 @@ Gate.createConstants ( [ 0, 0, 0, 1 ]) return this }, - applyToQubit: function( qubit, phi ) { - if( isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ] - const matrix = new Matrix( - [ 1, 0, 0, 0 ], - [ 0, 1, 0, 0 ], - [ 0, 0, ComplexNumber.E.power( new ComplexNumber( 0, phi)), 0 ], - [ 0, 0, 0, 1 ] - ) - return new Qubit( matrix.multiply( qubit )) - }, is_multi_qubit: true, has_parameters: true }), @@ -774,3 +627,5 @@ Gate.createConstants ( ) + +module.exports = { Gate } \ No newline at end of file diff --git a/Q/Q-History.js b/Q/Q-History.js index db829cb..77efb85 100644 --- a/Q/Q-History.js +++ b/Q/Q-History.js @@ -4,7 +4,7 @@ -Q.History = function( instance ){ +History = function( instance ){ this.instance = instance this.entries = [[{ @@ -19,7 +19,7 @@ Q.History = function( instance ){ -Object.assign( Q.History.prototype, { +Object.assign( History.prototype, { assess: function(){ @@ -28,28 +28,28 @@ Object.assign( Q.History.prototype, { window.dispatchEvent( new CustomEvent( - 'Q.History undo is capable', { detail: { instance }} + 'History undo is capable', { detail: { instance }} )) } else { window.dispatchEvent( new CustomEvent( - 'Q.History undo is depleted', { detail: { instance }} + 'History undo is depleted', { detail: { instance }} )) } if( this.index + 1 < this.entries.length ){ window.dispatchEvent( new CustomEvent( - 'Q.History redo is capable', { detail: { instance }} + 'History redo is capable', { detail: { instance }} )) } else { window.dispatchEvent( new CustomEvent( - 'Q.History redo is depleted', { detail: { instance }} + 'History redo is depleted', { detail: { instance }} )) } return this @@ -193,3 +193,4 @@ Object.assign( Q.History.prototype, { +module.exports = { History } \ No newline at end of file diff --git a/Q/Q-Matrix.js b/Q/Q-Matrix.js index 4e4ba8f..5d7f28f 100644 --- a/Q/Q-Matrix.js +++ b/Q/Q-Matrix.js @@ -1,353 +1,319 @@ - // 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); + } +}; - - -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 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 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 // - // // +/////////////////////////// +// // +// Static properties // +// // /////////////////////////// - -Object.assign( Matrix, { - - index: 0, - help: function(){ return help( this )}, - constants: {},// Only holds references; an easy way to look up what constants exist. - createConstant: createConstant, - createConstants: createConstants, - - - 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 error( `Matrix could not find an importer for “${format}” data.` ) - return f - }, - fromArray: function( array ){ - - return new Matrix( ...array ) - }, - fromXsv: function( input, rowSeparator, valueSeparator ){ - - ` +Object.assign(Matrix, { + index: 0, + help: function () { + return 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 error( `Matrix attempted to add something that was not a matrix.` ) - } - if( Matrix.haveEqualDimensions( matrix0, matrix1 ) !== true ) - return 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 error( `Matrix attempted to scale something that was not a matrix.` ) - } - if( typeof scalar !== 'number' ){ - - return 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 ){ - - ` + `; + + 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||") + .match(/
/g, "") + .replace(/<\/td>(\s*)<\/tr>/g, "
(.*)<\/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. @@ -358,305 +324,275 @@ Object.assign( Matrix, { SEE ALSO https://en.wikipedia.org/wiki/Matrix_multiplication - ` - - if( Matrix.isMatrixLike( matrix0 ) !== true || - Matrix.isMatrixLike( matrix1 ) !== true ){ - - return error( `Matrix attempted to multiply something that was not a matrix.` ) - } - if( matrix0.columns.length !== matrix1.rows.length ){ - - return 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 ){ - - ` + `; + + 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 error( `Matrix attempted to tensor something that was not a matrix.` ) - } + `; + + 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); + }, +}); - 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 // - // // +////////////////////////////// +// // +// 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 ){ - - ` +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 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 ){ - - ` + `; + + 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" + cell.toText() + ""; + }, "\n\t") + + "\n\t" + ); + }, "\n") + "\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 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 error( `Invalid cell address for Matrix#${this.index}: x=${x}, y=${y}`, this ) - }, - copy$: function( matrix ){ - - if( Matrix.isMatrixLike( matrix ) !== true ) - return 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 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 ){ + `; + + 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)); + }, +}); - 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 // - // // ////////////////////////// - +// // +// 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 ]) -) - - - + "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 }; diff --git a/Q/Q-Qubit.js b/Q/Q-Qubit.js index 2b33355..48c3f1e 100644 --- a/Q/Q-Qubit.js +++ b/Q/Q-Qubit.js @@ -1,222 +1,205 @@ - // 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 ){ - - ` +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 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 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 ){ - - ` + `; + + 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), @@ -224,158 +207,143 @@ Object.assign( Q.Qubit, { 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 )) - }, -}) - - - + `; + + // 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 }; diff --git a/Q/Q.js b/Q/Q.js index 8ef8ae4..5ae6ed8 100644 --- a/Q/Q.js +++ b/Q/Q.js @@ -1,606 +1,27 @@ - // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +const { isUsefulInteger } = require("./Misc"); +const { Circuit } = require("./Q-Circuit"); +const Q = function () { + // Did we send arguments of the form + // ( bandwidth, timewidth )? + if ( + arguments.length === 2 && + Array.from(arguments).every(function (argument) { + return misc.isUsefulInteger(argument); + }) + ) { + return new Circuit(arguments[0], arguments[1]); + } + // Otherwise assume we are creating a circuit + // from a text block. -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 ){ + return Circuit.fromText(arguments[0]); +}; - 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( ` +console.log(` QQQQQQ @@ -617,7 +38,6 @@ https://quantumjavascript.app -` ) - - +`); +module.exports = { Q }; From b85ac8a69ca614ffb6f07c6245b34bc66c62fe00 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Mon, 26 Jul 2021 23:34:23 +0000 Subject: [PATCH 20/41] Replace Q.REVISION with a constant from Misc.js --- Q/Q.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Q/Q.js b/Q/Q.js index 5ae6ed8..278d95e 100644 --- a/Q/Q.js +++ b/Q/Q.js @@ -1,6 +1,6 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const { isUsefulInteger } = require("./Misc"); +const { isUsefulInteger, constants } = require("./Misc"); const { Circuit } = require("./Q-Circuit"); const Q = function () { // Did we send arguments of the form @@ -30,7 +30,7 @@ QQ QQ QQ QQ QQ QQ QQ QQ QQ - QQQQ ${Q.REVISION} + QQQQ ${constants.REVISION} From 03d8cfe1321561e13a88b95748fc407d66689e52 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Mon, 26 Jul 2021 23:35:28 +0000 Subject: [PATCH 21/41] MFix errors posited by esLint --- Q/Misc.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Q/Misc.js b/Q/Misc.js index cf0f526..f00d982 100644 --- a/Q/Misc.js +++ b/Q/Misc.js @@ -1,8 +1,8 @@ -const {warn, log, error} = require('./Logging'); +const logger = require('./Logging'); -COLORS = []; -ANIMALS = []; -constants = {}; +const COLORS = []; +const ANIMALS = []; +const constants = {}; function createConstant(key, value) { @@ -18,13 +18,13 @@ function createConstant(key, value) { // value, // writable: false // }) - this.constants[key] = this[key]; + constants[key] = this[key]; Object.freeze(this[key]); } function createConstants() { if (arguments.length % 2 !== 0) { - return error( + return logger.error( "Q attempted to create constants with invalid (KEY, VALUE) pairs." ); } @@ -32,7 +32,7 @@ function createConstants() { createConstant(arguments[i], arguments[i + 1]); } } -function loop() {} +// function loop() {} let namesIndex = 0; let shuffledNames = []; From e09bcdd9da3f9b72ca1eea92b58064db8040fd02 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Tue, 27 Jul 2021 00:02:58 +0000 Subject: [PATCH 22/41] Create index.js, npm entry point --- index.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 index.js diff --git a/index.js b/index.js new file mode 100644 index 0000000..fc40a83 --- /dev/null +++ b/index.js @@ -0,0 +1,13 @@ +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'); + +console.log("Howdy! Welcome to Q.js!"); + +module.exports = {Q, Circuit, Qubit, Gate, Matrix, ComplexNumber, mathf, misc, logger}; \ No newline at end of file From 72900595bed3ff35a458ba2d86139035b7553a7e Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Tue, 27 Jul 2021 18:02:55 +0000 Subject: [PATCH 23/41] Create dispatchEventToGlobal() function to handle where events are dispatched. --- Q/Misc.js | 10 ++++++++++ Q/Q-Circuit.js | 11 +++++------ Q/Q-History.js | 12 ++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Q/Misc.js b/Q/Misc.js index f00d982..82b41b8 100644 --- a/Q/Misc.js +++ b/Q/Misc.js @@ -4,6 +4,15 @@ const COLORS = []; const ANIMALS = []; const constants = {}; +function dispatchEventToGlobal(event) { + if(typeof window != undefined) { + window.dispatchEvent(event); + } + else { + global.dispatchEvent(event); + console.log(event); + } +} function createConstant(key, value) { //Object.freeze( value ) @@ -394,5 +403,6 @@ module.exports = { getRandomName$: getRandomName$, hueToColorName: hueToColorName, colorIndexToHue: colorIndexToHue, + dispatchEventToGlobal: dispatchEventToGlobal, constants: constants, }; diff --git a/Q/Q-Circuit.js b/Q/Q-Circuit.js index 8089822..e22cd03 100644 --- a/Q/Q-Circuit.js +++ b/Q/Q-Circuit.js @@ -423,8 +423,7 @@ Object.assign( Circuit, { // console.log( circuit.toDiagram() ) - - window.dispatchEvent( new CustomEvent( + misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate began', { @@ -540,7 +539,7 @@ Object.assign( Circuit, { const progress = operationsCompleted / operationsTotal - window.dispatchEvent( new CustomEvent( 'Circuit.evaluate progressed', { detail: { + misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate progressed', { detail: { circuit, progress, @@ -590,7 +589,7 @@ Object.assign( Circuit, { - window.dispatchEvent( new CustomEvent( 'Circuit.evaluate completed', { detail: { + misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate completed', { detail: { // circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: { circuit, @@ -1387,7 +1386,7 @@ print(task.result().measurement_counts)` foundOperations.forEach( function( operation ){ - window.dispatchEvent( new CustomEvent( + misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.clear$', { detail: { @@ -1544,7 +1543,7 @@ print(task.result().measurement_counts)` // Emit an event that we have set an operation // on this circuit. - window.dispatchEvent( new CustomEvent( + misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.set$', { detail: { diff --git a/Q/Q-History.js b/Q/Q-History.js index 77efb85..dd1c0d6 100644 --- a/Q/Q-History.js +++ b/Q/Q-History.js @@ -1,7 +1,7 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - +const {dispatchEventToGlobal} = require('./Misc'); History = function( instance ){ @@ -26,28 +26,28 @@ Object.assign( History.prototype, { const instance = this.instance if( this.index > 0 ){ - window.dispatchEvent( new CustomEvent( + dispatchEventToGlobal(new CustomEvent( 'History undo is capable', { detail: { instance }} - )) + )); } else { - window.dispatchEvent( new CustomEvent( + dispatchEventToGlobal(new CustomEvent( 'History undo is depleted', { detail: { instance }} )) } if( this.index + 1 < this.entries.length ){ - window.dispatchEvent( new CustomEvent( + dispatchEventToGlobal(new CustomEvent( 'History redo is capable', { detail: { instance }} )) } else { - window.dispatchEvent( new CustomEvent( + dispatchEventToGlobal(new CustomEvent( 'History redo is depleted', { detail: { instance }} )) From c268301ed4bef7fd4b980bccdf390586120f4cd4 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Tue, 27 Jul 2021 22:42:34 +0000 Subject: [PATCH 24/41] Safety Commit --- Q/Misc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Q/Misc.js b/Q/Misc.js index 82b41b8..adb4bf7 100644 --- a/Q/Misc.js +++ b/Q/Misc.js @@ -9,6 +9,7 @@ function dispatchEventToGlobal(event) { 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); } From 93e41f8f5d81517433e34d110587c1f02b18efe3 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 28 Jul 2021 21:54:31 +0000 Subject: [PATCH 25/41] Workspace Commit --- package.json | 30 + packages/quantum-js-util/Logging.mjs | 89 + packages/quantum-js-util/Math-Functions.mjs | 56 + packages/quantum-js-util/Misc.mjs | 400 +++ packages/quantum-js-util/Q-Circuit.mjs | 2098 +++++++++++ packages/quantum-js-util/Q-ComplexNumber.mjs | 585 ++++ packages/quantum-js-util/Q-Gate.mjs | 630 ++++ packages/quantum-js-util/Q-History.mjs | 196 ++ packages/quantum-js-util/Q-Matrix.mjs | 598 ++++ packages/quantum-js-util/Q-Qubit.mjs | 349 ++ packages/quantum-js-util/Q.mjs | 57 + .../quantum-js-util/__test__/Misc.test.js | 5 + .../__test__/Q-Circuit.test.js | 20 + .../__test__/Q-ComplexNumber.test.js | 8 + .../quantum-js-util/__test__/Q-Gate.test.js | 4 + .../quantum-js-util/__test__/Q-Matrix.test.js | 3 + .../quantum-js-util/__test__/Q-Qubit.test.js | 4 + packages/quantum-js-util/__test__/Q.test.js | 2 + packages/quantum-js-util/index.mjs | 1 + packages/quantum-js-util/package-lock.json | 3062 +++++++++++++++++ packages/quantum-js-util/package.json | 21 + packages/quantum-js-vis/Q-BlochSphere.js | 582 ++++ packages/quantum-js-vis/Q-Circuit-Editor.css | 899 +++++ packages/quantum-js-vis/Q-Circuit-Editor.js | 2282 ++++++++++++ packages/quantum-js-vis/Q.css | 475 +++ packages/quantum-js-vis/index.mjs | 2 + packages/quantum-js-vis/package-lock.json | 5 + packages/quantum-js-vis/package.json | 12 + 28 files changed, 12475 insertions(+) create mode 100644 package.json create mode 100644 packages/quantum-js-util/Logging.mjs create mode 100644 packages/quantum-js-util/Math-Functions.mjs create mode 100644 packages/quantum-js-util/Misc.mjs create mode 100644 packages/quantum-js-util/Q-Circuit.mjs create mode 100644 packages/quantum-js-util/Q-ComplexNumber.mjs create mode 100644 packages/quantum-js-util/Q-Gate.mjs create mode 100644 packages/quantum-js-util/Q-History.mjs create mode 100644 packages/quantum-js-util/Q-Matrix.mjs create mode 100644 packages/quantum-js-util/Q-Qubit.mjs create mode 100644 packages/quantum-js-util/Q.mjs create mode 100644 packages/quantum-js-util/__test__/Misc.test.js create mode 100644 packages/quantum-js-util/__test__/Q-Circuit.test.js create mode 100644 packages/quantum-js-util/__test__/Q-ComplexNumber.test.js create mode 100644 packages/quantum-js-util/__test__/Q-Gate.test.js create mode 100644 packages/quantum-js-util/__test__/Q-Matrix.test.js create mode 100644 packages/quantum-js-util/__test__/Q-Qubit.test.js create mode 100644 packages/quantum-js-util/__test__/Q.test.js create mode 100644 packages/quantum-js-util/index.mjs create mode 100644 packages/quantum-js-util/package-lock.json create mode 100644 packages/quantum-js-util/package.json 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.mjs create mode 100644 packages/quantum-js-vis/package-lock.json create mode 100644 packages/quantum-js-vis/package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000..0573d1b --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "quantum-js", + "version": "1.0.0", + "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", + "lint": "eslint", + "prettier": "prettier --write" + }, + "repository": { + "type": "git", + "url": "https://gitlab.aws.dev/ltnln/q-js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "eslint": "^7.31.0", + "jest": "^27.0.6", + "jsdom": "^16.6.0", + "lerna": "^4.0.0", + "n": "^7.3.1", + "prettier": "2.3.2" + }, + "dependencies": {}, + "workspaces": [ + "./packages/*" + ] +} diff --git a/packages/quantum-js-util/Logging.mjs b/packages/quantum-js-util/Logging.mjs new file mode 100644 index 0000000..98074c1 --- /dev/null +++ b/packages/quantum-js-util/Logging.mjs @@ -0,0 +1,89 @@ +//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; +} + +export { log, error, help, warn, toTitleCase, centerText }; diff --git a/packages/quantum-js-util/Math-Functions.mjs b/packages/quantum-js-util/Math-Functions.mjs new file mode 100644 index 0000000..9e8e927 --- /dev/null +++ b/packages/quantum-js-util/Math-Functions.mjs @@ -0,0 +1,56 @@ +//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); +} + + +export { isUsefulNumber, isUsefulInteger, hypotenuse, logHypotenuse, hyperbolicCosine, hyperbolicSine, round }; diff --git a/packages/quantum-js-util/Misc.mjs b/packages/quantum-js-util/Misc.mjs new file mode 100644 index 0000000..2af5cde --- /dev/null +++ b/packages/quantum-js-util/Misc.mjs @@ -0,0 +1,400 @@ +import * as logger from './Logging.mjs'; + +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 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 + ] +); + +export { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchEventToGlobal, constants }; diff --git a/packages/quantum-js-util/Q-Circuit.mjs b/packages/quantum-js-util/Q-Circuit.mjs new file mode 100644 index 0000000..608a323 --- /dev/null +++ b/packages/quantum-js-util/Q-Circuit.mjs @@ -0,0 +1,2098 @@ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + + +// +import * as logger from "./Logger.mjs"; +import * as misc from "./Misc.mjs"; +import * as mathf from "./Math-Functions.mjs"; +import { ComplexNumber } from "./Q-ComplexNumber.mjs"; +import { Gate } from "./Q-Gate.mjs"; +import { Qubit } from "./Q-Qubit.mjs"; +import { Matrix } from "./Q-Matrix.mjs"; +import { History } from "./Q-History.mjs"; + + + +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.dispatchEventToGlobal(new CustomEvent( + + '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.dispatchEventToGlobal(new CustomEvent( '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.dispatchEventToGlobal(new CustomEvent( '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.dispatchEventToGlobal(new CustomEvent( + + '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.dispatchEventToGlobal(new CustomEvent( + + '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- + // `) +) + + +export { Circuit }; + diff --git a/packages/quantum-js-util/Q-ComplexNumber.mjs b/packages/quantum-js-util/Q-ComplexNumber.mjs new file mode 100644 index 0000000..33b72ce --- /dev/null +++ b/packages/quantum-js-util/Q-ComplexNumber.mjs @@ -0,0 +1,585 @@ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +import { warn, error } from './Logging.mjs'; +import * as mathf from './Math-Functions.mjs'; + +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)); + }, +}); + +export { ComplexNumber }; diff --git a/packages/quantum-js-util/Q-Gate.mjs b/packages/quantum-js-util/Q-Gate.mjs new file mode 100644 index 0000000..c8b8c26 --- /dev/null +++ b/packages/quantum-js-util/Q-Gate.mjs @@ -0,0 +1,630 @@ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +import * as mathf from './Math-Functions.mjs'; +import * as logger from './Logging.mjs'; +import { ComplexNumber } from './Q-ComplexNumber.mjs'; +import {Matrix} from './Q-Matrix.mjs'; +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. + + + */ +) + + + +export { Gate }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-History.mjs b/packages/quantum-js-util/Q-History.mjs new file mode 100644 index 0000000..17727c4 --- /dev/null +++ b/packages/quantum-js-util/Q-History.mjs @@ -0,0 +1,196 @@ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +import { dispatchEventToGlobal } from './Misc.mjs'; + + +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 ){ + + dispatchEventToGlobal(new CustomEvent( + + 'History undo is capable', { detail: { instance }} + )); + } + else { + + dispatchEventToGlobal(new CustomEvent( + + 'History undo is depleted', { detail: { instance }} + )) + } + if( this.index + 1 < this.entries.length ){ + + dispatchEventToGlobal(new CustomEvent( + + 'History redo is capable', { detail: { instance }} + )) + } + else { + + dispatchEventToGlobal(new CustomEvent( + + '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 ) + } +}) + + + +export { History }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-Matrix.mjs b/packages/quantum-js-util/Q-Matrix.mjs new file mode 100644 index 0000000..1504a11 --- /dev/null +++ b/packages/quantum-js-util/Q-Matrix.mjs @@ -0,0 +1,598 @@ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +import * as logger from './Logging.mjs'; +import { ComplexNumber } from './Q-ComplexNumber.mjs'; + +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 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] + ) +); + +export { Matrix }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-Qubit.mjs b/packages/quantum-js-util/Q-Qubit.mjs new file mode 100644 index 0000000..4cdd21b --- /dev/null +++ b/packages/quantum-js-util/Q-Qubit.mjs @@ -0,0 +1,349 @@ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +import { Matrix } from "./Q-Matrix.mjs"; +import { Gate } from "./Q-Gate.mjs"; +import { ComplexNumber } from "./Q-ComplexNumber.mjs"; +import * as misc from "./Misc.mjs" +const logger = require("./Logging.mjs"); + +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 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)); + }, +}); + +export { Qubit }; diff --git a/packages/quantum-js-util/Q.mjs b/packages/quantum-js-util/Q.mjs new file mode 100644 index 0000000..e955fed --- /dev/null +++ b/packages/quantum-js-util/Q.mjs @@ -0,0 +1,57 @@ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + + +import Circuit from "./Q-Circuit.mjs"; + + + + +const 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 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 + + + +`); + + +export { Q }; +export { Gate } from "./Q-Gate.mjs"; +export { Matrix } from "./Q-Matrix.mjs"; +export { Qubit } from "./Q-Qubit.mjs"; +export { History } from "./Q-History.mjs"; +export { ComplexNumber } from "./Q-ComplexNumber.mjs"; +export * as logger from "./Logging.mjs"; +export * as misc from "./Misc.mjs"; +export { Circuit } from "./Q-Circuit.mjs"; diff --git a/packages/quantum-js-util/__test__/Misc.test.js b/packages/quantum-js-util/__test__/Misc.test.js new file mode 100644 index 0000000..14589b9 --- /dev/null +++ b/packages/quantum-js-util/__test__/Misc.test.js @@ -0,0 +1,5 @@ +const misc = require('../Misc'); +// console.log(misc); + +test.only("Trivial", () => expect(1).toBe(1)); +console.log(misc.constants.EPSILON); \ No newline at end of file diff --git a/packages/quantum-js-util/__test__/Q-Circuit.test.js b/packages/quantum-js-util/__test__/Q-Circuit.test.js new file mode 100644 index 0000000..6f7c1f8 --- /dev/null +++ b/packages/quantum-js-util/__test__/Q-Circuit.test.js @@ -0,0 +1,20 @@ +/* + *@jest-environment jsdom + */ +const {Circuit} = require('../Q-Circuit'); + +console.log(window == global ? "true": "false"); +window.addEventListener( 'Q.Circuit.evaluate completed', function( event ) { + console.log("event heard"); + console.log(event.detail.circuit.report$()); +}) +test('trivial', ()=> expect(1).toBe(1)); +test("creating a basic circuit with only a hadamard", () => { + let circuit = new Circuit(3, 3); + + circuit.set$('H', 1, 1); + // console.log(circuit.toDiagram()); + // console.log(circuit.toAmazonBraket()); + circuit.evaluate$(); + expect(1).toBe(1); +}) \ No newline at end of file diff --git a/packages/quantum-js-util/__test__/Q-ComplexNumber.test.js b/packages/quantum-js-util/__test__/Q-ComplexNumber.test.js new file mode 100644 index 0000000..8481793 --- /dev/null +++ b/packages/quantum-js-util/__test__/Q-ComplexNumber.test.js @@ -0,0 +1,8 @@ +const {ComplexNumber} = require('../Q-ComplexNumber'); +const {constants} = require('../Misc'); +console.log(ComplexNumber) +let num = new ComplexNumber(4, 0); +// console.log("constants: ", num.constants) +// console.log("misc.constants: ", constants); +console.log(ComplexNumber.E) +test("Trivial", () => expect(1).toBe(1)); diff --git a/packages/quantum-js-util/__test__/Q-Gate.test.js b/packages/quantum-js-util/__test__/Q-Gate.test.js new file mode 100644 index 0000000..14c0331 --- /dev/null +++ b/packages/quantum-js-util/__test__/Q-Gate.test.js @@ -0,0 +1,4 @@ +const {Gate} = require('../Q-Gate'); + +console.log(Gate.findBySymbol('P').name) +test("trivial", () => expect(1).toBe(1)); diff --git a/packages/quantum-js-util/__test__/Q-Matrix.test.js b/packages/quantum-js-util/__test__/Q-Matrix.test.js new file mode 100644 index 0000000..18f6a89 --- /dev/null +++ b/packages/quantum-js-util/__test__/Q-Matrix.test.js @@ -0,0 +1,3 @@ +const {Matrix} = require('../Q-Matrix'); +console.log(Matrix.constants['IDENTITY_2X2'].toTsv()); +test('trivial', () => expect(1).toBe(1)); \ No newline at end of file diff --git a/packages/quantum-js-util/__test__/Q-Qubit.test.js b/packages/quantum-js-util/__test__/Q-Qubit.test.js new file mode 100644 index 0000000..ebcc13a --- /dev/null +++ b/packages/quantum-js-util/__test__/Q-Qubit.test.js @@ -0,0 +1,4 @@ +const {Qubit} = require('../Q-Qubit'); + + +test('trivial', ()=> expect(1).toBe(1)); diff --git a/packages/quantum-js-util/__test__/Q.test.js b/packages/quantum-js-util/__test__/Q.test.js new file mode 100644 index 0000000..c3a51ca --- /dev/null +++ b/packages/quantum-js-util/__test__/Q.test.js @@ -0,0 +1,2 @@ +import {Q} from "../Q.mjs"; +test('trivial', ()=> expect(1).toBe(1)); \ No newline at end of file diff --git a/packages/quantum-js-util/index.mjs b/packages/quantum-js-util/index.mjs new file mode 100644 index 0000000..4bdd8e1 --- /dev/null +++ b/packages/quantum-js-util/index.mjs @@ -0,0 +1 @@ +export function print_greeting() {console.log("hello from util!");}; \ No newline at end of file diff --git a/packages/quantum-js-util/package-lock.json b/packages/quantum-js-util/package-lock.json new file mode 100644 index 0000000..9a5f4af --- /dev/null +++ b/packages/quantum-js-util/package-lock.json @@ -0,0 +1,3062 @@ +{ + "name": "quantum-js-util", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@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, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@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 + }, + "@babel/core": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", + "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@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, + "requires": { + "@babel/compat-data": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + } + }, + "@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, + "requires": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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 + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.8" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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 + }, + "@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 + }, + "@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, + "requires": { + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8" + } + }, + "@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, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "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, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "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, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "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, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@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 + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@babel/helper-validator-identifier": "^7.14.8", + "to-fast-properties": "^2.0.0" + } + }, + "@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 + }, + "@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, + "requires": { + "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" + } + }, + "@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 + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@jest/fake-timers": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "jest-mock": "^27.0.6" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@jest/globals": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.0.6.tgz", + "integrity": "sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==", + "dev": true, + "requires": { + "@jest/environment": "^27.0.6", + "@jest/types": "^27.0.6", + "expect": "^27.0.6" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + } + }, + "@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, + "requires": { + "@jest/console": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@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, + "requires": { + "@jest/test-result": "^27.0.6", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.0.6", + "jest-runtime": "^27.0.6" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@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, + "requires": { + "type-detect": "4.0.8" + } + }, + "@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, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@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 + }, + "@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, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@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, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@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, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@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, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@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, + "requires": { + "@types/node": "*" + } + }, + "@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 + }, + "@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, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@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, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@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 + }, + "@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 + }, + "@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 + }, + "@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, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@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 + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true + }, + "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, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "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 + }, + "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, + "requires": { + "debug": "4" + } + }, + "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, + "requires": { + "type-fest": "^0.21.3" + } + }, + "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 + }, + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "babel-plugin-jest-hoist": "^27.0.6", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "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 + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "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 + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "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, + "requires": { + "node-int64": "^0.4.0" + } + }, + "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 + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "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 + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "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 + }, + "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, + "requires": { + "color-name": "~1.1.4" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "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 + }, + "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, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "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, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "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, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "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, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "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, + "requires": { + "ms": "2.1.2" + } + }, + "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 + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "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 + }, + "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 + }, + "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 + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "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 + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "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" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expect": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz", + "integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "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 + } + } + }, + "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 + }, + "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 + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "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, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "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, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "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, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "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 + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "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 + }, + "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, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "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 + }, + "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, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "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, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "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 + }, + "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, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "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, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "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, + "requires": { + "ci-info": "^3.1.1" + } + }, + "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, + "requires": { + "has": "^1.0.3" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "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 + }, + "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, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + } + }, + "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, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "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, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "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, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.0.6.tgz", + "integrity": "sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA==", + "dev": true, + "requires": { + "@jest/core": "^27.0.6", + "import-local": "^3.0.2", + "jest-cli": "^27.0.6" + }, + "dependencies": { + "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, + "requires": { + "@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" + } + } + } + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + } + }, + "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, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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 + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + } + }, + "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, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "@types/node": "*" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "jest-regex-util": "^27.0.6", + "jest-snapshot": "^27.0.6" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "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, + "requires": { + "@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" + }, + "dependencies": { + "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" + } + } + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + } + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "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, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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 + }, + "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, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", + "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", + "dev": true, + "requires": { + "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" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "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, + "requires": { + "minimist": "^1.2.5" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "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 + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "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, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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, + "requires": { + "yallist": "^4.0.0" + } + }, + "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, + "requires": { + "semver": "^6.0.0" + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "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 + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "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 + }, + "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, + "requires": { + "mime-db": "1.49.0" + } + }, + "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 + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "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 + }, + "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-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-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 + }, + "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 + }, + "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 + }, + "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, + "requires": { + "path-key": "^3.0.0" + } + }, + "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 + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "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, + "requires": { + "p-try": "^2.0.0" + } + }, + "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, + "requires": { + "p-limit": "^2.2.0" + } + }, + "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 + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "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, + "requires": { + "find-up": "^4.0.0" + } + }, + "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 + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "ansi-regex": "^5.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "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 + } + } + }, + "prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "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, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "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 + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "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 + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, + "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 + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "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, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "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, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "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, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "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, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "has-flag": "^4.0.0" + } + }, + "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, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "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 + }, + "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, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "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, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "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 + }, + "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, + "requires": { + "is-number": "^7.0.0" + } + }, + "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, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "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 + }, + "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, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "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 + } + } + }, + "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, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "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, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "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 + }, + "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, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "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 + }, + "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, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "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 + }, + "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, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "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, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "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 + }, + "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 + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "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" + } + }, + "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 + } + } +} diff --git a/packages/quantum-js-util/package.json b/packages/quantum-js-util/package.json new file mode 100644 index 0000000..b835ae1 --- /dev/null +++ b/packages/quantum-js-util/package.json @@ -0,0 +1,21 @@ +{ + "name": "quantum-js-util", + "version": "1.0.0", + "description": "", + "main": "index.mjs", + "scripts": { + "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest" + }, + "devDependencies": { + "cross-env": "^7.0.3", + "eslint": "^7.31.0", + "jest": "^27.0.6", + "jsdom": "^16.6.0", + "prettier": "2.3.2" + }, + "dependencies": {}, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module" +} diff --git a/packages/quantum-js-vis/Q-BlochSphere.js b/packages/quantum-js-vis/Q-BlochSphere.js new file mode 100644 index 0000000..af0d3bd --- /dev/null +++ b/packages/quantum-js-vis/Q-BlochSphere.js @@ -0,0 +1,582 @@ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + + + + +Q.BlochSphere = function( onValueChange ){ + + Object.assign( this, { + + isRotating: false, + radius: 1, + radiusSafe: 1.01, + axesLineWidth: 0.01, + arcLineWidth: 0.015, + state: Q.Qubit.LEFT_HAND_CIRCULAR_POLARIZED.toBlochSphere(), + target: Q.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: Q.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: Q.BlochSphere.xAxisColor }) + ), + yAxis = new THREE.Mesh( + + new THREE.BoxGeometry( this.radius * 2.5, this.axesLineWidth, this.axesLineWidth ), + new THREE.MeshBasicMaterial({ color: Q.BlochSphere.yAxisColor }) + ), + zAxis = new THREE.Mesh( + + new THREE.BoxGeometry( this.axesLineWidth, this.radius * 2.5, this.axesLineWidth ), + new THREE.MeshBasicMaterial({ color: Q.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, + Q.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, + Q.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, + Q.BlochSphere.zAxisColor,// Blue + arrowHeadLength, + arrowHeadWidth + )) + + + // Create the X, Y, and Z axis labels. + + const + axesLabelStyle = { + + width: 128, + height: 128, + fillStyle: Q.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: Q.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: Q.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 = Q.BlochSphere.createLatitudeArc( arcR, 128, Math.PI / 2, Math.PI * 2 ), + thetaLine = new MeshLine(), + thetaPhiMaterial = new MeshLineMaterial({ + + color: Q.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 = Q.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. + + Q.BlochSphere.prototype.setTargetState.call( this ) +} + + + + + + + //////////////// + // // + // Static // + // // +//////////////// + + +Object.assign( Q.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 = Q.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 = Q.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( Q.BlochSphere.prototype, { + + update: function(){ + + if( this.isRotating ) this.group.rotation.y += Math.PI / 4096 + }, + setTargetState: function( target ){ + + if( target === undefined ) target = Q.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 ) + } + } +}) + + + + + + + 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..d4c5381 --- /dev/null +++ b/packages/quantum-js-vis/Q-Circuit-Editor.css @@ -0,0 +1,899 @@ +/* + + 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 { + + 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..d63781f --- /dev/null +++ b/packages/quantum-js-vis/Q-Circuit-Editor.js @@ -0,0 +1,2282 @@ +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. +const {Q, Circuit, Gate, logger, misc } = 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 + } +}) + + + + + + + ///////////////////////// + // // + // 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( !misc.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 Q.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' )) + console.log("param") + //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..3f28f02 --- /dev/null +++ b/packages/quantum-js-vis/Q.css @@ -0,0 +1,475 @@ +/* + + 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.mjs b/packages/quantum-js-vis/index.mjs new file mode 100644 index 0000000..b2f9778 --- /dev/null +++ b/packages/quantum-js-vis/index.mjs @@ -0,0 +1,2 @@ +import { print_greeting } from "quantum-js-util/index.js"; +print_greeting();nv \ 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..eef8069 --- /dev/null +++ b/packages/quantum-js-vis/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "quantum-js-vis", + "version": "1.0.0", + "lockfileVersion": 1 +} diff --git a/packages/quantum-js-vis/package.json b/packages/quantum-js-vis/package.json new file mode 100644 index 0000000..f6b127f --- /dev/null +++ b/packages/quantum-js-vis/package.json @@ -0,0 +1,12 @@ +{ + "name": "quantum-js-vis", + "version": "1.0.0", + "description": "Visualization Components for Q.js", + "main": "index.mjs", + "scripts": { + "test": "echo 'vis hello world' && exit 0" + }, + "keywords": [], + "author": "", + "license": "ISC" +} From 1df265b172e4c0b48d65d28651e70e35b45ad9ab Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Wed, 28 Jul 2021 22:25:39 +0000 Subject: [PATCH 26/41] Revert to required state --- .../{Logging.mjs => Logging.js} | 2 +- .../{Math-Functions.mjs => Math-Functions.js} | 2 +- .../quantum-js-util/{Misc.mjs => Misc.js} | 4 ++-- .../{Q-Circuit.mjs => Q-Circuit.js} | 19 ++++++++-------- ...Q-ComplexNumber.mjs => Q-ComplexNumber.js} | 6 ++--- .../quantum-js-util/{Q-Gate.mjs => Q-Gate.js} | 10 ++++----- .../{Q-History.mjs => Q-History.js} | 4 ++-- .../{Q-Matrix.mjs => Q-Matrix.js} | 6 ++--- .../{Q-Qubit.mjs => Q-Qubit.js} | 12 +++++----- packages/quantum-js-util/{Q.mjs => Q.js} | 22 +++++++++---------- packages/quantum-js-util/index.js | 1 + packages/quantum-js-util/index.mjs | 1 - packages/quantum-js-util/package.json | 3 +-- 13 files changed, 45 insertions(+), 47 deletions(-) rename packages/quantum-js-util/{Logging.mjs => Logging.js} (96%) rename packages/quantum-js-util/{Math-Functions.mjs => Math-Functions.js} (89%) rename packages/quantum-js-util/{Misc.mjs => Misc.js} (97%) rename packages/quantum-js-util/{Q-Circuit.mjs => Q-Circuit.js} (99%) rename packages/quantum-js-util/{Q-ComplexNumber.mjs => Q-ComplexNumber.js} (99%) rename packages/quantum-js-util/{Q-Gate.mjs => Q-Gate.js} (98%) rename packages/quantum-js-util/{Q-History.mjs => Q-History.js} (98%) rename packages/quantum-js-util/{Q-Matrix.mjs => Q-Matrix.js} (99%) rename packages/quantum-js-util/{Q-Qubit.mjs => Q-Qubit.js} (97%) rename packages/quantum-js-util/{Q.mjs => Q.js} (58%) create mode 100644 packages/quantum-js-util/index.js delete mode 100644 packages/quantum-js-util/index.mjs diff --git a/packages/quantum-js-util/Logging.mjs b/packages/quantum-js-util/Logging.js similarity index 96% rename from packages/quantum-js-util/Logging.mjs rename to packages/quantum-js-util/Logging.js index 98074c1..f05ebbd 100644 --- a/packages/quantum-js-util/Logging.mjs +++ b/packages/quantum-js-util/Logging.js @@ -86,4 +86,4 @@ function centerText(text, length, filler) { } else return text; } -export { log, error, help, warn, toTitleCase, centerText }; +module.exports = { log, error, help, warn, toTitleCase, centerText }; diff --git a/packages/quantum-js-util/Math-Functions.mjs b/packages/quantum-js-util/Math-Functions.js similarity index 89% rename from packages/quantum-js-util/Math-Functions.mjs rename to packages/quantum-js-util/Math-Functions.js index 9e8e927..498c381 100644 --- a/packages/quantum-js-util/Math-Functions.mjs +++ b/packages/quantum-js-util/Math-Functions.js @@ -53,4 +53,4 @@ function isUsefulInteger(n) { } -export { isUsefulNumber, isUsefulInteger, hypotenuse, logHypotenuse, hyperbolicCosine, hyperbolicSine, round }; +module.exports = { isUsefulNumber, isUsefulInteger, hypotenuse, logHypotenuse, hyperbolicCosine, hyperbolicSine, round }; diff --git a/packages/quantum-js-util/Misc.mjs b/packages/quantum-js-util/Misc.js similarity index 97% rename from packages/quantum-js-util/Misc.mjs rename to packages/quantum-js-util/Misc.js index 2af5cde..4eb824d 100644 --- a/packages/quantum-js-util/Misc.mjs +++ b/packages/quantum-js-util/Misc.js @@ -1,4 +1,4 @@ -import * as logger from './Logging.mjs'; +const logger = require('./Logging'); const COLORS = []; const ANIMALS = []; @@ -397,4 +397,4 @@ createConstants( ] ); -export { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchEventToGlobal, constants }; +module.exports = { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchEventToGlobal, constants }; diff --git a/packages/quantum-js-util/Q-Circuit.mjs b/packages/quantum-js-util/Q-Circuit.js similarity index 99% rename from packages/quantum-js-util/Q-Circuit.mjs rename to packages/quantum-js-util/Q-Circuit.js index 608a323..9cba24c 100644 --- a/packages/quantum-js-util/Q-Circuit.mjs +++ b/packages/quantum-js-util/Q-Circuit.js @@ -3,14 +3,15 @@ // -import * as logger from "./Logger.mjs"; -import * as misc from "./Misc.mjs"; -import * as mathf from "./Math-Functions.mjs"; -import { ComplexNumber } from "./Q-ComplexNumber.mjs"; -import { Gate } from "./Q-Gate.mjs"; -import { Qubit } from "./Q-Qubit.mjs"; -import { Matrix } from "./Q-Matrix.mjs"; -import { History } from "./Q-History.mjs"; +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'); + @@ -2094,5 +2095,5 @@ Circuit.createConstants( ) -export { Circuit }; +module.exports = {Circuit}; diff --git a/packages/quantum-js-util/Q-ComplexNumber.mjs b/packages/quantum-js-util/Q-ComplexNumber.js similarity index 99% rename from packages/quantum-js-util/Q-ComplexNumber.mjs rename to packages/quantum-js-util/Q-ComplexNumber.js index 33b72ce..e978248 100644 --- a/packages/quantum-js-util/Q-ComplexNumber.mjs +++ b/packages/quantum-js-util/Q-ComplexNumber.js @@ -1,7 +1,7 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -import { warn, error } from './Logging.mjs'; -import * as mathf from './Math-Functions.mjs'; +const { warn, error } = require('./Logging'); +const mathf = require('./Math-Functions'); ComplexNumber = function (real, imaginary) { ` @@ -582,4 +582,4 @@ Object.assign(ComplexNumber.prototype, { }, }); -export { ComplexNumber }; +module.exports = { ComplexNumber }; diff --git a/packages/quantum-js-util/Q-Gate.mjs b/packages/quantum-js-util/Q-Gate.js similarity index 98% rename from packages/quantum-js-util/Q-Gate.mjs rename to packages/quantum-js-util/Q-Gate.js index c8b8c26..472dde1 100644 --- a/packages/quantum-js-util/Q-Gate.mjs +++ b/packages/quantum-js-util/Q-Gate.js @@ -1,10 +1,10 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -import * as mathf from './Math-Functions.mjs'; -import * as logger from './Logging.mjs'; -import { ComplexNumber } from './Q-ComplexNumber.mjs'; -import {Matrix} from './Q-Matrix.mjs'; +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 ) @@ -627,4 +627,4 @@ Gate.createConstants ( -export { Gate }; \ No newline at end of file +module.exports = { Gate }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-History.mjs b/packages/quantum-js-util/Q-History.js similarity index 98% rename from packages/quantum-js-util/Q-History.mjs rename to packages/quantum-js-util/Q-History.js index 17727c4..1d38945 100644 --- a/packages/quantum-js-util/Q-History.mjs +++ b/packages/quantum-js-util/Q-History.js @@ -1,7 +1,7 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -import { dispatchEventToGlobal } from './Misc.mjs'; +const {dispatchEventToGlobal} = require('./Misc'); History = function( instance ){ @@ -193,4 +193,4 @@ Object.assign( History.prototype, { -export { History }; \ No newline at end of file +module.exports = { History }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-Matrix.mjs b/packages/quantum-js-util/Q-Matrix.js similarity index 99% rename from packages/quantum-js-util/Q-Matrix.mjs rename to packages/quantum-js-util/Q-Matrix.js index 1504a11..6f837c5 100644 --- a/packages/quantum-js-util/Q-Matrix.mjs +++ b/packages/quantum-js-util/Q-Matrix.js @@ -1,7 +1,7 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -import * as logger from './Logging.mjs'; -import { ComplexNumber } from './Q-ComplexNumber.mjs'; +const logger = require('./Logging'); +const {ComplexNumber} = require('./Q-ComplexNumber'); Matrix = function () { // We’re keeping track of how many matrices are @@ -595,4 +595,4 @@ Matrix.createConstants( ) ); -export { Matrix }; \ No newline at end of file +module.exports = { Matrix }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-Qubit.mjs b/packages/quantum-js-util/Q-Qubit.js similarity index 97% rename from packages/quantum-js-util/Q-Qubit.mjs rename to packages/quantum-js-util/Q-Qubit.js index 4cdd21b..48c3f1e 100644 --- a/packages/quantum-js-util/Q-Qubit.mjs +++ b/packages/quantum-js-util/Q-Qubit.js @@ -1,9 +1,9 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -import { Matrix } from "./Q-Matrix.mjs"; -import { Gate } from "./Q-Gate.mjs"; -import { ComplexNumber } from "./Q-ComplexNumber.mjs"; -import * as misc from "./Misc.mjs" -const logger = require("./Logging.mjs"); +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 @@ -346,4 +346,4 @@ Object.assign(Qubit.prototype, { }, }); -export { Qubit }; +module.exports = { Qubit }; diff --git a/packages/quantum-js-util/Q.mjs b/packages/quantum-js-util/Q.js similarity index 58% rename from packages/quantum-js-util/Q.mjs rename to packages/quantum-js-util/Q.js index e955fed..67a3a16 100644 --- a/packages/quantum-js-util/Q.mjs +++ b/packages/quantum-js-util/Q.js @@ -1,8 +1,14 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - -import Circuit from "./Q-Circuit.mjs"; - +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-ComplexNumber'); @@ -45,13 +51,5 @@ https://quantumjavascript.app `); +module.exports = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q}; -export { Q }; -export { Gate } from "./Q-Gate.mjs"; -export { Matrix } from "./Q-Matrix.mjs"; -export { Qubit } from "./Q-Qubit.mjs"; -export { History } from "./Q-History.mjs"; -export { ComplexNumber } from "./Q-ComplexNumber.mjs"; -export * as logger from "./Logging.mjs"; -export * as misc from "./Misc.mjs"; -export { Circuit } from "./Q-Circuit.mjs"; diff --git a/packages/quantum-js-util/index.js b/packages/quantum-js-util/index.js new file mode 100644 index 0000000..c1cc69f --- /dev/null +++ b/packages/quantum-js-util/index.js @@ -0,0 +1 @@ +const {Q} = require('./Q'); diff --git a/packages/quantum-js-util/index.mjs b/packages/quantum-js-util/index.mjs deleted file mode 100644 index 4bdd8e1..0000000 --- a/packages/quantum-js-util/index.mjs +++ /dev/null @@ -1 +0,0 @@ -export function print_greeting() {console.log("hello from util!");}; \ No newline at end of file diff --git a/packages/quantum-js-util/package.json b/packages/quantum-js-util/package.json index b835ae1..180a6ad 100644 --- a/packages/quantum-js-util/package.json +++ b/packages/quantum-js-util/package.json @@ -16,6 +16,5 @@ "dependencies": {}, "keywords": [], "author": "", - "license": "ISC", - "type": "module" + "license": "ISC" } From b0ac243c11c587eabff1ac16db62a038d69306d2 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 01:18:08 +0000 Subject: [PATCH 27/41] Write imports with CommonJS Syntax --- packages/quantum-js-util/Q.js | 2 +- packages/quantum-js-util/package.json | 4 +- packages/quantum-js-vis/Q-Circuit-Editor.js | 3 +- packages/quantum-js-vis/Q.css | 475 ------------------ .../__test__/Q-Circuit-Editor.test.js | 6 + packages/quantum-js-vis/index.js | 6 + packages/quantum-js-vis/index.mjs | 2 - packages/quantum-js-vis/package-lock.json | 32 +- packages/quantum-js-vis/package.json | 9 +- 9 files changed, 53 insertions(+), 486 deletions(-) delete mode 100644 packages/quantum-js-vis/Q.css create mode 100644 packages/quantum-js-vis/__test__/Q-Circuit-Editor.test.js create mode 100644 packages/quantum-js-vis/index.js delete mode 100644 packages/quantum-js-vis/index.mjs diff --git a/packages/quantum-js-util/Q.js b/packages/quantum-js-util/Q.js index 67a3a16..a4ca1d5 100644 --- a/packages/quantum-js-util/Q.js +++ b/packages/quantum-js-util/Q.js @@ -8,7 +8,7 @@ const {Gate} = require('./Q-Gate'); const {Qubit} = require('./Q-Qubit'); const {Matrix} = require('./Q-Matrix'); const {History} = require('./Q-History'); -const {Circuit} = require('./Q-ComplexNumber'); +const {Circuit} = require('./Q-Circuit'); diff --git a/packages/quantum-js-util/package.json b/packages/quantum-js-util/package.json index 180a6ad..36b51ed 100644 --- a/packages/quantum-js-util/package.json +++ b/packages/quantum-js-util/package.json @@ -2,9 +2,9 @@ "name": "quantum-js-util", "version": "1.0.0", "description": "", - "main": "index.mjs", + "main": "Q.js", "scripts": { - "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest" + "test": "echo 'hello from util!'" }, "devDependencies": { "cross-env": "^7.0.3", diff --git a/packages/quantum-js-vis/Q-Circuit-Editor.js b/packages/quantum-js-vis/Q-Circuit-Editor.js index d63781f..98bf717 100644 --- a/packages/quantum-js-vis/Q-Circuit-Editor.js +++ b/packages/quantum-js-vis/Q-Circuit-Editor.js @@ -1,7 +1,6 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. const {Q, Circuit, Gate, logger, misc } = require('quantum-js-util'); - Editor = function( circuit, targetEl ){ @@ -955,7 +954,7 @@ Editor.onSelectionChanged = function( circuitEl ){ Editor.onCircuitChanged = function( circuitEl ){ const circuit = circuitEl.circuit - window.dispatchEvent( new CustomEvent( + misc.dispatchEvent( new CustomEvent( 'Q gui altered circuit', { detail: { circuit: circuit }} diff --git a/packages/quantum-js-vis/Q.css b/packages/quantum-js-vis/Q.css deleted file mode 100644 index 3f28f02..0000000 --- a/packages/quantum-js-vis/Q.css +++ /dev/null @@ -1,475 +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: ''; -} - - - - - - - diff --git a/packages/quantum-js-vis/__test__/Q-Circuit-Editor.test.js b/packages/quantum-js-vis/__test__/Q-Circuit-Editor.test.js new file mode 100644 index 0000000..ba54036 --- /dev/null +++ b/packages/quantum-js-vis/__test__/Q-Circuit-Editor.test.js @@ -0,0 +1,6 @@ +/* + *@jest-environment jsdom + * + */ +Editor = require('../Q-Circuit-Editor'); +test("trivial", ()=> expect(1).toBe(1)); \ No newline at end of file diff --git a/packages/quantum-js-vis/index.js b/packages/quantum-js-vis/index.js new file mode 100644 index 0000000..ae12a1a --- /dev/null +++ b/packages/quantum-js-vis/index.js @@ -0,0 +1,6 @@ +const {Editor} = require('./Q-Circuit-Editor'); + + +console.log("Welcome to Q.js! The GUI experience!"); + +module.exports = {Editor}; \ No newline at end of file diff --git a/packages/quantum-js-vis/index.mjs b/packages/quantum-js-vis/index.mjs deleted file mode 100644 index b2f9778..0000000 --- a/packages/quantum-js-vis/index.mjs +++ /dev/null @@ -1,2 +0,0 @@ -import { print_greeting } from "quantum-js-util/index.js"; -print_greeting();nv \ No newline at end of file diff --git a/packages/quantum-js-vis/package-lock.json b/packages/quantum-js-vis/package-lock.json index eef8069..95e01ab 100644 --- a/packages/quantum-js-vis/package-lock.json +++ b/packages/quantum-js-vis/package-lock.json @@ -1,5 +1,35 @@ { "name": "quantum-js-vis", "version": "1.0.0", - "lockfileVersion": 1 + "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 index f6b127f..dda1527 100644 --- a/packages/quantum-js-vis/package.json +++ b/packages/quantum-js-vis/package.json @@ -2,11 +2,14 @@ "name": "quantum-js-vis", "version": "1.0.0", "description": "Visualization Components for Q.js", - "main": "index.mjs", + "main": "index.js", "scripts": { - "test": "echo 'vis hello world' && exit 0" + "test": "jest" }, "keywords": [], "author": "", - "license": "ISC" + "license": "ISC", + "devDependencies": { + "prettier": "^2.3.2" + } } From 2bde0035de12a7e1616a1c356ed459045a9aa83e Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:54:44 +0000 Subject: [PATCH 28/41] Update Q.js --- Q/Q.js | 620 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 599 insertions(+), 21 deletions(-) diff --git a/Q/Q.js b/Q/Q.js index 278d95e..0592f38 100644 --- a/Q/Q.js +++ b/Q/Q.js @@ -1,27 +1,604 @@ + // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const { isUsefulInteger, constants } = require("./Misc"); -const { Circuit } = require("./Q-Circuit"); -const Q = function () { - // Did we send arguments of the form - // ( bandwidth, timewidth )? - if ( - arguments.length === 2 && - Array.from(arguments).every(function (argument) { - return misc.isUsefulInteger(argument); - }) - ) { - return new Circuit(arguments[0], arguments[1]); - } +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 ) - // Otherwise assume we are creating a circuit - // from a text block. + 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 ){ - return Circuit.fromText(arguments[0]); -}; + const + a = Math.abs( x ), + b = Math.abs( y ) -console.log(` + 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 @@ -30,7 +607,7 @@ QQ QQ QQ QQ QQ QQ QQ QQ QQ - QQQQ ${constants.REVISION} + QQQQ ${Q.REVISION} @@ -38,6 +615,7 @@ https://quantumjavascript.app -`); +` ) + -module.exports = { Q }; +module.exports = Q From 4de6afee49a65b0175347a0c9c97223c55662c25 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:55:08 +0000 Subject: [PATCH 29/41] Delete Logging.js --- Q/Logging.js | 96 ---------------------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 Q/Logging.js diff --git a/Q/Logging.js b/Q/Logging.js deleted file mode 100644 index 7ec7cc3..0000000 --- a/Q/Logging.js +++ /dev/null @@ -1,96 +0,0 @@ -//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: log, - error: error, - help: help, - warn: warn, - toTitleCase: toTitleCase, - centerText: centerText, -}; From 533c754363c4a910a15b933811c615185945f26a Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:55:17 +0000 Subject: [PATCH 30/41] Delete Math-Functions.js --- Q/Math-Functions.js | 63 --------------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 Q/Math-Functions.js diff --git a/Q/Math-Functions.js b/Q/Math-Functions.js deleted file mode 100644 index 731f7ea..0000000 --- a/Q/Math-Functions.js +++ /dev/null @@ -1,63 +0,0 @@ -//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: isUsefulNumber, - isUsefulInteger: isUsefulInteger, - hypotenuse: hypotenuse, - logHypotenuse: logHypotenuse, - hyperbolicSine: hyperbolicSine, - hyperbolicCosine: hyperbolicCosine, - round: round, -}; From e72499f1751b9861629f41803a4d7af5e6b1a5ee Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:55:21 +0000 Subject: [PATCH 31/41] Delete Misc.js --- Q/Misc.js | 409 ------------------------------------------------------ 1 file changed, 409 deletions(-) delete mode 100644 Q/Misc.js diff --git a/Q/Misc.js b/Q/Misc.js deleted file mode 100644 index adb4bf7..0000000 --- a/Q/Misc.js +++ /dev/null @@ -1,409 +0,0 @@ -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 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 - ] -); - -//Yes you can instead just define the functions in here using () => {} expressions, this is just seems neater... -module.exports = { - createConstant: createConstant, - createConstants: createConstants, - getRandomName$: getRandomName$, - hueToColorName: hueToColorName, - colorIndexToHue: colorIndexToHue, - dispatchEventToGlobal: dispatchEventToGlobal, - constants: constants, -}; From 2cd6b48a3b2d215416b1aabbcb710af97a5fa4c8 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:55:54 +0000 Subject: [PATCH 32/41] Update Q-Circuit.js --- Q/Q-Circuit.js | 236 ++++++++++++++++++++++++------------------------- 1 file changed, 114 insertions(+), 122 deletions(-) diff --git a/Q/Q-Circuit.js b/Q/Q-Circuit.js index e22cd03..30d315d 100644 --- a/Q/Q-Circuit.js +++ b/Q/Q-Circuit.js @@ -2,42 +2,34 @@ // 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 ){ +Q.Circuit = function( bandwidth, timewidth ){ + // What number Circuit is this // that we’re attempting to make here? - this.index = Circuit.index ++ + this.index = Q.Circuit.index ++ // How many qubits (registers) shall we use? - if( !mathf.isUsefulInteger( bandwidth )) bandwidth = 3 + 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( !mathf.isUsefulInteger( timewidth )) timewidth = 5 + 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( Qubit.HORIZONTAL ) + this.qubits = new Array( bandwidth ).fill( Q.Qubit.HORIZONTAL ) // What operations will we perform on our qubits? @@ -60,35 +52,36 @@ Circuit = function( bandwidth, timewidth ){ // Undo / Redo history. - this.history = new History( this ) + this.history = new Q.History( this ) } -Object.assign( Circuit, { +Object.assign( Q.Circuit, { + index: 0, - help: function(){ return logger.help( this )}, + help: function(){ return Q.help( this )}, constants: {}, - createConstant: misc.createConstant, - createConstants: misc.createConstants, + createConstant: Q.createConstant, + createConstants: Q.createConstants, fromText: function( text ){ // This is a quick way to enable `fromText()` - // to return a default new Circuit(). + // to return a default new Q.Circuit(). - if( text === undefined ) return new 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 Circuit.fromTableTransposed( + return Q.Circuit.fromTableTransposed( text .trim() @@ -144,9 +137,9 @@ Object.assign( Circuit, { // This is a quick way to enable `fromText()` - // to return a default new Circuit(). + // to return a default new Q.Circuit(). - if( text === undefined ) return new Circuit() + if( text === undefined ) return new Q.Circuit() // Is this a String Template -- as opposed to a regular String? @@ -209,7 +202,7 @@ Object.assign( Circuit, { return Math.max( max, moments.length ) }, 0 ), - circuit = new Circuit( bandwidth, timewidth ) + circuit = new Q.Circuit( bandwidth, timewidth ) circuit.bandwidth = bandwidth circuit.timewidth = timewidth @@ -227,8 +220,8 @@ Object.assign( Circuit, { const sibling = table[ s ][ m ] if( operation.gateSymbol === sibling.gateSymbol && operation.operationMomentId === sibling.operationMomentId && - mathf.isUsefulInteger( operation.mappingIndex ) && - mathf.isUsefulInteger( sibling.mappingIndex ) && + Q.isUsefulInteger( operation.mappingIndex ) && + Q.isUsefulInteger( sibling.mappingIndex ) && operation.mappingIndex !== sibling.mappingIndex ){ @@ -250,10 +243,10 @@ Object.assign( Circuit, { if( siblingHasBeenFound === false && operation.gateSymbol !== 'I' ){ const - gate = Gate.findBySymbol( operation.gateSymbol ), + gate = Q.Gate.findBySymbol( operation.gateSymbol ), registerIndices = [] - if( mathf.isUsefulInteger( operation.mappingIndex )){ + if( Q.isUsefulInteger( operation.mappingIndex )){ registerIndices[ operation.mappingIndex ] = registerIndex } @@ -285,7 +278,7 @@ Object.assign( Circuit, { const size = U.getWidth(), - result = Matrix.createIdentity( size * 2 ) + result = Q.Matrix.createIdentity( size * 2 ) // console.log( 'U', U.toTsv() ) // console.log( 'size', size ) @@ -294,6 +287,7 @@ Object.assign( Circuit, { 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 ) @@ -301,14 +295,7 @@ Object.assign( Circuit, { } 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 @@ -318,6 +305,7 @@ Object.assign( Circuit, { // 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()) @@ -336,7 +324,7 @@ Object.assign( Circuit, { for( let i = 0; i < qubitIndices.length; i ++ ){ //qubitIndices[ i ] = ( circuitBandwidth - 1 ) - qubitIndices[ i ] - qubitIndices[ i ] -= 1 + qubitIndices[ i ] = ( circuitBandwidth - 0 ) - qubitIndices[ i ] } // console.log( 'qubits AFTER manipulation', qubitIndices ) @@ -357,7 +345,7 @@ Object.assign( Circuit, { - const result = new Matrix.createZero( n ) + const result = new Q.Matrix.createZero( n ) // const X = numeric.rep([n, n], 0); @@ -418,14 +406,17 @@ Object.assign( Circuit, { }, + + evaluate: function( circuit ){ // console.log( circuit.toDiagram() ) - misc.dispatchEventToGlobal(new CustomEvent( - 'Circuit.evaluate began', { + window.dispatchEvent( new CustomEvent( + + 'Q.Circuit.evaluate began', { detail: { circuit } } @@ -451,7 +442,7 @@ Object.assign( Circuit, { // │ . │ // └ ┘ - const state = new Matrix( 1, Math.pow( 2, circuit.bandwidth )) + const state = new Q.Matrix( 1, Math.pow( 2, circuit.bandwidth )) state.write$( 0, 0, 1 ) @@ -477,6 +468,7 @@ Object.assign( Circuit, { let matrix = circuit.operations.reduce( function( state, operation, i ){ + let U if( operation.registerIndices.length < Infinity ){ @@ -508,13 +500,12 @@ Object.assign( Circuit, { // 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 ++ ){ + // 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 = Circuit.controlled( U ) - // console.log( 'qubitIndex #', j, 'U = Circuit.controlled( U )', U.toTsv() ) + U = Q.Circuit.controlled( U ) + //console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() ) } } @@ -525,21 +516,21 @@ Object.assign( Circuit, { // and wow -- tracking down that bug was painful! const registerIndices = operation.registerIndices.slice() - state = Circuit.expandMatrix( + state = Q.Circuit.expandMatrix( circuit.bandwidth, U, registerIndices ).multiply( state ) - + operationsCompleted ++ const progress = operationsCompleted / operationsTotal - misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate progressed', { detail: { + window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate progressed', { detail: { circuit, progress, @@ -566,6 +557,7 @@ Object.assign( Circuit, { }, state ) + // console.log( 'result matrix', matrix.toTsv() ) @@ -589,7 +581,7 @@ Object.assign( Circuit, { - misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate completed', { detail: { + window.dispatchEvent( new CustomEvent( 'Q.Circuit.evaluate completed', { detail: { // circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: { circuit, @@ -610,7 +602,7 @@ Object.assign( Circuit, { -Object.assign( Circuit.prototype, { +Object.assign( Q.Circuit.prototype, { clone: function(){ @@ -626,13 +618,13 @@ Object.assign( Circuit.prototype, { }, evaluate$: function(){ - Circuit.evaluate( this ) + Q.Circuit.evaluate( this ) return this }, report$: function( length ){ if( this.needsEvaluation ) this.evaluate$() - if( !mathf.isUsefulInteger( length )) length = 20 + if( !Q.isUsefulInteger( length )) length = 20 const circuit = this, @@ -647,7 +639,7 @@ Object.assign( Circuit.prototype, { + outcome.state +' ' + ''.padStart( probabilityPositive, '█' ) + ''.padStart( probabilityNegative, '░' ) - + mathf.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance' + + Q.round( Math.round( 100 * outcome.probability ), 8 ).toString().padStart( 4, ' ' ) +'% chance' }, '' ) + '\n' return text @@ -1016,7 +1008,7 @@ because there’s another stand-alone X there tripping the logic!!! ? table.maximumCharacterWidth : table[ x ].maximumCharacterWidth - output[ 0 ] += logger.centerText( 'm'+ ( x + 1 ), padToLength + 4 ) + output[ 0 ] += Q.centerText( 'm'+ ( x + 1 ), padToLength + 4 ) for( let y = 0; y < table.bandwidth; y ++ ){ let @@ -1032,7 +1024,7 @@ because there’s another stand-alone X there tripping the logic!!! third += ' ' first += ' '.padEnd( padToLength ) - second += logger.centerText( '○', padToLength, '─' ) + second += Q.centerText( '○', padToLength, '─' ) third += ' '.padEnd( padToLength ) first += ' ' @@ -1055,7 +1047,7 @@ because there’s another stand-alone X there tripping the logic!!! second += '┤ ' first += '─'.padEnd( padToLength, '─' ) - second += logger.centerText( operation.symbolDisplay, padToLength ) + second += Q.centerText( operation.symbolDisplay, padToLength ) third += '─'.padEnd( padToLength, '─' ) @@ -1115,30 +1107,30 @@ https://cirq.readthedocs.io/en/stable/tutorial.html return headers }, toAmazonBraket: function(){ - let isValidBraketCircuit = true + 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` +aws_account_id = boto3.client("sts").get_caller_identity()["Account"] +device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/SIMULATOR_NAME)` //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. ++ `\ns3_folder = (f"braket-output-{aws_account_id}", "folder-name")\n\n` 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 + 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 isValidBraketCircuit = false + else is_valid_braket_circuit = false } else if( operation.gate.symbol === 'S' ) { @@ -1155,29 +1147,28 @@ device = LocalSimulator()\n\n` } //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) ) { + 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 isValidBraketCircuit = false + 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 + //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 + is_valid_braket_circuit &= 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), + 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 = ComplexNumber.toText(-Math.cos(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), + 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 = ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2), + 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 = ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2), + 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(` + @@ -1192,7 +1183,7 @@ device = LocalSimulator()\n\n` } // 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 ) + 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 ){ @@ -1210,7 +1201,7 @@ device = LocalSimulator()\n\n` 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!###` + return is_valid_braket_circuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###` }, toLatex: function(){ @@ -1297,12 +1288,12 @@ print(task.result().measurement_counts)` // 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 ] + 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 logger.error( `Circuit attempted to clear an input on Circuit #${ circuit.index } using an invalid register indices array:`, registerIndices ) + 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 @@ -1386,9 +1377,9 @@ print(task.result().measurement_counts)` foundOperations.forEach( function( operation ){ - misc.dispatchEventToGlobal(new CustomEvent( + window.dispatchEvent( new CustomEvent( - 'Circuit.clear$', { detail: { + 'Q.Circuit.clear$', { detail: { circuit, momentIndex, @@ -1423,25 +1414,25 @@ print(task.result().measurement_counts)` // 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 ) + 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( mathf.isUsefulNumber( momentIndex ) !== true || + if( Q.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 ) + 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 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 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 && @@ -1456,7 +1447,7 @@ print(task.result().measurement_counts)` }, 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 ) + 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 ) } @@ -1499,7 +1490,7 @@ print(task.result().measurement_counts)` // 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 + isControlled = registerIndices.length > 1 && gate !== Q.Gate.SWAP && gate.can_be_controlled !== undefined operation = { gate, @@ -1543,9 +1534,9 @@ print(task.result().measurement_counts)` // Emit an event that we have set an operation // on this circuit. - misc.dispatchEventToGlobal(new CustomEvent( + window.dispatchEvent( new CustomEvent( - 'Circuit.set$', { detail: { + 'Q.Circuit.set$', { detail: { circuit, operation @@ -1576,17 +1567,17 @@ print(task.result().measurement_counts)` 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.` ) + 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 logger.error( `Circuit attempted to copy a circuit but could not understand what moments to copy.` ) + else return Q.error( `Q.Circuit attempted to copy a circuit but could not understand what moments to copy.` ) - logger.log( 0.8, + Q.log( 0.8, - '\nCircuit copy operation:', + '\nQ.Circuit copy operation:', '\n\n qubitFirstIndex', qubitFirstIndex, '\n qubitLastIndex ', qubitLastIndex, '\n qubitRange ', qubitRange, @@ -1622,7 +1613,7 @@ print(task.result().measurement_counts)` } = this.determineRanges( options ) - const copy = new Circuit( registerRange, momentRange ) + const copy = new Q.Circuit( registerRange, momentRange ) original.operations .filter( function( operation ){ @@ -1731,7 +1722,7 @@ print(task.result().measurement_counts)` 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.` ) + return Q.error( `Q.Circuit attempted to splice circuit #${this.index} by an area that did not include all qubits _or_ all moments.` ) } @@ -1855,7 +1846,7 @@ print(task.result().measurement_counts)` 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.' ) + // 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.' ) @@ -1936,7 +1927,8 @@ print(task.result().measurement_counts)` // 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 ){ + +Object.entries( Q.Gate.constants ).forEach( function( entry ){ const gateConstantName = entry[ 0 ], @@ -1946,9 +1938,9 @@ Object.entries( Gate.constants ).forEach( function( entry ){ this.set$( gate, momentIndex, registerIndexOrIndices ) return this } - Circuit.prototype[ gateConstantName ] = set$ - Circuit.prototype[ gate.symbol ] = set$ - Circuit.prototype[ gate.symbol.toLowerCase() ] = set$ + Q.Circuit.prototype[ gateConstantName ] = set$ + Q.Circuit.prototype[ gate.symbol ] = set$ + Q.Circuit.prototype[ gate.symbol.toLowerCase() ] = set$ }) @@ -1959,55 +1951,55 @@ const bells = [ // Verbose without shortcuts. - new Circuit( 2, 2 ) + new Q.Circuit( 2, 2 ) .set$( Q.Gate.HADAMARD, 1, [ 1 ]) .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]), - new Circuit( 2, 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 Circuit( 2, 2 ) + new Q.Circuit( 2, 2 ) .set$( 'H', 1, [ 1 ]) .set$( 'X', 2, [ 1 , 2 ]), - new Circuit( 2, 2 ) + new Q.Circuit( 2, 2 ) .set$( 'H', 1, 1 ) .set$( 'X', 2, [ 1 , 2 ]), // Convenience gate functions -- constant name. - new Circuit( 2, 2 ) + new Q.Circuit( 2, 2 ) .HADAMARD( 1, [ 1 ]) .PAULI_X( 2, [ 1, 2 ]), - new Circuit( 2, 2 ) + new Q.Circuit( 2, 2 ) .HADAMARD( 1, 1 ) .PAULI_X( 2, [ 1, 2 ]), // Convenience gate functions -- uppercase symbol. - new Circuit( 2, 2 ) + new Q.Circuit( 2, 2 ) .H( 1, [ 1 ]) .X( 2, [ 1, 2 ]), - new Circuit( 2, 2 ) + new Q.Circuit( 2, 2 ) .H( 1, 1 ) .X( 2, [ 1, 2 ]), // Convenience gate functions -- lowercase symbol. - new Circuit( 2, 2 ) + new Q.Circuit( 2, 2 ) .h( 1, [ 1 ]) .x( 2, [ 1, 2 ]), - new Circuit( 2, 2 )// Perhaps the closest to Braket style. + new Q.Circuit( 2, 2 )// Perhaps the closest to Braket style. .h( 1, 1 ) .x( 2, [ 1, 2 ]), @@ -2068,13 +2060,13 @@ if( bellsAreEqual ){ -Circuit.createConstants( +Q.Circuit.createConstants( - 'BELL', new Circuit.fromText(` + '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 @@ -2093,5 +2085,5 @@ Circuit.createConstants( ) -module.exports = { Circuit } + From c8cbed2b71193744ca587032cececba9d4bc470a Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:56:20 +0000 Subject: [PATCH 33/41] Update Q-ComplexNumber.js --- Q/Q-ComplexNumber.js | 1251 +++++++++++++++++++++++------------------- 1 file changed, 693 insertions(+), 558 deletions(-) diff --git a/Q/Q-ComplexNumber.js b/Q/Q-ComplexNumber.js index 46f4dee..be320bc 100644 --- a/Q/Q-ComplexNumber.js +++ b/Q/Q-ComplexNumber.js @@ -1,11 +1,12 @@ + // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const { warn, error } = require("./Logging"); -const misc = require("./Misc"); -const mathf = require("./Math-Functions"); -ComplexNumber = function (real, imaginary) { - ` + + +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 @@ -30,557 +31,691 @@ ComplexNumber = function (real, imaginary) { component (𝕀). https://en.wikipedia.org/wiki/Complex_number - Operation functions on ComplexNumber instances generally accept as + 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 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 }; + 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 )) + } +}) + + From 5c32d755a06cbd8f58f2d769d120d327480f1da5 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:56:45 +0000 Subject: [PATCH 34/41] Update Q-Gate.js --- Q/Q-Gate.js | 526 ++++++++++------------------------------------------ 1 file changed, 97 insertions(+), 429 deletions(-) diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js index 26b33b5..41d3337 100644 --- a/Q/Q-Gate.js +++ b/Q/Q-Gate.js @@ -1,20 +1,17 @@ // 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 ){ + + +Q.Gate = function( params ){ Object.assign( this, params ) - this.index = Gate.index ++ + 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 @@ -25,7 +22,7 @@ Gate = function( params ){ const scope = this, foundConstant = Object - .values( Gate.constants ) + .values( Q.Gate.constants ) .find( function( gate ){ return gate.symbol === scope.symbol @@ -33,7 +30,7 @@ Gate = function( params ){ if( foundConstant ){ - logger.warn( `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' @@ -50,38 +47,38 @@ Gate = function( params ){ // 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( typeof this.applyToQubit !== 'function' ){ - if( arguments.length % 2 !== 0 ){ + if( this.matrix instanceof Q.Matrix === true ){ + + this.applyToQubit = function( qubit ){ - return logger.error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' ) + return new Q.Qubit( this.matrix.multiply( qubit )) + } } - for( let i = 0; i < arguments.length; i += 2 ){ + else { - this.createConstant( arguments[ i ], arguments[ i + 1 ]) + 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( Gate.constants ) + .values( Q.Gate.constants ) .find( function( item ){ if( typeof value === 'string' && @@ -95,22 +92,26 @@ Object.assign( Gate, { }, findBySymbol: function( symbol ){ - return Gate.findBy( 'symbol', symbol ) + return Q.Gate.findBy( 'symbol', symbol ) }, findByName: function( name ){ - return Gate.findBy( 'name', name ) + return Q.Gate.findBy( 'name', name ) } }) -Object.assign( Gate.prototype, { +Object.assign( Q.Gate.prototype, { clone: function( params ){ - return new Gate( Object.assign( {}, this, params )) + return new Q.Gate( Object.assign( {}, this, params )) + }, + applyToQubits: function(){ + + return Array.from( arguments ).map( this.applyToQubit.bind( this )) }, set$: function( key, value ){ @@ -126,492 +127,161 @@ Object.assign( Gate.prototype, { -Gate.createConstants ( +Q.Gate.createConstants( // Operate on a single qubit. - 'IDENTITY', new Gate({ + 'IDENTITY', new Q.Gate({ symbol: 'I', symbolAmazonBraket: 'i', symbolSvg: '', name: 'Identity', nameCss: 'identity', - matrix: Matrix.IDENTITY_2X2 + matrix: Q.Matrix.IDENTITY_2X2 }), - 'CURSOR', new Gate({ + 'CURSOR', new Q.Gate({ symbol: '*', symbolAmazonBraket: 'i', symbolSvg: '', name: 'Identity', nameCss: 'identity', - matrix: Matrix.IDENTITY_2X2 + matrix: Q.Matrix.IDENTITY_2X2 }), - 'MEASURE', new Gate({ + 'MEASURE', new Q.Gate({ symbol: 'M', symbolAmazonBraket: 'm', symbolSvg: '', name: 'Measure', nameCss: 'measure', - matrix: Matrix.IDENTITY_2X2, + matrix: Q.Matrix.IDENTITY_2X2, + applyToQubit: function( state ){} }), - 'HADAMARD', new Gate({ + 'HADAMARD', new Q.Gate({ symbol: 'H', symbolAmazonBraket: 'h', symbolSvg: '', name: 'Hadamard', nameCss: 'hadamard', - matrix: new Matrix( + matrix: new Q.Matrix( [ Math.SQRT1_2, Math.SQRT1_2 ], [ Math.SQRT1_2, -Math.SQRT1_2 ]) }), - 'PAULI_X', new Gate({ + 'PAULI_X', new Q.Gate({ symbol: 'X', symbolAmazonBraket: 'x', symbolSvg: '', name: 'Pauli X', nameCss: 'pauli-x', - matrix: new Matrix( + 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 Gate({ + [ 1, 0 ]) + }), + 'PAULI_Y', new Q.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({ + matrix: new Q.Matrix( + [ 0, new Q.ComplexNumber( 0, -1 )], + [ new Q.ComplexNumber( 0, 1 ), 0 ]) + }), + 'PAULI_Z', new Q.Gate({ symbol: 'Z', symbolAmazonBraket: 'z', symbolSvg: '', name: 'Pauli Z', nameCss: 'pauli-z', - matrix: new Matrix( + matrix: new Q.Matrix( [ 1, 0 ], - [ 0, -1 ]), - can_be_controlled: true - }, - ), - 'PHASE', new Gate({ + [ 0, -1 ]) + }), + 'PHASE', new Q.Gate({ symbol: 'P', - symbolAmazonBraket: 'phaseshift',// ltnln edit: change from 'p' to 'phaseshift' + symbolAmazonBraket: 'p',// !!! Double check this !!! symbolSvg: '', name: 'Phase', nameCss: 'phase', - parameters: { "phi" : 1 }, + phi: 1, updateMatrix$: function( phi ){ - if( mathf.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi; - this.matrix = new Matrix( + + if( Q.isUsefulNumber( phi ) === true ) this.phi = phi + this.matrix = new Q.Matrix( [ 1, 0 ], - [ 0, ComplexNumber.E.power( new ComplexNumber( 0, this.parameters[ "phi" ] ))]) + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.phi ))]) return this }, - can_be_controlled: true, - has_parameters: true + applyToQubit: function( qubit, phi ){ + + if( Q.isUsefulNumber( phi ) !== true ) phi = this.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 )) + } }), - 'PI_8', new Gate({ + 'PI_8', new Q.Gate({ symbol: 'T', symbolAmazonBraket: 't',// !!! Double check this !!! symbolSvg: '', name: 'π ÷ 8', nameCss: 'pi8', - matrix: new Matrix( + matrix: new Q.Matrix( [ 1, 0 ], - [ 0, ComplexNumber.E.power( new ComplexNumber( 0, Math.PI / 4 )) ]) + [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, Math.PI / 4 )) ]) }), - 'BLOCH', new Gate({ + '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 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({ + applyToQubit: function( qubit ){ - 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 )) ]) + // Create Bloch sphere visualizer instance. + } }), - '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', + 'SWAP', new Q.Gate({ + + symbol: 'S', + symbolAmazonBraket: 's',// !!! Double check this !!! 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 + matrix: new Q.Matrix( + [ 1, 0, 0, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 1, 0, 0 ], + [ 0, 0, 0, 1 ]) }), - 'SWAP1_2', new Gate({ + 'SWAP1_2', new Q.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( + matrix: new Q.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] - ) - }) + [ 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 ]) + }) /* @@ -624,8 +294,6 @@ Gate.createConstants ( */ -) - +) -module.exports = { Gate } \ No newline at end of file From 9d9cc6713cbc7da3f070994c4abd8072de03693e Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:57:15 +0000 Subject: [PATCH 35/41] Update Q-History.js --- Q/Q-History.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Q/Q-History.js b/Q/Q-History.js index dd1c0d6..df88c75 100644 --- a/Q/Q-History.js +++ b/Q/Q-History.js @@ -1,10 +1,10 @@ // Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const {dispatchEventToGlobal} = require('./Misc'); -History = function( instance ){ + +Q.History = function( instance ){ this.instance = instance this.entries = [[{ @@ -19,37 +19,37 @@ History = function( instance ){ -Object.assign( History.prototype, { +Object.assign( Q.History.prototype, { assess: function(){ const instance = this.instance if( this.index > 0 ){ - dispatchEventToGlobal(new CustomEvent( + window.dispatchEvent( new CustomEvent( - 'History undo is capable', { detail: { instance }} - )); + 'Q.History undo is capable', { detail: { instance }} + )) } else { - dispatchEventToGlobal(new CustomEvent( + window.dispatchEvent( new CustomEvent( - 'History undo is depleted', { detail: { instance }} + 'Q.History undo is depleted', { detail: { instance }} )) } if( this.index + 1 < this.entries.length ){ - dispatchEventToGlobal(new CustomEvent( + window.dispatchEvent( new CustomEvent( - 'History redo is capable', { detail: { instance }} + 'Q.History redo is capable', { detail: { instance }} )) } else { - dispatchEventToGlobal(new CustomEvent( + window.dispatchEvent( new CustomEvent( - 'History redo is depleted', { detail: { instance }} + 'Q.History redo is depleted', { detail: { instance }} )) } return this @@ -192,5 +192,3 @@ Object.assign( History.prototype, { }) - -module.exports = { History } \ No newline at end of file From 93e81b0b4428970674acf05f1ef12b1ba39d1b59 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:57:57 +0000 Subject: [PATCH 36/41] Update Q-Matrix.js --- Q/Q-Matrix.js | 1183 ++++++++++++++++++++++++++----------------------- 1 file changed, 623 insertions(+), 560 deletions(-) diff --git a/Q/Q-Matrix.js b/Q/Q-Matrix.js index 5d7f28f..af3a293 100644 --- a/Q/Q-Matrix.js +++ b/Q/Q-Matrix.js @@ -1,319 +1,353 @@ + // 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); - } -}; + + +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 // + // // /////////////////////////// -// // -// Static properties // -// // -/////////////////////////// -Object.assign(Matrix, { - index: 0, - help: function () { - return 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) { - ` + +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 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) { - ` + ` + + 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||' ) + .match( /
/g, '' ) + .replace( /<\/td>(\s*)<\/tr>/g, '
(.*)<\/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. @@ -324,275 +358,304 @@ Object.assign(Matrix, { 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) { - ` + ` + + 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 ( - 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); - }, -}); + ` + 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 // + // // ////////////////////////////// -// // -// 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) { - ` + +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 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) { - ` + ` + + 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'+ cell.toText() +'' + + }, '\n\t' ) + '\n\t' + + }, '\n' ) +'\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)); - }, -}); + ` + 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 // + // // ////////////////////////// -// // -// 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 }; + +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 ]) +) + + From 8fbb39c6f82d25d7b6905cb46297da0cea7804fd Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:58:24 +0000 Subject: [PATCH 37/41] Update Q-Qubit.js --- Q/Q-Qubit.js | 705 +++++++++++++++++++++++++++------------------------ 1 file changed, 368 insertions(+), 337 deletions(-) diff --git a/Q/Q-Qubit.js b/Q/Q-Qubit.js index 48c3f1e..6c32cb9 100644 --- a/Q/Q-Qubit.js +++ b/Q/Q-Qubit.js @@ -1,205 +1,222 @@ + // 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 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. - ` + + + + +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 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) { - ` + ` + + 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), @@ -207,143 +224,157 @@ Object.assign(Qubit, { 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 }; + ` + + + // 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 )) + }, +}) + + From dc160510ab5f78008ec2a3f7aa58cbadcf0d9aca Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 19:59:11 +0000 Subject: [PATCH 38/41] Update Q-Circuit-Editor.css --- Q/Q-Circuit-Editor.css | 1 - 1 file changed, 1 deletion(-) diff --git a/Q/Q-Circuit-Editor.css b/Q/Q-Circuit-Editor.css index d4c5381..c891f94 100644 --- a/Q/Q-Circuit-Editor.css +++ b/Q/Q-Circuit-Editor.css @@ -896,4 +896,3 @@ } - From 513a6b48eca6519d3d21e1b6751ad53293d84b2c Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Thu, 29 Jul 2021 20:08:28 +0000 Subject: [PATCH 39/41] Delete workspace from master --- .gitignore | 3 +- build/__test__/Q.test.js | 319 -- packages/quantum-js-util/Logging.js | 89 - packages/quantum-js-util/Math-Functions.js | 56 - packages/quantum-js-util/Misc.js | 400 --- packages/quantum-js-util/Q-Circuit.js | 2099 ----------- packages/quantum-js-util/Q-ComplexNumber.js | 585 ---- packages/quantum-js-util/Q-Gate.js | 630 ---- packages/quantum-js-util/Q-History.js | 196 -- packages/quantum-js-util/Q-Matrix.js | 598 ---- packages/quantum-js-util/Q-Qubit.js | 349 -- packages/quantum-js-util/Q.js | 55 - .../quantum-js-util/__test__/Misc.test.js | 5 - .../__test__/Q-Circuit.test.js | 20 - .../__test__/Q-ComplexNumber.test.js | 8 - .../quantum-js-util/__test__/Q-Gate.test.js | 4 - .../quantum-js-util/__test__/Q-Matrix.test.js | 3 - .../quantum-js-util/__test__/Q-Qubit.test.js | 4 - packages/quantum-js-util/__test__/Q.test.js | 2 - packages/quantum-js-util/index.js | 1 - packages/quantum-js-util/package-lock.json | 3062 ----------------- packages/quantum-js-util/package.json | 20 - packages/quantum-js-vis/Q-BlochSphere.js | 582 ---- packages/quantum-js-vis/Q-Circuit-Editor.css | 899 ----- packages/quantum-js-vis/Q-Circuit-Editor.js | 2281 ------------ .../__test__/Q-Circuit-Editor.test.js | 6 - packages/quantum-js-vis/index.js | 6 - packages/quantum-js-vis/package-lock.json | 35 - packages/quantum-js-vis/package.json | 15 - 29 files changed, 2 insertions(+), 12330 deletions(-) delete mode 100644 build/__test__/Q.test.js delete mode 100644 packages/quantum-js-util/Logging.js delete mode 100644 packages/quantum-js-util/Math-Functions.js delete mode 100644 packages/quantum-js-util/Misc.js delete mode 100644 packages/quantum-js-util/Q-Circuit.js delete mode 100644 packages/quantum-js-util/Q-ComplexNumber.js delete mode 100644 packages/quantum-js-util/Q-Gate.js delete mode 100644 packages/quantum-js-util/Q-History.js delete mode 100644 packages/quantum-js-util/Q-Matrix.js delete mode 100644 packages/quantum-js-util/Q-Qubit.js delete mode 100644 packages/quantum-js-util/Q.js delete mode 100644 packages/quantum-js-util/__test__/Misc.test.js delete mode 100644 packages/quantum-js-util/__test__/Q-Circuit.test.js delete mode 100644 packages/quantum-js-util/__test__/Q-ComplexNumber.test.js delete mode 100644 packages/quantum-js-util/__test__/Q-Gate.test.js delete mode 100644 packages/quantum-js-util/__test__/Q-Matrix.test.js delete mode 100644 packages/quantum-js-util/__test__/Q-Qubit.test.js delete mode 100644 packages/quantum-js-util/__test__/Q.test.js delete mode 100644 packages/quantum-js-util/index.js delete mode 100644 packages/quantum-js-util/package-lock.json delete mode 100644 packages/quantum-js-util/package.json delete mode 100644 packages/quantum-js-vis/Q-BlochSphere.js delete mode 100644 packages/quantum-js-vis/Q-Circuit-Editor.css delete mode 100644 packages/quantum-js-vis/Q-Circuit-Editor.js delete mode 100644 packages/quantum-js-vis/__test__/Q-Circuit-Editor.test.js delete mode 100644 packages/quantum-js-vis/index.js delete mode 100644 packages/quantum-js-vis/package-lock.json delete mode 100644 packages/quantum-js-vis/package.json diff --git a/.gitignore b/.gitignore index 2c15fd5..b90121d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ notes.txt -.DS_Store \ No newline at end of file +.DS_Store +node_modules diff --git a/build/__test__/Q.test.js b/build/__test__/Q.test.js deleted file mode 100644 index 3ae28dd..0000000 --- a/build/__test__/Q.test.js +++ /dev/null @@ -1,319 +0,0 @@ -/** - * @jest-environment jsdom - */ - - -//TODO: ask for the breadth of these tests; ex. should I be testing functions on every single gate operation? - -var Q = require('../q-old'); - -/* - * Q-Gate.js; File contains all valid gate operations in the Q library. - */ - -/* - * Testing basic getters (.findBySymbol(), .findByName()) for basic correctness. - */ -beforeEach(() => { - Q.Matrix.index = 0 -}); - -test(`Q.Gate.findBySymbol(), with symbol 'I'. Should return Identity gate object.`, () => { - expect(Q.Gate.findBySymbol( 'I' )).toEqual({ - index: 0, - symbol: 'I', - symbolAmazonBraket: 'i', - symbolSvg: '', - name: 'Identity', - nameCss: 'identity', - matrix: Q.Matrix.IDENTITY_2X2, - parameters: {}, - applyToQubit: expect.any(Function) - }) -}) - -test(`Q.Gate.findByName(), with name 'Identity'. Should return Identity gate object.`, () => { - expect(Q.Gate.findByName( 'Identity' )).toEqual({ - index: 0, - symbol: 'I', - symbolAmazonBraket: 'i', - symbolSvg: '', - name: 'Identity', - nameCss: 'identity', - matrix: Q.Matrix.IDENTITY_2X2, - parameters: {}, - applyToQubit: expect.any(Function) - }) -}) - -/* - * Testing basic getters (.findBySymbol(), .findByName())for invalid gate requests - * - */ - -test(`Q.Gate.findBySymbol(), with symbol 'n/a', which should return undefined`, () => { - expect(Q.Gate.findBySymbol( 'n/a' )).toBeUndefined() -}) - -test(`Q.Gate.findByName(), with name 'n/a -', which should return undefined`, () => { - expect(Q.Gate.findByName( 'n/a' )).toBeUndefined() -}) - -/* - * Test that Q.Gate.prototype.clone() returns a deep copy of a gate operation and not a reference. - */ - -test(`Check that Q.Gate.prototype.clone() returns a deep copy of the passed in gate`, () => { - let new_gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( 'U' )) - expect( new_gate ).not.toBe( Q.Gate.findBySymbol( 'U' )) - expect( Q.Gate.findBySymbol( 'U' ) ).toBe( Q.Gate.findBySymbol( 'U' )) -}) - - -/* - * The following series of functions will test the updateMatrix$ function for a few of the - * gate operations included in Q. Not all gate operations need to recalculate their matrix. - * But all parameterized qubits calculate their matrix in the same way. - * - * Traditional edge cases (divide by 0, restricted input domain) are non-existent as - * none of the [currently] includes gates' matrices have restricted domains for inputs. - * - * Checking CSV strings as values should be the same; deep copies will never match due to Q.Matrix.index attribute. - */ - -test(`updateMatrix$ for the phase gate with parameter (phi = -π)`, () => { - const gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( 'P' )); - const expected_result = Q.Matrix.toCsv( new Q.Matrix( - [1, 0], - [0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -Math.PI ))] - )); - gate.updateMatrix$(-Math.PI); - expect( Q.Matrix.toCsv( gate.matrix )).toBe( expected_result ); -}) - -//This test surprisingly&indirectly helped uncover a problem calculating live probability results :D -test(`updateMatrix$ for the unitary gate with parameters (phi = -π,` - + ` theta = π, lambda = -π)`, () => { - const gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( 'U' )); - - //creating expected matrix converted to a Csv string for comparison - const phi = -Math.PI; - const theta = Math.PI; - const lambda = -Math.PI; - 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 expected_result = Q.Matrix.toCsv( new Q.Matrix( - [a, b], - [c, d] - )) - - //updating the current gate with the values phi, theta, lambda - gate.updateMatrix$( phi, theta, lambda ) - expect( Q.Matrix.toCsv( gate.matrix )).toBe( expected_result ); -}); - -/* - * The following methods test simply the creation of the parameter input div when a valid gate operation - * in the circuit is doubleclicked. - * - */ -('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(( element ) => { - let operation = Q.Gate.prototype.clone( Q.Gate.findBySymbol( element ) ); - let test_message = "Check that gate operation with symbol [" + element + "] exists and is found by Q.Gate.findBySymbol()"; - test(test_message, () => { - expect(operation).toBeDefined(); - }) - let description = "Checking circuit set for operation [" + operation.name + "] is valid" - describe(description, () => { - document.body.innerHTML = - '
' + - '
' + - '
'; - let container = document.getElementById( 'container' ); - const circuit = new Q.Circuit(2, 2); - const parameters = {}; - Object.assign(parameters, operation.parameters); - const register_indices = operation.is_multi_qubit ? [1, 2] : [1]; - circuit.set$( operation.symbol, 1, register_indices, parameters ); - container.appendChild(circuit.toDom()); - const operationEl = (container.querySelector('.Q-circuit-operation' )); - test_message = "Check that the operation icon for operation [" + operation.name + "] exists" - test(test_message, () => { - expect(operationEl).toBeDefined(); - }) - - if( operation.is_multi_qubit ) { - test_message = "Check that multi-qubit gate operation [" + operation.name + "] takes up two register indices in the circuit"; - test(test_message, () => { - expect(operationEl.getAttribute( 'register-indices' ).split(',').length).toBe( 2 ); - }) - } - - if( operation.has_parameters ) { - const parameters = operation.parameters; - test_message = "Since operation [" + operation.name + "] 'has_parameters', operation.parameters should NOT be empty" - test(test_message, () => { - expect(parameters).toBeDefined(); - expect(Object.keys(parameters).length) - }) - - let clientRect = operationEl.getBoundingClientRect(); - let clientX = clientRect.left; - let clientY = clientRect.top; - const event = { - 'clientX' : clientX, - 'clientY' : clientY - } - Q.Circuit.Editor.onDoubleclick( event, operationEl ); - expect( document.querySelector( '.Q-parameters-box' ).style.display ).toBe('block'); - // Check that the correct number of textboxes were created. - // Setting the values 1 and 3 as the usual case is 1 parameter with the exception of the unitary gate. - // This can be generalized by using Object.keys(parameters).length but this is more exact for testing purposes. - const textboxes = document.querySelectorAll( '.Q-parameter-box-input' ); - test_message = "The number of parameter-input textboxes generated for operation [" + operation.name + "] on a double click must be " - + operation.symbol === 'U' ? '3' : '1'; - test(test_message, () => { - let expected_result = operation.symbol === 'U' ? 3 : 1; - expect( Object.keys( operation.parameters ).length ).toBe( expected_result ); - expect( Array.from( textboxes ).length ).toBe( expected_result ); - }) - } - }) -}) - -/* - * The following test checks various responses to the parameter textboxes to a change event. - * These tests can be generalized for input for any single-qubit operation. - * - */ - -describe("Checking the responses to adding different inputs to a parameter textbox for the PHASE operation", () => { - beforeEach(() => { - let operation = Q.Gate.findBySymbol( 'P' ); - document.body.innerHTML = - '
' + - '
' + - '
'; - let container = document.getElementById( 'container' ); - const circuit = new Q.Circuit(1, 1); - circuit.set$( operation.symbol, 1, [1], {'phi': 1} ); - container.appendChild(circuit.toDom()); - const operationEl = (container.querySelector('.Q-circuit-operation' )); - let clientRect = operationEl.getBoundingClientRect(); - let clientX = clientRect.left; - let clientY = clientRect.top; - const event = { - 'clientX' : clientX, - 'clientY' : clientY - } - Q.Circuit.Editor.onDoubleclick( event, operationEl ); - }) - test("Check that the textbox default value is equal to 1, the 'phi' value of the operation", () => { - const textbox = document.querySelector( '.Q-parameter-box-input' ); - expect( +( textbox.value ) ).toBe( 1 ); - }) - - test("Check that the textbox retains decimal/number ('1.234') values inputted after a change event", () => { - - const operationEl = (container.querySelector('.Q-circuit-operation' )); - const textbox = document.querySelector( '.Q-parameter-box-input' ); - textbox.value = '1.234'; - let event = new Event('change'); - textbox.dispatchEvent( event ); - expect( textbox.value ).toBe( '1.234' ); - expect( operationEl.getAttribute( 'phi' ) ).toBe( '1.234' ); - }) - - test("Check that the textbox removes invalid values ('4/2') inputted after a change event", () => { - const evaluate = require("mathjs"); - - const operationEl = (container.querySelector('.Q-circuit-operation' )); - const textbox = document.querySelector( '.Q-parameter-box-input' ); - textbox.value = '4/2'; - let event = new Event('change'); - textbox.dispatchEvent( event ); - expect( textbox.value ).toBe( '1' ); - expect( operationEl.getAttribute( 'phi' ) ).toBe( '1' ); - }) - - test("Check that the textbox removes invalid values ('1.23abcd') inputted after a change event", () => { - const operationEl = (container.querySelector('.Q-circuit-operation' )); - const textbox = document.querySelector( '.Q-parameter-box-input' ); - textbox.value = '1.23abcd'; - let event = new Event('change'); - textbox.dispatchEvent( event ); - expect( textbox.value ).toBe( '1' ); - expect( operationEl.getAttribute( 'phi' ) ).toBe( '1' ); - }) - - test("Check that the textbox removes invalid values ('1.23/0') inputted after a change event", () => { - const operationEl = (container.querySelector('.Q-circuit-operation' )); - const textbox = document.querySelector( '.Q-parameter-box-input' ); - textbox.value = '1.23/0'; - let event = new Event('change'); - textbox.dispatchEvent( event ); - expect( textbox.value ).toBe( '1' ); - expect( operationEl.getAttribute( 'phi' ) ).toBe( '1' ); - }) - -}) - -/* - * The following series of tests checks that the circuit and editor are updated upon exiting the parameter input box - * of a gate with a single parameter - * - */ - -describe("Exiting the parameter box after changing the parameter of a single-parameter gate (PHASE)", () => { - let operation = Q.Gate.findBySymbol( 'P' ); - document.body.innerHTML = - '
' + - '
' + - '
'; - let container = document.getElementById( 'container' ); - const circuit = new Q.Circuit(1, 1); - circuit.set$( operation.symbol, 1, [1], {'phi': 1} ); - container.appendChild(circuit.toDom()); - let operationEl = (container.querySelector('.Q-circuit-operation' )); - let clientRect = operationEl.getBoundingClientRect(); - let clientX = clientRect.left; - let clientY = clientRect.top; - let event = { - 'clientX' : clientX, - 'clientY' : clientY - } - // The following sequence is was proven valid by the previous tests. - Q.Circuit.Editor.onDoubleclick( event, operationEl ); - const textbox = document.querySelector( '.Q-parameter-box-input' ); - textbox.value = '1.234'; - textbox.dispatchEvent( new Event( 'change' ) ); - //Mimic an exit event - const exitButton = document.querySelector( '.Q-parameter-box-exit' ); - event = { - target: exitButton - } - Q.Circuit.Editor.onPointerPress( event ); - const parameterEl = document.querySelector( '.Q-parameters-box' ) - test("Check that pressing the exit button exits the parameter box", () => { - expect( parameterEl.style.display ).toBe('none'); - expect( parameterEl.innerHTML ).toBe( "" ); - }) - - //Checking that the circuit && editor were set with the updated values - test("Checking that PHASE circuit operation and editor tile were updated with the new 'phi' parameter", () => { - //checking for operationEl again as .Editor$ is destructive - operationEl = (container.querySelector('.Q-circuit-operation' )); - expect( operationEl.getAttribute( 'gate-symbol' ) ).toBe( 'P' ); - expect( operationEl.getAttribute( 'phi' )).toBe( '1.234' ); - expect( circuit.operations[0].gate.symbol ).toBe( 'P' ); - expect( circuit.operations[0].gate.parameters[ 'phi' ] ).toBe( 1.234 ); - }) -}) - diff --git a/packages/quantum-js-util/Logging.js b/packages/quantum-js-util/Logging.js deleted file mode 100644 index f05ebbd..0000000 --- a/packages/quantum-js-util/Logging.js +++ /dev/null @@ -1,89 +0,0 @@ -//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 }; diff --git a/packages/quantum-js-util/Math-Functions.js b/packages/quantum-js-util/Math-Functions.js deleted file mode 100644 index 498c381..0000000 --- a/packages/quantum-js-util/Math-Functions.js +++ /dev/null @@ -1,56 +0,0 @@ -//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 }; diff --git a/packages/quantum-js-util/Misc.js b/packages/quantum-js-util/Misc.js deleted file mode 100644 index 4eb824d..0000000 --- a/packages/quantum-js-util/Misc.js +++ /dev/null @@ -1,400 +0,0 @@ -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 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, dispatchEventToGlobal, constants }; diff --git a/packages/quantum-js-util/Q-Circuit.js b/packages/quantum-js-util/Q-Circuit.js deleted file mode 100644 index 9cba24c..0000000 --- a/packages/quantum-js-util/Q-Circuit.js +++ /dev/null @@ -1,2099 +0,0 @@ - -// 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.dispatchEventToGlobal(new CustomEvent( - - '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.dispatchEventToGlobal(new CustomEvent( '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.dispatchEventToGlobal(new CustomEvent( '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.dispatchEventToGlobal(new CustomEvent( - - '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.dispatchEventToGlobal(new CustomEvent( - - '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}; - diff --git a/packages/quantum-js-util/Q-ComplexNumber.js b/packages/quantum-js-util/Q-ComplexNumber.js deleted file mode 100644 index e978248..0000000 --- a/packages/quantum-js-util/Q-ComplexNumber.js +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - -const { warn, error } = require('./Logging'); -const mathf = require('./Math-Functions'); - -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 }; diff --git a/packages/quantum-js-util/Q-Gate.js b/packages/quantum-js-util/Q-Gate.js deleted file mode 100644 index 472dde1..0000000 --- a/packages/quantum-js-util/Q-Gate.js +++ /dev/null @@ -1,630 +0,0 @@ - -// 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 }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-History.js b/packages/quantum-js-util/Q-History.js deleted file mode 100644 index 1d38945..0000000 --- a/packages/quantum-js-util/Q-History.js +++ /dev/null @@ -1,196 +0,0 @@ - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - -const {dispatchEventToGlobal} = 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 ){ - - dispatchEventToGlobal(new CustomEvent( - - 'History undo is capable', { detail: { instance }} - )); - } - else { - - dispatchEventToGlobal(new CustomEvent( - - 'History undo is depleted', { detail: { instance }} - )) - } - if( this.index + 1 < this.entries.length ){ - - dispatchEventToGlobal(new CustomEvent( - - 'History redo is capable', { detail: { instance }} - )) - } - else { - - dispatchEventToGlobal(new CustomEvent( - - '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 }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-Matrix.js b/packages/quantum-js-util/Q-Matrix.js deleted file mode 100644 index 6f837c5..0000000 --- a/packages/quantum-js-util/Q-Matrix.js +++ /dev/null @@ -1,598 +0,0 @@ -// 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 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 }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-Qubit.js b/packages/quantum-js-util/Q-Qubit.js deleted file mode 100644 index 48c3f1e..0000000 --- a/packages/quantum-js-util/Q-Qubit.js +++ /dev/null @@ -1,349 +0,0 @@ -// 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 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 }; diff --git a/packages/quantum-js-util/Q.js b/packages/quantum-js-util/Q.js deleted file mode 100644 index a4ca1d5..0000000 --- a/packages/quantum-js-util/Q.js +++ /dev/null @@ -1,55 +0,0 @@ -// 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 () { - // Did we send arguments of the form - // ( bandwidth, timewidth )? - - if ( - arguments.length === 2 && - Array.from(arguments).every(function (argument) { - return 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 = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q}; - diff --git a/packages/quantum-js-util/__test__/Misc.test.js b/packages/quantum-js-util/__test__/Misc.test.js deleted file mode 100644 index 14589b9..0000000 --- a/packages/quantum-js-util/__test__/Misc.test.js +++ /dev/null @@ -1,5 +0,0 @@ -const misc = require('../Misc'); -// console.log(misc); - -test.only("Trivial", () => expect(1).toBe(1)); -console.log(misc.constants.EPSILON); \ No newline at end of file diff --git a/packages/quantum-js-util/__test__/Q-Circuit.test.js b/packages/quantum-js-util/__test__/Q-Circuit.test.js deleted file mode 100644 index 6f7c1f8..0000000 --- a/packages/quantum-js-util/__test__/Q-Circuit.test.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - *@jest-environment jsdom - */ -const {Circuit} = require('../Q-Circuit'); - -console.log(window == global ? "true": "false"); -window.addEventListener( 'Q.Circuit.evaluate completed', function( event ) { - console.log("event heard"); - console.log(event.detail.circuit.report$()); -}) -test('trivial', ()=> expect(1).toBe(1)); -test("creating a basic circuit with only a hadamard", () => { - let circuit = new Circuit(3, 3); - - circuit.set$('H', 1, 1); - // console.log(circuit.toDiagram()); - // console.log(circuit.toAmazonBraket()); - circuit.evaluate$(); - expect(1).toBe(1); -}) \ No newline at end of file diff --git a/packages/quantum-js-util/__test__/Q-ComplexNumber.test.js b/packages/quantum-js-util/__test__/Q-ComplexNumber.test.js deleted file mode 100644 index 8481793..0000000 --- a/packages/quantum-js-util/__test__/Q-ComplexNumber.test.js +++ /dev/null @@ -1,8 +0,0 @@ -const {ComplexNumber} = require('../Q-ComplexNumber'); -const {constants} = require('../Misc'); -console.log(ComplexNumber) -let num = new ComplexNumber(4, 0); -// console.log("constants: ", num.constants) -// console.log("misc.constants: ", constants); -console.log(ComplexNumber.E) -test("Trivial", () => expect(1).toBe(1)); diff --git a/packages/quantum-js-util/__test__/Q-Gate.test.js b/packages/quantum-js-util/__test__/Q-Gate.test.js deleted file mode 100644 index 14c0331..0000000 --- a/packages/quantum-js-util/__test__/Q-Gate.test.js +++ /dev/null @@ -1,4 +0,0 @@ -const {Gate} = require('../Q-Gate'); - -console.log(Gate.findBySymbol('P').name) -test("trivial", () => expect(1).toBe(1)); diff --git a/packages/quantum-js-util/__test__/Q-Matrix.test.js b/packages/quantum-js-util/__test__/Q-Matrix.test.js deleted file mode 100644 index 18f6a89..0000000 --- a/packages/quantum-js-util/__test__/Q-Matrix.test.js +++ /dev/null @@ -1,3 +0,0 @@ -const {Matrix} = require('../Q-Matrix'); -console.log(Matrix.constants['IDENTITY_2X2'].toTsv()); -test('trivial', () => expect(1).toBe(1)); \ No newline at end of file diff --git a/packages/quantum-js-util/__test__/Q-Qubit.test.js b/packages/quantum-js-util/__test__/Q-Qubit.test.js deleted file mode 100644 index ebcc13a..0000000 --- a/packages/quantum-js-util/__test__/Q-Qubit.test.js +++ /dev/null @@ -1,4 +0,0 @@ -const {Qubit} = require('../Q-Qubit'); - - -test('trivial', ()=> expect(1).toBe(1)); diff --git a/packages/quantum-js-util/__test__/Q.test.js b/packages/quantum-js-util/__test__/Q.test.js deleted file mode 100644 index c3a51ca..0000000 --- a/packages/quantum-js-util/__test__/Q.test.js +++ /dev/null @@ -1,2 +0,0 @@ -import {Q} from "../Q.mjs"; -test('trivial', ()=> expect(1).toBe(1)); \ No newline at end of file diff --git a/packages/quantum-js-util/index.js b/packages/quantum-js-util/index.js deleted file mode 100644 index c1cc69f..0000000 --- a/packages/quantum-js-util/index.js +++ /dev/null @@ -1 +0,0 @@ -const {Q} = require('./Q'); diff --git a/packages/quantum-js-util/package-lock.json b/packages/quantum-js-util/package-lock.json deleted file mode 100644 index 9a5f4af..0000000 --- a/packages/quantum-js-util/package-lock.json +++ /dev/null @@ -1,3062 +0,0 @@ -{ - "name": "quantum-js-util", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@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, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, - "@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 - }, - "@babel/core": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", - "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", - "dev": true, - "requires": { - "@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" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@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, - "requires": { - "@babel/types": "^7.14.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@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, - "requires": { - "@babel/compat-data": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - } - }, - "@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, - "requires": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@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, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@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, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@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, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@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, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@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 - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "@babel/types": "^7.14.8" - } - }, - "@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, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@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 - }, - "@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 - }, - "@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, - "requires": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" - } - }, - "@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, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "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, - "requires": { - "color-convert": "^1.9.0" - } - }, - "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, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "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, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "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, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@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 - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@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, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "@babel/helper-validator-identifier": "^7.14.8", - "to-fast-properties": "^2.0.0" - } - }, - "@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 - }, - "@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, - "requires": { - "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" - } - }, - "@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 - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "@jest/fake-timers": "^27.0.6", - "@jest/types": "^27.0.6", - "@types/node": "*", - "jest-mock": "^27.0.6" - } - }, - "@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, - "requires": { - "@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" - } - }, - "@jest/globals": { - "version": "27.0.6", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.0.6.tgz", - "integrity": "sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==", - "dev": true, - "requires": { - "@jest/environment": "^27.0.6", - "@jest/types": "^27.0.6", - "expect": "^27.0.6" - } - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@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, - "requires": { - "@jest/console": "^27.0.6", - "@jest/types": "^27.0.6", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@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, - "requires": { - "@jest/test-result": "^27.0.6", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.0.6", - "jest-runtime": "^27.0.6" - } - }, - "@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, - "requires": { - "@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" - } - }, - "@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, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@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, - "requires": { - "type-detect": "4.0.8" - } - }, - "@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, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@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 - }, - "@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, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@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, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@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, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@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, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@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, - "requires": { - "@types/node": "*" - } - }, - "@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 - }, - "@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, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@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, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@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 - }, - "@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 - }, - "@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 - }, - "@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, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@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 - }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", - "dev": true - }, - "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, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "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 - }, - "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, - "requires": { - "debug": "4" - } - }, - "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, - "requires": { - "type-fest": "^0.21.3" - } - }, - "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 - }, - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "babel-plugin-jest-hoist": "^27.0.6", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "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 - }, - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "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 - }, - "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "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, - "requires": { - "node-int64": "^0.4.0" - } - }, - "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 - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "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 - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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 - }, - "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 - }, - "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 - }, - "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, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "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 - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "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 - }, - "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, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "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, - "requires": { - "cross-spawn": "^7.0.1" - } - }, - "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, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "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, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "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, - "requires": { - "ms": "2.1.2" - } - }, - "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 - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "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 - }, - "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 - }, - "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 - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "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 - }, - "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, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "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 - }, - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "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" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expect": { - "version": "27.0.6", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz", - "integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==", - "dev": true, - "requires": { - "@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" - }, - "dependencies": { - "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 - } - } - }, - "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 - }, - "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 - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "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, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "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, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "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, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "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" - } - }, - "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 - }, - "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 - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "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 - }, - "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, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "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 - }, - "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, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "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, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "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 - }, - "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, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "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, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "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, - "requires": { - "ci-info": "^3.1.1" - } - }, - "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, - "requires": { - "has": "^1.0.3" - } - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "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 - }, - "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, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, - "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, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "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, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "27.0.6", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.0.6.tgz", - "integrity": "sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA==", - "dev": true, - "requires": { - "@jest/core": "^27.0.6", - "import-local": "^3.0.2", - "jest-cli": "^27.0.6" - }, - "dependencies": { - "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, - "requires": { - "@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" - } - } - } - }, - "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, - "requires": { - "@jest/types": "^27.0.6", - "execa": "^5.0.0", - "throat": "^6.0.1" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.0.6", - "jest-get-type": "^27.0.6", - "pretty-format": "^27.0.6" - } - }, - "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, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@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" - } - }, - "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 - }, - "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, - "requires": { - "@jest/types": "^27.0.6", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "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" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "jest-get-type": "^27.0.6", - "pretty-format": "^27.0.6" - } - }, - "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, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.0.6", - "jest-get-type": "^27.0.6", - "pretty-format": "^27.0.6" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@jest/types": "^27.0.6", - "@types/node": "*" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@jest/types": "^27.0.6", - "jest-regex-util": "^27.0.6", - "jest-snapshot": "^27.0.6" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - } - }, - "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, - "requires": { - "@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" - }, - "dependencies": { - "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" - } - } - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@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" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - } - } - }, - "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, - "requires": { - "@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" - } - }, - "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, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "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 - }, - "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, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsdom": { - "version": "16.6.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", - "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", - "dev": true, - "requires": { - "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" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "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, - "requires": { - "minimist": "^1.2.5" - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "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 - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "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, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "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, - "requires": { - "yallist": "^4.0.0" - } - }, - "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, - "requires": { - "semver": "^6.0.0" - } - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, - "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 - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "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 - }, - "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, - "requires": { - "mime-db": "1.49.0" - } - }, - "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 - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "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 - }, - "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-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-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 - }, - "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 - }, - "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 - }, - "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, - "requires": { - "path-key": "^3.0.0" - } - }, - "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 - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "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" - } - }, - "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 - }, - "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, - "requires": { - "p-try": "^2.0.0" - } - }, - "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, - "requires": { - "p-limit": "^2.2.0" - } - }, - "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 - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "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, - "requires": { - "find-up": "^4.0.0" - } - }, - "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 - }, - "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, - "requires": { - "@jest/types": "^27.0.6", - "ansi-regex": "^5.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "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 - } - } - }, - "prompts": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", - "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "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, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "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 - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "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 - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "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, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "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 - }, - "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 - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "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 - }, - "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, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "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, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "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, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "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, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "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, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - }, - "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, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "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 - }, - "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, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "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, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", - "dev": true - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "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 - }, - "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, - "requires": { - "is-number": "^7.0.0" - } - }, - "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, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "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 - }, - "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, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "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 - } - } - }, - "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, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "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, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, - "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 - }, - "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, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "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 - }, - "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, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "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 - }, - "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, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "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, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "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 - }, - "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 - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "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" - } - }, - "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 - } - } -} diff --git a/packages/quantum-js-util/package.json b/packages/quantum-js-util/package.json deleted file mode 100644 index 36b51ed..0000000 --- a/packages/quantum-js-util/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "quantum-js-util", - "version": "1.0.0", - "description": "", - "main": "Q.js", - "scripts": { - "test": "echo 'hello from util!'" - }, - "devDependencies": { - "cross-env": "^7.0.3", - "eslint": "^7.31.0", - "jest": "^27.0.6", - "jsdom": "^16.6.0", - "prettier": "2.3.2" - }, - "dependencies": {}, - "keywords": [], - "author": "", - "license": "ISC" -} diff --git a/packages/quantum-js-vis/Q-BlochSphere.js b/packages/quantum-js-vis/Q-BlochSphere.js deleted file mode 100644 index af0d3bd..0000000 --- a/packages/quantum-js-vis/Q-BlochSphere.js +++ /dev/null @@ -1,582 +0,0 @@ - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.BlochSphere = function( onValueChange ){ - - Object.assign( this, { - - isRotating: false, - radius: 1, - radiusSafe: 1.01, - axesLineWidth: 0.01, - arcLineWidth: 0.015, - state: Q.Qubit.LEFT_HAND_CIRCULAR_POLARIZED.toBlochSphere(), - target: Q.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: Q.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: Q.BlochSphere.xAxisColor }) - ), - yAxis = new THREE.Mesh( - - new THREE.BoxGeometry( this.radius * 2.5, this.axesLineWidth, this.axesLineWidth ), - new THREE.MeshBasicMaterial({ color: Q.BlochSphere.yAxisColor }) - ), - zAxis = new THREE.Mesh( - - new THREE.BoxGeometry( this.axesLineWidth, this.radius * 2.5, this.axesLineWidth ), - new THREE.MeshBasicMaterial({ color: Q.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, - Q.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, - Q.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, - Q.BlochSphere.zAxisColor,// Blue - arrowHeadLength, - arrowHeadWidth - )) - - - // Create the X, Y, and Z axis labels. - - const - axesLabelStyle = { - - width: 128, - height: 128, - fillStyle: Q.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: Q.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: Q.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 = Q.BlochSphere.createLatitudeArc( arcR, 128, Math.PI / 2, Math.PI * 2 ), - thetaLine = new MeshLine(), - thetaPhiMaterial = new MeshLineMaterial({ - - color: Q.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 = Q.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. - - Q.BlochSphere.prototype.setTargetState.call( this ) -} - - - - - - - //////////////// - // // - // Static // - // // -//////////////// - - -Object.assign( Q.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 = Q.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 = Q.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( Q.BlochSphere.prototype, { - - update: function(){ - - if( this.isRotating ) this.group.rotation.y += Math.PI / 4096 - }, - setTargetState: function( target ){ - - if( target === undefined ) target = Q.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 ) - } - } -}) - - - - - - - diff --git a/packages/quantum-js-vis/Q-Circuit-Editor.css b/packages/quantum-js-vis/Q-Circuit-Editor.css deleted file mode 100644 index d4c5381..0000000 --- a/packages/quantum-js-vis/Q-Circuit-Editor.css +++ /dev/null @@ -1,899 +0,0 @@ -/* - - 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 { - - 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 deleted file mode 100644 index 98bf717..0000000 --- a/packages/quantum-js-vis/Q-Circuit-Editor.js +++ /dev/null @@ -1,2281 +0,0 @@ -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. -const {Q, Circuit, Gate, logger, misc } = 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 - } -}) - - - - - - - ///////////////////////// - // // - // 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( !misc.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 - misc.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 Q.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' )) - console.log("param") - //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/__test__/Q-Circuit-Editor.test.js b/packages/quantum-js-vis/__test__/Q-Circuit-Editor.test.js deleted file mode 100644 index ba54036..0000000 --- a/packages/quantum-js-vis/__test__/Q-Circuit-Editor.test.js +++ /dev/null @@ -1,6 +0,0 @@ -/* - *@jest-environment jsdom - * - */ -Editor = require('../Q-Circuit-Editor'); -test("trivial", ()=> expect(1).toBe(1)); \ No newline at end of file diff --git a/packages/quantum-js-vis/index.js b/packages/quantum-js-vis/index.js deleted file mode 100644 index ae12a1a..0000000 --- a/packages/quantum-js-vis/index.js +++ /dev/null @@ -1,6 +0,0 @@ -const {Editor} = require('./Q-Circuit-Editor'); - - -console.log("Welcome to Q.js! The GUI experience!"); - -module.exports = {Editor}; \ No newline at end of file diff --git a/packages/quantum-js-vis/package-lock.json b/packages/quantum-js-vis/package-lock.json deleted file mode 100644 index 95e01ab..0000000 --- a/packages/quantum-js-vis/package-lock.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "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 deleted file mode 100644 index dda1527..0000000 --- a/packages/quantum-js-vis/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "quantum-js-vis", - "version": "1.0.0", - "description": "Visualization Components for Q.js", - "main": "index.js", - "scripts": { - "test": "jest" - }, - "keywords": [], - "author": "", - "license": "ISC", - "devDependencies": { - "prettier": "^2.3.2" - } -} From 05e107f1e745e771161b86f89fd0f074312e950f Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Sun, 1 Aug 2021 00:28:25 +0000 Subject: [PATCH 40/41] Create quantum-js-util package --- packages/quantum-js-util/Logging.js | 89 + packages/quantum-js-util/Math-Functions.js | 56 + packages/quantum-js-util/Misc.js | 400 +++ packages/quantum-js-util/Q-Circuit.js | 2099 +++++++++++++ packages/quantum-js-util/Q-ComplexNumber.js | 586 ++++ packages/quantum-js-util/Q-Gate.js | 630 ++++ packages/quantum-js-util/Q-History.js | 196 ++ packages/quantum-js-util/Q-Matrix.js | 598 ++++ packages/quantum-js-util/Q-Qubit.js | 349 +++ packages/quantum-js-util/Q.js | 55 + packages/quantum-js-util/index.js | 1 + packages/quantum-js-util/package-lock.json | 3062 +++++++++++++++++++ packages/quantum-js-util/package.json | 21 + 13 files changed, 8142 insertions(+) create mode 100644 packages/quantum-js-util/Logging.js create mode 100644 packages/quantum-js-util/Math-Functions.js create mode 100644 packages/quantum-js-util/Misc.js create mode 100644 packages/quantum-js-util/Q-Circuit.js create mode 100644 packages/quantum-js-util/Q-ComplexNumber.js create mode 100644 packages/quantum-js-util/Q-Gate.js create mode 100644 packages/quantum-js-util/Q-History.js create mode 100644 packages/quantum-js-util/Q-Matrix.js create mode 100644 packages/quantum-js-util/Q-Qubit.js create mode 100644 packages/quantum-js-util/Q.js create mode 100644 packages/quantum-js-util/index.js create mode 100644 packages/quantum-js-util/package-lock.json create mode 100644 packages/quantum-js-util/package.json diff --git a/packages/quantum-js-util/Logging.js b/packages/quantum-js-util/Logging.js new file mode 100644 index 0000000..f05ebbd --- /dev/null +++ b/packages/quantum-js-util/Logging.js @@ -0,0 +1,89 @@ +//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 }; diff --git a/packages/quantum-js-util/Math-Functions.js b/packages/quantum-js-util/Math-Functions.js new file mode 100644 index 0000000..498c381 --- /dev/null +++ b/packages/quantum-js-util/Math-Functions.js @@ -0,0 +1,56 @@ +//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 }; diff --git a/packages/quantum-js-util/Misc.js b/packages/quantum-js-util/Misc.js new file mode 100644 index 0000000..4eb824d --- /dev/null +++ b/packages/quantum-js-util/Misc.js @@ -0,0 +1,400 @@ +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 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, dispatchEventToGlobal, constants }; diff --git a/packages/quantum-js-util/Q-Circuit.js b/packages/quantum-js-util/Q-Circuit.js new file mode 100644 index 0000000..9cba24c --- /dev/null +++ b/packages/quantum-js-util/Q-Circuit.js @@ -0,0 +1,2099 @@ + +// 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.dispatchEventToGlobal(new CustomEvent( + + '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.dispatchEventToGlobal(new CustomEvent( '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.dispatchEventToGlobal(new CustomEvent( '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.dispatchEventToGlobal(new CustomEvent( + + '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.dispatchEventToGlobal(new CustomEvent( + + '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}; + diff --git a/packages/quantum-js-util/Q-ComplexNumber.js b/packages/quantum-js-util/Q-ComplexNumber.js new file mode 100644 index 0000000..79181da --- /dev/null +++ b/packages/quantum-js-util/Q-ComplexNumber.js @@ -0,0 +1,586 @@ +// 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 }; diff --git a/packages/quantum-js-util/Q-Gate.js b/packages/quantum-js-util/Q-Gate.js new file mode 100644 index 0000000..472dde1 --- /dev/null +++ b/packages/quantum-js-util/Q-Gate.js @@ -0,0 +1,630 @@ + +// 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 }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-History.js b/packages/quantum-js-util/Q-History.js new file mode 100644 index 0000000..1d38945 --- /dev/null +++ b/packages/quantum-js-util/Q-History.js @@ -0,0 +1,196 @@ + +// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. + +const {dispatchEventToGlobal} = 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 ){ + + dispatchEventToGlobal(new CustomEvent( + + 'History undo is capable', { detail: { instance }} + )); + } + else { + + dispatchEventToGlobal(new CustomEvent( + + 'History undo is depleted', { detail: { instance }} + )) + } + if( this.index + 1 < this.entries.length ){ + + dispatchEventToGlobal(new CustomEvent( + + 'History redo is capable', { detail: { instance }} + )) + } + else { + + dispatchEventToGlobal(new CustomEvent( + + '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 }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-Matrix.js b/packages/quantum-js-util/Q-Matrix.js new file mode 100644 index 0000000..6f837c5 --- /dev/null +++ b/packages/quantum-js-util/Q-Matrix.js @@ -0,0 +1,598 @@ +// 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 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 }; \ No newline at end of file diff --git a/packages/quantum-js-util/Q-Qubit.js b/packages/quantum-js-util/Q-Qubit.js new file mode 100644 index 0000000..48c3f1e --- /dev/null +++ b/packages/quantum-js-util/Q-Qubit.js @@ -0,0 +1,349 @@ +// 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 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 }; diff --git a/packages/quantum-js-util/Q.js b/packages/quantum-js-util/Q.js new file mode 100644 index 0000000..a4ca1d5 --- /dev/null +++ b/packages/quantum-js-util/Q.js @@ -0,0 +1,55 @@ +// 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 () { + // Did we send arguments of the form + // ( bandwidth, timewidth )? + + if ( + arguments.length === 2 && + Array.from(arguments).every(function (argument) { + return 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 = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q}; + diff --git a/packages/quantum-js-util/index.js b/packages/quantum-js-util/index.js new file mode 100644 index 0000000..c1cc69f --- /dev/null +++ b/packages/quantum-js-util/index.js @@ -0,0 +1 @@ +const {Q} = require('./Q'); diff --git a/packages/quantum-js-util/package-lock.json b/packages/quantum-js-util/package-lock.json new file mode 100644 index 0000000..9a5f4af --- /dev/null +++ b/packages/quantum-js-util/package-lock.json @@ -0,0 +1,3062 @@ +{ + "name": "quantum-js-util", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@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, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@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 + }, + "@babel/core": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", + "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@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, + "requires": { + "@babel/compat-data": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + } + }, + "@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, + "requires": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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 + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.8" + } + }, + "@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, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@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 + }, + "@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 + }, + "@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, + "requires": { + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.8", + "@babel/types": "^7.14.8" + } + }, + "@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, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "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, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "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, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "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, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@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 + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@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, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/template": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", + "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@babel/helper-validator-identifier": "^7.14.8", + "to-fast-properties": "^2.0.0" + } + }, + "@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 + }, + "@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, + "requires": { + "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" + } + }, + "@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 + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@jest/fake-timers": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/node": "*", + "jest-mock": "^27.0.6" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@jest/globals": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.0.6.tgz", + "integrity": "sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==", + "dev": true, + "requires": { + "@jest/environment": "^27.0.6", + "@jest/types": "^27.0.6", + "expect": "^27.0.6" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + } + }, + "@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, + "requires": { + "@jest/console": "^27.0.6", + "@jest/types": "^27.0.6", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@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, + "requires": { + "@jest/test-result": "^27.0.6", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.0.6", + "jest-runtime": "^27.0.6" + } + }, + "@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, + "requires": { + "@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" + } + }, + "@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, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@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, + "requires": { + "type-detect": "4.0.8" + } + }, + "@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, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@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 + }, + "@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, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@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, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@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, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@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, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@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, + "requires": { + "@types/node": "*" + } + }, + "@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 + }, + "@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, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@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, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@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 + }, + "@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 + }, + "@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 + }, + "@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, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@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 + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true + }, + "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, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "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 + }, + "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, + "requires": { + "debug": "4" + } + }, + "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, + "requires": { + "type-fest": "^0.21.3" + } + }, + "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 + }, + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "babel-plugin-jest-hoist": "^27.0.6", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "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 + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "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 + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "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, + "requires": { + "node-int64": "^0.4.0" + } + }, + "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 + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "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 + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "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 + }, + "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, + "requires": { + "color-name": "~1.1.4" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "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 + }, + "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, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "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, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "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, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "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, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "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, + "requires": { + "ms": "2.1.2" + } + }, + "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 + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "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 + }, + "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 + }, + "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 + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "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 + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "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" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expect": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz", + "integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "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 + } + } + }, + "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 + }, + "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 + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "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, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "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, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "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, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "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 + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "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 + }, + "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, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "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 + }, + "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, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "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, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "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 + }, + "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, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "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, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "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, + "requires": { + "ci-info": "^3.1.1" + } + }, + "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, + "requires": { + "has": "^1.0.3" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "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 + }, + "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, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + } + }, + "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, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "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, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "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, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.0.6.tgz", + "integrity": "sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA==", + "dev": true, + "requires": { + "@jest/core": "^27.0.6", + "import-local": "^3.0.2", + "jest-cli": "^27.0.6" + }, + "dependencies": { + "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, + "requires": { + "@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" + } + } + } + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + } + }, + "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, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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 + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + } + }, + "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, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.0.6", + "jest-get-type": "^27.0.6", + "pretty-format": "^27.0.6" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "@types/node": "*" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "jest-regex-util": "^27.0.6", + "jest-snapshot": "^27.0.6" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "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, + "requires": { + "@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" + }, + "dependencies": { + "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" + } + } + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@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" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + } + } + }, + "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, + "requires": { + "@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" + } + }, + "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, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "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, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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 + }, + "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, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", + "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", + "dev": true, + "requires": { + "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" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "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, + "requires": { + "minimist": "^1.2.5" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "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 + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "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, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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, + "requires": { + "yallist": "^4.0.0" + } + }, + "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, + "requires": { + "semver": "^6.0.0" + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "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 + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "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 + }, + "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, + "requires": { + "mime-db": "1.49.0" + } + }, + "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 + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "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 + }, + "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-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-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 + }, + "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 + }, + "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 + }, + "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, + "requires": { + "path-key": "^3.0.0" + } + }, + "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 + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "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, + "requires": { + "p-try": "^2.0.0" + } + }, + "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, + "requires": { + "p-limit": "^2.2.0" + } + }, + "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 + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "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, + "requires": { + "find-up": "^4.0.0" + } + }, + "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 + }, + "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, + "requires": { + "@jest/types": "^27.0.6", + "ansi-regex": "^5.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "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 + } + } + }, + "prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "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, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "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 + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "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 + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, + "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 + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "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, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "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, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "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, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "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, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "has-flag": "^4.0.0" + } + }, + "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, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "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 + }, + "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, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "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, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "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 + }, + "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, + "requires": { + "is-number": "^7.0.0" + } + }, + "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, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "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 + }, + "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 + }, + "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, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "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 + }, + "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, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "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 + } + } + }, + "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, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "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, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "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 + }, + "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, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "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 + }, + "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, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "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 + }, + "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, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "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, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "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 + }, + "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 + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "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" + } + }, + "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 + } + } +} diff --git a/packages/quantum-js-util/package.json b/packages/quantum-js-util/package.json new file mode 100644 index 0000000..55c7fe1 --- /dev/null +++ b/packages/quantum-js-util/package.json @@ -0,0 +1,21 @@ +{ + "name": "quantum-js-util", + "version": "1.0.0", + "description": "", + "main": "Q.js", + "scripts": { + "test": "jest", + "prettier": "echo 'I am a prettier util!' && exit 0" + }, + "devDependencies": { + "cross-env": "^7.0.3", + "eslint": "^7.31.0", + "jest": "^27.0.6", + "jsdom": "^16.6.0", + "prettier": "2.3.2" + }, + "dependencies": {}, + "keywords": [], + "author": "", + "license": "ISC" +} From 9367d0a892a10e657aee6fba5893d919084fcd56 Mon Sep 17 00:00:00 2001 From: Altan Nagji Date: Sun, 1 Aug 2021 00:31:59 +0000 Subject: [PATCH 41/41] Remove Q folder. Replacing with packages. --- Q/Q-BlochSphere.js | 582 ---------- Q/Q-Circuit-Editor.css | 898 ---------------- Q/Q-Circuit-Editor.js | 2281 ---------------------------------------- Q/Q-Circuit.js | 2089 ------------------------------------ Q/Q-ComplexNumber.js | 721 ------------- Q/Q-Gate.js | 299 ------ Q/Q-History.js | 194 ---- Q/Q-Matrix.js | 661 ------------ Q/Q-Qubit.js | 380 ------- Q/Q.css | 475 --------- Q/Q.js | 621 ----------- 11 files changed, 9201 deletions(-) delete mode 100644 Q/Q-BlochSphere.js delete mode 100644 Q/Q-Circuit-Editor.css delete mode 100644 Q/Q-Circuit-Editor.js delete mode 100644 Q/Q-Circuit.js delete mode 100644 Q/Q-ComplexNumber.js delete mode 100644 Q/Q-Gate.js delete mode 100644 Q/Q-History.js delete mode 100644 Q/Q-Matrix.js delete mode 100644 Q/Q-Qubit.js delete mode 100644 Q/Q.css delete mode 100644 Q/Q.js diff --git a/Q/Q-BlochSphere.js b/Q/Q-BlochSphere.js deleted file mode 100644 index af0d3bd..0000000 --- a/Q/Q-BlochSphere.js +++ /dev/null @@ -1,582 +0,0 @@ - -// Copyright © 2019–2020, Stewart Smith. See LICENSE for details. - - - - -Q.BlochSphere = function( onValueChange ){ - - Object.assign( this, { - - isRotating: false, - radius: 1, - radiusSafe: 1.01, - axesLineWidth: 0.01, - arcLineWidth: 0.015, - state: Q.Qubit.LEFT_HAND_CIRCULAR_POLARIZED.toBlochSphere(), - target: Q.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: Q.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: Q.BlochSphere.xAxisColor }) - ), - yAxis = new THREE.Mesh( - - new THREE.BoxGeometry( this.radius * 2.5, this.axesLineWidth, this.axesLineWidth ), - new THREE.MeshBasicMaterial({ color: Q.BlochSphere.yAxisColor }) - ), - zAxis = new THREE.Mesh( - - new THREE.BoxGeometry( this.axesLineWidth, this.radius * 2.5, this.axesLineWidth ), - new THREE.MeshBasicMaterial({ color: Q.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, - Q.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, - Q.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, - Q.BlochSphere.zAxisColor,// Blue - arrowHeadLength, - arrowHeadWidth - )) - - - // Create the X, Y, and Z axis labels. - - const - axesLabelStyle = { - - width: 128, - height: 128, - fillStyle: Q.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: Q.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: Q.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 = Q.BlochSphere.createLatitudeArc( arcR, 128, Math.PI / 2, Math.PI * 2 ), - thetaLine = new MeshLine(), - thetaPhiMaterial = new MeshLineMaterial({ - - color: Q.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 = Q.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. - - Q.BlochSphere.prototype.setTargetState.call( this ) -} - - - - - - - //////////////// - // // - // Static // - // // -//////////////// - - -Object.assign( Q.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 = Q.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 = Q.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( Q.BlochSphere.prototype, { - - update: function(){ - - if( this.isRotating ) this.group.rotation.y += Math.PI / 4096 - }, - setTargetState: function( target ){ - - if( target === undefined ) target = Q.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 ) - } - } -}) - - - - - - - diff --git a/Q/Q-Circuit-Editor.css b/Q/Q-Circuit-Editor.css deleted file mode 100644 index c891f94..0000000 --- a/Q/Q-Circuit-Editor.css +++ /dev/null @@ -1,898 +0,0 @@ -/* - - 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 { - - 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/Q/Q-Circuit-Editor.js b/Q/Q-Circuit-Editor.js deleted file mode 100644 index 7247204..0000000 --- a/Q/Q-Circuit-Editor.js +++ /dev/null @@ -1,2281 +0,0 @@ -// 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' ] - } - }, - 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 = 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?) - - 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 ] - }) - //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 ) - 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. - - 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 ) { - 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 = Q.Circuit.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' )) - console.log("param") - //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 = Q.Circuit.Editor.createNewElement( "span", input_fields, 'Q-parameter-input-label' ) - label.appendChild(document.createTextNode( element )) - - const textbox = Q.Circuit.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 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 ) diff --git a/Q/Q-Circuit.js b/Q/Q-Circuit.js deleted file mode 100644 index 30d315d..0000000 --- a/Q/Q-Circuit.js +++ /dev/null @@ -1,2089 +0,0 @@ - -// 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 - -aws_account_id = boto3.client("sts").get_caller_identity()["Account"] -device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/SIMULATOR_NAME)` -//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. -+ `\ns3_folder = (f"braket-output-{aws_account_id}", "folder-name")\n\n` - 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!!! - 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- - // `) -) - - - - diff --git a/Q/Q-ComplexNumber.js b/Q/Q-ComplexNumber.js deleted file mode 100644 index be320bc..0000000 --- a/Q/Q-ComplexNumber.js +++ /dev/null @@ -1,721 +0,0 @@ - -// 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 )) - } -}) - - diff --git a/Q/Q-Gate.js b/Q/Q-Gate.js deleted file mode 100644 index 41d3337..0000000 --- a/Q/Q-Gate.js +++ /dev/null @@ -1,299 +0,0 @@ - -// 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() - - - // 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 - }) - - 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 ]) - }), - '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 ]) - }), - 'PAULI_Z', new Q.Gate({ - - symbol: 'Z', - symbolAmazonBraket: 'z', - symbolSvg: '', - name: 'Pauli Z', - nameCss: 'pauli-z', - matrix: new Q.Matrix( - [ 1, 0 ], - [ 0, -1 ]) - }), - 'PHASE', new Q.Gate({ - - symbol: 'P', - symbolAmazonBraket: 'p',// !!! Double check this !!! - symbolSvg: '', - name: 'Phase', - nameCss: 'phase', - phi: 1, - updateMatrix$: function( phi ){ - - if( Q.isUsefulNumber( phi ) === true ) this.phi = phi - this.matrix = new Q.Matrix( - [ 1, 0 ], - [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.phi ))]) - return this - }, - applyToQubit: function( qubit, phi ){ - - if( Q.isUsefulNumber( phi ) !== true ) phi = this.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 )) - } - }), - '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. - } - }), - - - // Operate on 2 qubits. - - 'SWAP', new Q.Gate({ - - symbol: 'S', - symbolAmazonBraket: 's',// !!! Double check this !!! - 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 ]) - }), - '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 ]) - }) - /* - - - All further gates, - such as Toffoli (CCNOT) - or Fredkin (CSWAP) - can be easily constructed - from the above gates - using Q conveniences. - - - */ -) - - diff --git a/Q/Q-History.js b/Q/Q-History.js deleted file mode 100644 index df88c75..0000000 --- a/Q/Q-History.js +++ /dev/null @@ -1,194 +0,0 @@ - -// 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 ) - } -}) - - diff --git a/Q/Q-Matrix.js b/Q/Q-Matrix.js deleted file mode 100644 index af3a293..0000000 --- a/Q/Q-Matrix.js +++ /dev/null @@ -1,661 +0,0 @@ - -// 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 ]) -) - - diff --git a/Q/Q-Qubit.js b/Q/Q-Qubit.js deleted file mode 100644 index 6c32cb9..0000000 --- a/Q/Q-Qubit.js +++ /dev/null @@ -1,380 +0,0 @@ - -// 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 )) - }, -}) - - diff --git a/Q/Q.css b/Q/Q.css deleted file mode 100644 index 3f28f02..0000000 --- a/Q/Q.css +++ /dev/null @@ -1,475 +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: ''; -} - - - - - - - diff --git a/Q/Q.js b/Q/Q.js deleted file mode 100644 index 0592f38..0000000 --- a/Q/Q.js +++ /dev/null @@ -1,621 +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 - - - -` ) - - -module.exports = Q