diff --git a/.gitignore b/.gitignore
index b90121d..b85986f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
notes.txt
.DS_Store
node_modules
+coverage
+__test__
\ No newline at end of file
diff --git a/Q-Circuit.html b/Q-Circuit.html
index a64b2b2..c5c5e77 100644
--- a/Q-Circuit.html
+++ b/Q-Circuit.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
diff --git a/Q-ComplexNumber.html b/Q-ComplexNumber.html
index 011efaf..144e3a1 100644
--- a/Q-ComplexNumber.html
+++ b/Q-ComplexNumber.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
@@ -1096,17 +1089,17 @@
Maths (destructive)
-// console.log( '\n\nQ.ComplexNumber\n\n', Q.ComplexNumber.help(), '\n\n' )
+// console.log( '\n\nComplexNumber\n\n', ComplexNumber.help(), '\n\n' )
var
-cat = new Q.ComplexNumber( 1, 2 ),
-dog = new Q.ComplexNumber( 3, -4 ),
-i = new Q.ComplexNumber( 0, 1 )
+cat = new ComplexNumber( 1, 2 ),
+dog = new ComplexNumber( 3, -4 ),
+i = new ComplexNumber( 0, 1 )
var
-ape = new Q.ComplexNumber(),
-bee = new Q.ComplexNumber( 1 ),
-elk = new Q.ComplexNumber( 1, 2 ),
+ape = new ComplexNumber(),
+bee = new ComplexNumber( 1 ),
+elk = new ComplexNumber( 1, 2 ),
fox = elk.clone()// We’re avoiding a warning here: new Q .ComplexNumber( cat ),
diff --git a/Q-Gate.html b/Q-Gate.html
index 172620b..0e799c8 100644
--- a/Q-Gate.html
+++ b/Q-Gate.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
@@ -1265,18 +1258,18 @@ Prototype properties
-var gup = new Q.Gate({
+var gup = new Gate({
symbol: 'G',
name: 'Gup',
nameCss: 'gup',
- matrix: Q.Gate.PAULI_X.matrix
+ matrix: Gate.PAULI_X.matrix
})
-var fox = Q.Gate.PHASE.clone({ symbol: 'F', phi: Math.PI })
+var fox = Gate.PHASE.clone({ symbol: 'F', phi: Math.PI })
diff --git a/Q-Matrix.html b/Q-Matrix.html
index 5d80b14..946d9ce 100644
--- a/Q-Matrix.html
+++ b/Q-Matrix.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
@@ -1250,7 +1243,7 @@ Maths operations (destructive)
// First, what do the docs have to say?
-// console.log( '\n\nQ.Matrix\n\n', Q.Matrix.help(), '\n\n' )
+// console.log( '\n\nMatrix\n\n', Matrix.help(), '\n\n' )
// We’re going to use `var` here instead of `let` or `const`
@@ -1258,7 +1251,7 @@ Maths operations (destructive)
-var a = new Q.Matrix(
+var a = new Matrix(
[ 1, 2, 3 ],
[ 4, 5, 6 ]
@@ -1283,22 +1276,22 @@ Maths operations (destructive)
/*
-console.log( '\n\nQ.Matrix\n', Q.extractDocumentation( Q.Matrix ), '\n\n' )
-console.log( '\n\nQ.Matrix.prototype.multiply\n', Q.extractDocumentation( Q.Matrix.prototype.multiply ), '\n\n' )
+console.log( '\n\nMatrix\n', Q.extractDocumentation( Matrix ), '\n\n' )
+console.log( '\n\nMatrix.prototype.multiply\n', Q.extractDocumentation( Matrix.prototype.multiply ), '\n\n' )
-console.log( 'Q.Matrix.IDENTITY_2X2', Q.Matrix.IDENTITY_2X2 )
-console.log( 'Q.Matrix.IDENTITY_3X3', Q.Matrix.IDENTITY_3X3 )
-console.log( 'Q.Matrix.IDENTITY_4X4', Q.Matrix.IDENTITY_4X4 )
+console.log( 'Matrix.IDENTITY_2X2', Matrix.IDENTITY_2X2 )
+console.log( 'Matrix.IDENTITY_3X3', Matrix.IDENTITY_3X3 )
+console.log( 'Matrix.IDENTITY_4X4', Matrix.IDENTITY_4X4 )
-console.log( 'Q.Matrix.CNOT', Q.Matrix.CNOT )
-console.log( '\nQ.Matrix.CNOT.toHtml()', Q.Matrix.CNOT.toHTML(), '\n\n' )
-console.log( '\nQ.Matrix.TEST_MAP_9X9.toCSV()', Q.Matrix.TEST_MAP_9X9.toCSV(), '\n\n' )
-console.log( '\nQ.Matrix.TEST_MAP_9X9.toTsv()', Q.Matrix.TEST_MAP_9X9.toTsv(), '\n\n' )
+console.log( 'Matrix.CNOT', Matrix.CNOT )
+console.log( '\nMatrix.CNOT.toHtml()', Matrix.CNOT.toHTML(), '\n\n' )
+console.log( '\nMatrix.TEST_MAP_9X9.toCSV()', Matrix.TEST_MAP_9X9.toCSV(), '\n\n' )
+console.log( '\nMatrix.TEST_MAP_9X9.toTsv()', Matrix.TEST_MAP_9X9.toTsv(), '\n\n' )
-console.log( 'How many matrices have we created?', Q.Matrix.index )
+console.log( 'How many matrices have we created?', Matrix.index )
*/
@@ -1357,8 +1350,8 @@ Maths operations (destructive)
[ 3, 3, 3 ])
-console.log( 'Q.Matrix.IDENTITY_2X2.multiply( e ).toTsv()', Q.Matrix.IDENTITY_2X2.multiply( e ))
-console.log( 'Q.Matrix.IDENTITY_3X3.multiply( c ).toTsv()', Q.Matrix.IDENTITY_3X3.multiply( c ))
+console.log( 'Matrix.IDENTITY_2X2.multiply( e ).toTsv()', Matrix.IDENTITY_2X2.multiply( e ))
+console.log( 'Matrix.IDENTITY_3X3.multiply( c ).toTsv()', Matrix.IDENTITY_3X3.multiply( c ))
*/
@@ -1383,14 +1376,14 @@ Maths operations (destructive)
// Import and export formats.
-var csv = Q.Matrix.fromCsv(`
+var csv = Matrix.fromCsv(`
1, 2, 3
4, 5, 6
7, 8, 9`)
// console.log( 'Matrix from CSV', csv.toTsv(), '\n\n' )
-var tsv = Q.Matrix.fromTsv(`1 2 3
+var tsv = Matrix.fromTsv(`1 2 3
4 5 6
7 8 9`)
// console.log( 'Matrix from TSV', tsv.toTsv(), '\n\n' )
@@ -1415,7 +1408,7 @@ Maths operations (destructive)
`
-// console.log( 'Matrix from HTML', Q.Matrix.fromHtml( html ).toTsv(), '\n\n' )
+// console.log( 'Matrix from HTML', Matrix.fromHtml( html ).toTsv(), '\n\n' )
diff --git a/Q-Qubit.html b/Q-Qubit.html
index 01f6e18..5df02cd 100644
--- a/Q-Qubit.html
+++ b/Q-Qubit.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
@@ -61,7 +54,6 @@
-
@@ -1885,7 +1877,7 @@ Destructive methods
// Examples created in the docs:
-var fox = new Q.Qubit( 1, 0 )
+var fox = new Qubit( 1, 0 )
@@ -1958,7 +1950,7 @@ Destructive methods
camera.add( light )
scene.add( new THREE.AmbientLight( 0xFFFFFF, 0.7 ))
-const blochSphere = new Q.BlochSphere( function(){
+const blochSphere = new BlochSphere( function(){
document
.getElementById( 'bloch-theta' )
@@ -2033,7 +2025,7 @@ Destructive methods
'HDLARV'.split( '' ).forEach( function( symbol ){
const
- qubit = Q.Qubit.findBySymbol( symbol ),
+ qubit = Qubit.findBySymbol( symbol ),
qubitElement = document.createElement( 'div' ),
qubitGutsElement = document.createElement( 'div' )
@@ -2069,7 +2061,7 @@ Destructive methods
blochSphere.group.rotation.x = Math.PI * 0.3
blochSphere.group.rotation.y = Math.PI / -4
-selectBlochQubit( Q.Qubit.HORIZONTAL )
+selectBlochQubit( Qubit.HORIZONTAL )
render()
diff --git a/Q.html b/Q.html
index 3dc569f..b00ba31 100644
--- a/Q.html
+++ b/Q.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
diff --git a/build/bundle.css b/build/bundle.css
new file mode 100644
index 0000000..ba23b8d
--- /dev/null
+++ b/build/bundle.css
@@ -0,0 +1,2232 @@
+/*
+
+ 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 );
+}
+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;
+ padding: 1em;
+ font-family: var( --Q-font-family-mono );
+ font-weight: 300;
+ line-height: 1em;
+ text-align: right;
+}
+.matrix td {
+
+ padding: 5px 10px;
+}
+.matrix-bracket-left, .matrix-bracket-right {
+
+ position: absolute;
+ top: 0;
+ width: 5px;
+ height: 100%;
+ border: 1px solid #CCC;
+}
+.matrix-bracket-left {
+
+ left: 0;
+ border-right: none;
+}
+.matrix-bracket-right {
+
+ right: 0;
+ border-left: none;
+}
+/*.matrix.qubit tr:first-child td {
+
+ color: #BBB;
+}*/
+
+
+
+.Q-state-vector,
+.complex-vector {
+
+ font-family: var( --Q-font-family-mono );
+}
+.Q-state-vector.bra::before,
+.complex-vector.bra::before {
+
+ content: '⟨';
+ color: #BBB;
+}
+.Q-state-vector.bra::after,
+.complex-vector.bra::after {
+
+ content: '|';
+ color: #BBB;
+}
+.Q-state-vector.ket::before,
+.complex-vector.ket::before {
+
+ content: '|';
+ color: #BBB;
+}
+.Q-state-vector.ket::after,
+.complex-vector.ket::after {
+
+ content: '⟩';
+ color: #BBB;
+}
+.Q-state-vector.bra + .Q-state-vector.ket::before,
+.complex-vector.bra + .complex-vector.ket::before {
+
+ content: '';
+}
+
+
+
+
+
+
+
+/*
+
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+*/
+@charset "utf-8";
+
+
+
+
+
+
+
+
+
+/*
+
+ Z indices:
+
+ Clipboard =100
+ Selected op 10
+ Operation 0
+ Shadow -10
+ Background -20
+
+
+
+
+
+ Circuit
+
+ Menu Moments
+ ╭───────┬───┬───┬───┬───╮
+ │ ≡ ↘ │ 1 │ 2 │ 3 │ + │ Add moment
+ ├───┬───┼───┼───┼───┼───╯
+ R │ 0 │|0⟩│ H │ C0│ X │ -
+ e ├───┼───┼───┼───┼───┤
+ g │ 1 │|0⟩│ I │ C1│ X │ -
+ s ├───┼───┴───┴───┴───┘
+ │ + │ - - - -
+ ╰───╯
+ Add
+ register
+
+
+ Circuit Palette
+
+ ╭───────────────────┬───╮
+ │ H X Y Z S T π M … │ @ │
+ ╰───────────────────┴───╯
+
+
+ Circuit clipboard
+
+ ┌───────────────┐
+ ▟│ ┌───┬───────┐ │
+ █│ │ H │ X#0.0 │ │
+ █│ ├───┼───────┤ │
+ █│ │ I │ X#0.1 │ │
+ █│ └───┴───────┘ │
+ █└───────────────┘
+ ███████████████▛
+
+
+
+ ◢◣
+ ◢■■■■◣
+◢■■■■■■■■◣
+◥■■■■■■■■◤
+ ◥■■■■◤
+ ◥◤
+
+
+ ◢■■■■■■◤
+ ◢◤ ◢◤
+◢■■■■■■◤
+
+
+ ───────────
+ ╲ ╱ ╱ ╱
+ ╳ ╱ ╱
+ ╱ ╲╱ ╱
+ ───────
+
+
+ ─────⦢
+ ╱ ╱
+⦣─────
+
+
+*/
+
+
+
+
+
+
+.Q-circuit,
+.Q-circuit-palette {
+
+ position: relative;
+ width: 100%;
+}
+.Q-circuit-palette {
+
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ line-height: 0;
+}
+.Q-circuit-palette > div {
+
+ display: inline-block;
+ position: relative;
+ width: 4rem;
+ height: 4rem;
+}
+
+
+.Q-circuit {
+
+ margin: 1rem 0 2rem 0;
+ /*border-top: 2px solid hsl( 0, 0%, 50% );*/
+}
+.Q-circuit-board-foreground {
+
+ line-height: 3.85rem;
+ width: auto;
+}
+
+
+
+
+
+
+ /***************/
+ /* */
+ /* Toolbar */
+ /* */
+/***************/
+
+
+.Q-circuit-toolbar {
+
+ display: block;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ margin-bottom: 0.5rem;
+
+ box-sizing: border-box;
+ display: grid;
+ grid-auto-columns: 3.6rem;
+ grid-auto-rows: 3.0rem;
+ grid-auto-flow: column;
+
+}
+.Q-circuit-button {
+
+ position: relative;
+ display: inline-block;
+ /*margin: 0 0.5rem 0.5rem 0;*/
+ width: 3.6rem;
+ height: 3rem;
+/* box-shadow:
+ -0.1rem -0.1rem 0 rgba( 255, 255, 255, 0.8 ),
+ 0.1rem 0.1rem 0.1rem rgba( 0, 0, 0, 0.35 );*/
+
+ border-top: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 100%
+ );
+ border-right: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 90%
+ );
+ border-bottom: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 85%
+ );
+ border-left: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 97%
+ );
+ background: var( --Q-color-background );
+/* background:
+ var( --Q-color-background )
+ linear-gradient(
+
+ 0.4turn,
+
+ rgba( 0, 0, 0, 0.02 ),
+ rgba( 255, 255, 255, 0.1 )
+ );*/
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 30%
+ );
+ text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 );
+ /*border-radius: 0.5rem;*/
+ /*border-radius: 100%;*/
+ line-height: 2.9rem;
+ text-align: center;
+ cursor: pointer;
+ overflow: hidden;
+ font-weight: 900;
+}
+.Q-circuit-toolbar .Q-circuit-button:first-child {
+
+ border-top-left-radius: 0.5rem;
+ border-bottom-left-radius: 0.5rem;
+}
+.Q-circuit-toolbar .Q-circuit-button:last-child {
+
+ border-top-right-radius: 0.5rem;
+ border-bottom-right-radius: 0.5rem;
+}
+.Q-circuit-locked .Q-circuit-button,
+.Q-circuit-button[Q-disabled] {
+
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 85%
+ );
+ cursor: not-allowed;
+}
+.Q-circuit-locked .Q-circuit-toggle-lock {
+
+ color: inherit;
+ cursor: pointer;
+}
+
+
+
+
+.Q-circuit-board-container {
+
+ position: relative;
+ margin: 0 0 2rem 0;
+ margin: 0;
+ width: 100%;
+ max-height: 60vh;
+ overflow: scroll;
+}
+.Q-circuit-board {
+
+ position: relative;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+/*.Q-circuit-palette,*/
+.Q-circuit-board-foreground,
+.Q-circuit-board-background,
+.Q-circuit-clipboard {
+
+ box-sizing: border-box;
+ display: grid;
+ grid-auto-rows: 4rem;
+ grid-auto-columns: 4rem;
+ grid-auto-flow: column;
+}
+/*.Q-circuit-palette,*/
+.Q-circuit-board-foreground,
+.Q-circuit-board-background {
+
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.Q-circuit-clipboard {
+
+ position: absolute;
+ z-index: 100;
+ min-width: 4rem;
+ min-height: 4rem;
+ transform: scale( 1.05 );
+}
+.Q-circuit-clipboard, .Q-circuit-clipboard > div {
+
+ cursor: grabbing;
+}
+.Q-circuit-clipboard-danger .Q-circuit-operation {
+
+ background-color: var( --Q-color-yellow );
+}
+.Q-circuit-clipboard-destroy {
+
+ animation-name: Q-circuit-clipboard-poof;
+ animation-fill-mode: forwards;
+ animation-duration: 0.3s;
+ animation-iteration-count: 1;
+}
+@keyframes Q-circuit-clipboard-poof {
+
+ 100% {
+
+ transform: scale( 1.5 );
+ opacity: 0;
+ }
+}
+.Q-circuit-board-background {
+
+ /*
+
+ Clipboard: 100
+ Operation: 0
+ Shadow: -10
+ Background: -20
+
+ */
+ position: absolute;
+ z-index: -20;
+ color: rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-board-background > div {
+
+/* transition:
+ background-color 0.2s,
+ color 0.2s;*/
+}
+.Q-circuit-board-background .Q-circuit-cell-highlighted {
+
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+ /*transition: none;*/
+}
+
+
+
+
+.Q-circuit-register-wire {
+
+ position: absolute;
+ top: calc( 50% - 0.5px );
+ width: 100%;
+ height: 1px;
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 50%
+ );
+}
+
+
+
+.Q-circuit-palette > div,
+.Q-circuit-clipboard > div,
+.Q-circuit-board-foreground > div {
+
+ text-align: center;
+}
+
+
+
+
+
+
+ /***************/
+ /* */
+ /* Headers */
+ /* */
+/***************/
+
+
+.Q-circuit-header {
+
+ position: sticky;
+ z-index: 2;
+ margin: 0;
+ /*background-color: var( --Q-color-background );*/
+ background-color: white;
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 75%
+ );
+ font-family: var( --Q-font-family-mono );
+}
+.Q-circuit-input.Q-circuit-cell-highlighted,
+.Q-circuit-header.Q-circuit-cell-highlighted {
+
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+ color: black;
+}
+.Q-circuit-selectall {
+
+ z-index: 3;
+ margin: 0;
+ top: 0;
+ /*left: 4rem;*/
+ /*grid-column: 2;*/
+ left: 0;
+ grid-column-start: 1;
+ grid-column-end: 3;
+ grid-row: 1;
+ cursor: se-resize;
+}
+.Q-circuit-moment-label,
+.Q-circuit-moment-add {
+
+ grid-row: 1;
+ top: 0;
+ cursor: s-resize;
+}
+.Q-circuit-register-label,
+.Q-circuit-register-add {
+
+ grid-column: 2;
+ left: 4rem;
+ cursor: e-resize;
+}
+.Q-circuit-moment-add,
+.Q-circuit-register-add {
+
+ cursor: pointer;
+}
+.Q-circuit-moment-add,
+.Q-circuit-register-add {
+
+ display: none;
+}
+.Q-circuit-selectall,
+.Q-circuit-moment-label,
+.Q-circuit-moment-add {
+
+ border-bottom: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+}
+.Q-circuit-selectall,
+.Q-circuit-register-label,
+.Q-circuit-register-add {
+
+ border-right: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+}
+.Q-circuit-input {
+
+ position: sticky;
+ z-index: 2;
+ grid-column: 1;
+ left: 0;
+ /*background-color: var( --Q-color-background );*/
+ background-color: white;
+ font-size: 1.5rem;
+ font-weight: 900;
+ font-family: var( --Q-font-family-mono );
+}
+
+
+
+
+
+
+.Q-circuit-operation-link-container {
+
+ --Q-link-stroke: 3px;
+ --Q-link-radius: 100%;
+
+ display: block;
+ position: relative;
+ left: calc( 50% - ( var( --Q-link-stroke ) / 2 ));
+ width: 50%;
+ height: 100%;
+ overflow: hidden;
+}
+.Q-circuit-operation-link-container.Q-circuit-cell-highlighted {
+
+ background-color: transparent;
+}
+.Q-circuit-operation-link {
+
+ display: block;
+ position: absolute;
+ width: calc( var( --Q-link-stroke ) * 2 );
+ height: calc( 100% - 4rem + var( --Q-link-stroke ));
+ /*border: var( --Q-link-stroke ) solid hsl( 0, 0%, 50% );*/
+ border: var( --Q-link-stroke ) solid hsl(
+
+ var( --Q-color-background-hue ),
+ 10%,
+ 30%
+ );
+
+ /*border: var( --Q-link-stroke ) solid var( --Q-color-orange );*/
+
+ transform: translate( -50%, calc( 2rem - ( var( --Q-link-stroke ) / 2 )));
+ transform-origin: center;
+}
+.Q-circuit-operation-link.Q-circuit-operation-link-curved {
+
+ width: calc( var( --Q-link-radius ) - var( --Q-link-stroke ));
+ width: 200%;
+ border-radius: 100%;
+}
+
+
+
+
+
+
+ /******************/
+ /* */
+ /* Operations */
+ /* */
+/******************/
+
+
+.Q-circuit-operation {
+
+ position: relative;
+ /*--Q-operation-color-hue: var( --Q-color-green-hue );
+ --Q-operation-color-main: var( --Q-color-green );*/
+
+ --Q-operation-color-hue: var( --Q-color-blue-hue );
+ --Q-operation-color-main: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 35%
+ );
+
+ --Q-operation-color-light: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 50%
+ );
+ --Q-operation-color-dark: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 25%
+ );
+ color: white;
+ text-shadow: -0.05rem -0.05rem 0 rgba( 0, 0, 0, 0.1 );
+ font-size: 1.5rem;
+ line-height: 2.9rem;
+ font-weight: 900;
+ cursor: grab;
+}
+.Q-circuit-locked .Q-circuit-operation {
+
+ cursor: not-allowed;
+}
+.Q-circuit-operation-tile {
+
+ position: absolute;
+ top: 0.5rem;
+ left: 0.5rem;
+ right: 0.5rem;
+ bottom: 0.5rem;
+
+ /*margin: 0.5rem;*/
+ /*padding: 0.5rem;*/
+
+ /*box-shadow: 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.2 );*/
+ border-radius: 0.2rem;
+ /*
+ border-top: 0.1rem solid var( --Q-operation-color-light );
+ border-left: 0.1rem solid var( --Q-operation-color-light );
+ border-right: 0.1rem solid var( --Q-operation-color-dark );
+ border-bottom: 0.1rem solid var( --Q-operation-color-dark );
+ */
+ background:
+ var( --Q-operation-color-main )
+ /*linear-gradient(
+
+ 0.45turn,
+ rgba( 255, 255, 255, 0.1 ),
+ rgba( 0, 0, 0, 0.05 )
+ )*/;
+}
+.Q-circuit-palette .Q-circuit-operation:hover {
+
+ /*background-color: rgba( 255, 255, 255, 0.6 );*/
+ background-color: white;
+}
+.Q-circuit-palette .Q-circuit-operation-tile {
+
+ --Q-before-rotation: 12deg;
+ --Q-before-x: 1px;
+ --Q-before-y: -2px;
+
+ --Q-after-rotation: -7deg;
+ --Q-after-x: -2px;
+ --Q-after-y: 3px;
+
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-palette .Q-circuit-operation-tile:before,
+.Q-circuit-palette .Q-circuit-operation-tile:after {
+
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ border-radius: 0.2rem;
+ /*background-color: hsl( 0, 0%, 60% );*/
+
+ background-color: var( --Q-operation-color-dark );
+ transform:
+ translate( var( --Q-before-x ), var( --Q-before-y ))
+ rotate( var( --Q-before-rotation ));
+ z-index: -10;
+ /*z-index: 10;*/
+ display: block;
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-palette .Q-circuit-operation-tile:after {
+
+ transform:
+ translate( var( --Q-after-x ), var( --Q-after-y ))
+ rotate( var( --Q-after-rotation ));
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-operation:hover .Q-circuit-operation-tile {
+
+ color: white;
+}
+
+
+
+
+.Q-circuit-operation-hadamard .Q-circuit-operation-tile {
+
+ /*--Q-operation-color-hue: var( --Q-color-red-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-red );*/
+
+ /*--Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 10% );*/
+
+
+/* background:
+ linear-gradient(
+
+ -33deg,
+ var( --Q-color-blue ) 20%,
+ #6f3c69 50%,
+ var( --Q-color-red ) 80%
+ );*/
+}
+.Q-circuit-operation-identity .Q-circuit-operation-tile,
+.Q-circuit-operation-control .Q-circuit-operation-tile,
+.Q-circuit-operation-target .Q-circuit-operation-tile {
+
+ /*--Q-operation-color-hue: var( --Q-color-orange-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-orange );*/
+ border-radius: 100%;
+}
+.Q-circuit-operation-identity .Q-circuit-operation-tile,
+.Q-circuit-operation-control .Q-circuit-operation-tile {
+
+ top: calc( 50% - 0.7rem );
+ left: calc( 50% - 0.7rem );
+ width: 1.4rem;
+ height: 1.4rem;
+ overflow: hidden;
+/* --Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 10% );*/
+}
+.Q-circuit-operation-pauli-x,
+.Q-circuit-operation-pauli-y,
+.Q-circuit-operation-pauli-z {
+
+ /*--Q-operation-color-hue: var( --Q-color-red-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-red );*/
+
+/* --Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 30% );*/
+}
+.Q-circuit-operation-swap .Q-circuit-operation-tile {
+
+ top: calc( 50% - 0.55rem );
+ left: calc( 50% - 0.55rem );
+ width: 1.2rem;
+ height: 1.2rem;
+ border-radius: 0;
+ transform-origin: center;
+ transform: rotate( 45deg );
+ font-size: 0;
+}
+
+
+
+
+
+
+ /********************/
+ /* */
+ /* Other states */
+ /* */
+/********************/
+
+
+.Q-circuit-palette > div:hover,
+.Q-circuit-board-foreground > div:hover {
+
+ outline: 2px solid var( --Q-hyperlink-internal-color );
+ outline-offset: -2px;
+}
+.Q-circuit-palette > div:hover .Q-circuit-operation-tile {
+
+ box-shadow: none;
+}
+/*.Q-circuit-palette > div:hover,*/
+.Q-circuit-board-foreground > div:hover {
+
+ background-color: white;
+ color: black;
+}
+
+
+
+
+
+
+.Q-circuit-clipboard > div,
+.Q-circuit-cell-selected {
+
+ background-color: white;
+}
+.Q-circuit-clipboard > div:before,
+.Q-circuit-cell-selected:before {
+
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ z-index: -10;
+ box-shadow:
+ 0 0 1rem rgba( 0, 0, 0, 0.2 ),
+ 0.4rem 0.4rem 0.2rem rgba( 0, 0, 0, 0.2 );
+ outline: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 50%
+ );
+ /*outline-offset: -1px;*/
+}
+
+
+
+
+.Q-circuit-clipboard > div {
+
+ background-color: white;
+}
+.Q-circuit-clipboard > div:before {
+
+ /*
+
+ This was very helpful!
+ https://blog.dudak.me/2014/css-shadows-under-adjacent-elements/
+
+ */
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: -10;
+ display: block;
+ box-shadow: 0.4rem 0.4rem 0.3rem rgba( 0, 0, 0, 0.2 );
+}
+
+
+
+
+
+ /***************/
+ /* */
+ /* Buttons */
+ /* */
+/***************/
+
+
+.Q-circuit-locked .Q-circuit-toggle-lock,
+.Q-circuit-locked .Q-circuit-toggle-lock:hover {
+
+ background-color: var( --Q-color-red );
+}
+.Q-circuit-toggle-lock {
+
+ z-index: 3;
+ left: 0;
+ top: 0;
+ grid-column: 1;
+ grid-row: 1;
+ cursor: pointer;
+ font-size: 1.1rem;
+ text-shadow: none;
+ font-weight: normal;
+}
+.Q-circuit-button-undo,
+.Q-circuit-button-redo {
+
+ font-size: 1.2rem;
+ line-height: 2.6rem;
+ font-weight: normal;
+}
+
+
+
+.Q-circuit p {
+
+ padding: 1rem;
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 66%
+ );
+}
+
+
+
+/*
+
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+*/
+@charset "utf-8";
+
+
+
+
+
+
+
+
+
+/*
+
+ Z indices:
+
+ Clipboard =100
+ Selected op 10
+ Operation 0
+ Shadow -10
+ Background -20
+
+
+
+
+
+ Circuit
+
+ Menu Moments
+ ╭───────┬───┬───┬───┬───╮
+ │ ≡ ↘ │ 1 │ 2 │ 3 │ + │ Add moment
+ ├───┬───┼───┼───┼───┼───╯
+ R │ 0 │|0⟩│ H │ C0│ X │ -
+ e ├───┼───┼───┼───┼───┤
+ g │ 1 │|0⟩│ I │ C1│ X │ -
+ s ├───┼───┴───┴───┴───┘
+ │ + │ - - - -
+ ╰───╯
+ Add
+ register
+
+
+ Circuit Palette
+
+ ╭───────────────────┬───╮
+ │ H X Y Z S T π M … │ @ │
+ ╰───────────────────┴───╯
+
+
+ Circuit clipboard
+
+ ┌───────────────┐
+ ▟│ ┌───┬───────┐ │
+ █│ │ H │ X#0.0 │ │
+ █│ ├───┼───────┤ │
+ █│ │ I │ X#0.1 │ │
+ █│ └───┴───────┘ │
+ █└───────────────┘
+ ███████████████▛
+
+
+
+ ◢◣
+ ◢■■■■◣
+◢■■■■■■■■◣
+◥■■■■■■■■◤
+ ◥■■■■◤
+ ◥◤
+
+
+ ◢■■■■■■◤
+ ◢◤ ◢◤
+◢■■■■■■◤
+
+
+ ───────────
+ ╲ ╱ ╱ ╱
+ ╳ ╱ ╱
+ ╱ ╲╱ ╱
+ ───────
+
+
+ ─────⦢
+ ╱ ╱
+⦣─────
+
+
+*/
+
+
+
+
+
+.Q-circuit,
+.Q-circuit-palette {
+
+ position: relative;
+ width: 100%;
+}
+.Q-circuit-palette {
+
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ line-height: 0;
+}
+.Q-circuit-palette > div {
+
+ display: inline-block;
+ position: relative;
+ width: 4rem;
+ height: 4rem;
+}
+
+
+.Q-circuit {
+
+ margin: 1rem 0 2rem 0;
+ /*border-top: 2px solid hsl( 0, 0%, 50% );*/
+}
+.Q-parameters-box,
+.Q-circuit-board-foreground {
+ line-height: 3.85rem;
+ width: auto;
+}
+
+
+
+
+
+
+ /***************/
+ /* */
+ /* Toolbar */
+ /* */
+/***************/
+
+
+.Q-circuit-toolbar {
+
+ position: relative;
+ display: block;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ margin-bottom: 0.5rem;
+
+ box-sizing: border-box;
+ display: grid;
+ grid-auto-columns: 3.6rem;
+ grid-auto-rows: 3.0rem;
+ grid-auto-flow: column;
+
+}
+.Q-circuit-button {
+
+ position: relative;
+ display: inline-block;
+ /*margin: 0 0.5rem 0.5rem 0;*/
+ width: 3.6rem;
+ height: 3rem;
+/* box-shadow:
+ -0.1rem -0.1rem 0 rgba( 255, 255, 255, 0.8 ),
+ 0.1rem 0.1rem 0.1rem rgba( 0, 0, 0, 0.35 );*/
+
+ border-top: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 100%
+ );
+ border-right: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 90%
+ );
+ border-bottom: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 85%
+ );
+ border-left: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 97%
+ );
+ background: var( --Q-color-background );
+/* background:
+ var( --Q-color-background )
+ linear-gradient(
+
+ 0.4turn,
+
+ rgba( 0, 0, 0, 0.02 ),
+ rgba( 255, 255, 255, 0.1 )
+ );*/
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 30%
+ );
+ text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 );
+ /*border-radius: 0.5rem;*/
+ /*border-radius: 100%;*/
+ line-height: 2.9rem;
+ text-align: center;
+ cursor: pointer;
+ overflow: hidden;
+ font-weight: 900;
+}
+.Q-circuit-toolbar .Q-circuit-button:first-child {
+
+ border-top-left-radius: 0.5rem;
+ border-bottom-left-radius: 0.5rem;
+}
+.Q-circuit-toolbar .Q-circuit-button:last-child {
+
+ border-top-right-radius: 0.5rem;
+ border-bottom-right-radius: 0.5rem;
+}
+.Q-circuit-locked .Q-circuit-button,
+.Q-circuit-button[Q-disabled] {
+
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 85%
+ );
+ cursor: not-allowed;
+}
+.Q-circuit-locked .Q-circuit-toggle-lock {
+
+ color: inherit;
+ cursor: pointer;
+}
+
+
+
+
+.Q-circuit-board-container {
+
+ position: relative;
+ margin: 0 0 2rem 0;
+ margin: 0;
+ width: 100%;
+ max-height: 60vh;
+ overflow: scroll;
+}
+.Q-circuit-board {
+
+ position: relative;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+/*.Q-circuit-palette,*/
+.Q-circuit-board-foreground,
+.Q-circuit-board-background,
+.Q-circuit-clipboard {
+
+ box-sizing: border-box;
+ display: grid;
+ grid-auto-rows: 4rem;
+ grid-auto-columns: 4rem;
+ grid-auto-flow: column;
+}
+
+.Q-parameters-box {
+
+ position: absolute;
+ display: none;
+ z-index: 100;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: whitesmoke;
+}
+
+/*.Q-circuit-palette,*/
+.Q-circuit-board-foreground,
+.Q-circuit-board-background {
+
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.Q-circuit-clipboard {
+
+ position: absolute;
+ z-index: 100;
+ min-width: 4rem;
+ min-height: 4rem;
+ transform: scale( 1.05 );
+}
+.Q-circuit-clipboard, .Q-circuit-clipboard > div {
+
+ cursor: grabbing;
+}
+.Q-circuit-clipboard-danger .Q-circuit-operation {
+
+ background-color: var( --Q-color-yellow );
+}
+.Q-circuit-clipboard-destroy {
+
+ animation-name: Q-circuit-clipboard-poof;
+ animation-fill-mode: forwards;
+ animation-duration: 0.3s;
+ animation-iteration-count: 1;
+}
+@keyframes Q-circuit-clipboard-poof {
+
+ 100% {
+
+ transform: scale( 1.5 );
+ opacity: 0;
+ }
+}
+.Q-circuit-board-background {
+
+ /*
+
+ Clipboard: 100
+ Operation: 0
+ Shadow: -10
+ Background: -20
+
+ */
+ position: absolute;
+ z-index: -20;
+ color: rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-board-background > div {
+
+/* transition:
+ background-color 0.2s,
+ color 0.2s;*/
+}
+.Q-circuit-board-background .Q-circuit-cell-highlighted {
+
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+ /*transition: none;*/
+}
+
+
+
+
+.Q-circuit-register-wire {
+
+ position: absolute;
+ top: calc( 50% - 0.5px );
+ width: 100%;
+ height: 1px;
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 50%
+ );
+}
+
+.Q-parameter-box-exit {
+ position: relative;
+ right: 0;
+ left: 0;
+ width: 5rem;
+ height: 2.5rem;
+ background-color: whitesmoke;
+}
+
+.Q-parameters-box > div,
+.Q-circuit-palette > div,
+.Q-circuit-clipboard > div,
+.Q-circuit-board-foreground > div {
+
+ text-align: center;
+}
+
+
+
+
+
+
+ /***************/
+ /* */
+ /* Headers */
+ /* */
+/***************/
+
+
+.Q-circuit-header {
+
+ position: sticky;
+ z-index: 2;
+ margin: 0;
+ /*background-color: var( --Q-color-background );*/
+ background-color: white;
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 75%
+ );
+ font-family: var( --Q-font-family-mono );
+}
+.Q-circuit-input.Q-circuit-cell-highlighted,
+.Q-circuit-header.Q-circuit-cell-highlighted {
+
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+ color: black;
+}
+.Q-circuit-selectall {
+
+ z-index: 3;
+ margin: 0;
+ top: 0;
+ /*left: 4rem;*/
+ /*grid-column: 2;*/
+ left: 0;
+ grid-column-start: 1;
+ grid-column-end: 3;
+ grid-row: 1;
+ cursor: se-resize;
+}
+.Q-circuit-moment-label,
+.Q-circuit-moment-add {
+
+ grid-row: 1;
+ top: 0;
+ cursor: s-resize;
+}
+.Q-circuit-register-label,
+.Q-circuit-register-add {
+
+ grid-column: 2;
+ left: 4rem;
+ cursor: e-resize;
+}
+.Q-circuit-moment-add,
+.Q-circuit-register-add {
+
+ cursor: pointer;
+}
+.Q-circuit-moment-add,
+.Q-circuit-register-add {
+
+ display: none;
+}
+.Q-circuit-selectall,
+.Q-circuit-moment-label,
+.Q-circuit-moment-add {
+
+ border-bottom: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+}
+.Q-circuit-selectall,
+.Q-circuit-register-label,
+.Q-circuit-register-add {
+
+ border-right: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+}
+.Q-circuit-input {
+
+ position: sticky;
+ z-index: 2;
+ grid-column: 1;
+ left: 0;
+ /*background-color: var( --Q-color-background );*/
+ background-color: white;
+ font-size: 1.5rem;
+ font-weight: 900;
+ font-family: var( --Q-font-family-mono );
+}
+
+
+
+
+
+
+.Q-circuit-operation-link-container {
+
+ --Q-link-stroke: 3px;
+ --Q-link-radius: 100%;
+
+ display: block;
+ position: relative;
+ left: calc( 50% - ( var( --Q-link-stroke ) / 2 ));
+ width: 50%;
+ height: 100%;
+ overflow: hidden;
+}
+.Q-circuit-operation-link-container.Q-circuit-cell-highlighted {
+
+ background-color: transparent;
+}
+.Q-circuit-operation-link {
+
+ display: block;
+ position: absolute;
+ width: calc( var( --Q-link-stroke ) * 2 );
+ height: calc( 100% - 4rem + var( --Q-link-stroke ));
+ /*border: var( --Q-link-stroke ) solid hsl( 0, 0%, 50% );*/
+ border: var( --Q-link-stroke ) solid hsl(
+
+ var( --Q-color-background-hue ),
+ 10%,
+ 30%
+ );
+
+ /*border: var( --Q-link-stroke ) solid var( --Q-color-orange );*/
+
+ transform: translate( -50%, calc( 2rem - ( var( --Q-link-stroke ) / 2 )));
+ transform-origin: center;
+}
+.Q-circuit-operation-link.Q-circuit-operation-link-curved {
+
+ width: calc( var( --Q-link-radius ) - var( --Q-link-stroke ));
+ width: 200%;
+ border-radius: 100%;
+}
+
+
+
+
+
+
+ /******************/
+ /* */
+ /* Operations */
+ /* */
+/******************/
+
+.Q-circuit-operation {
+
+ position: relative;
+ /*--Q-operation-color-hue: var( --Q-color-green-hue );
+ --Q-operation-color-main: var( --Q-color-green );*/
+
+ --Q-operation-color-hue: var( --Q-color-blue-hue );
+ --Q-operation-color-main: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 35%
+ );
+
+ --Q-operation-color-light: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 50%
+ );
+ --Q-operation-color-dark: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 25%
+ );
+ color: white;
+ text-shadow: -0.05rem -0.05rem 0 rgba( 0, 0, 0, 0.1 );
+ font-size: 1.5rem;
+ line-height: 2.9rem;
+ font-weight: 900;
+ cursor: grab;
+}
+.Q-circuit-locked .Q-circuit-operation {
+
+ cursor: not-allowed;
+}
+.Q-circuit-operation-tile {
+
+ position: absolute;
+ top: 0.5rem;
+ left: 0.5rem;
+ right: 0.5rem;
+ bottom: 0.5rem;
+
+ /*margin: 0.5rem;*/
+ /*padding: 0.5rem;*/
+
+ /*box-shadow: 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.2 );*/
+ border-radius: 0.2rem;
+ /*
+ border-top: 0.1rem solid var( --Q-operation-color-light );
+ border-left: 0.1rem solid var( --Q-operation-color-light );
+ border-right: 0.1rem solid var( --Q-operation-color-dark );
+ border-bottom: 0.1rem solid var( --Q-operation-color-dark );
+ */
+ background:
+ var( --Q-operation-color-main )
+ /*linear-gradient(
+
+ 0.45turn,
+ rgba( 255, 255, 255, 0.1 ),
+ rgba( 0, 0, 0, 0.05 )
+ )*/;
+}
+.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover {
+
+ /*background-color: rgba( 255, 255, 255, 0.6 );*/
+ background-color: white;
+}
+.Q-circuit-palette .Q-circuit-operation-tile {
+
+ --Q-before-rotation: 12deg;
+ --Q-before-x: 1px;
+ --Q-before-y: -2px;
+
+ --Q-after-rotation: -7deg;
+ --Q-after-x: -2px;
+ --Q-after-y: 3px;
+
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-palette .Q-circuit-operation-tile:before,
+.Q-circuit-palette .Q-circuit-operation-tile:after {
+
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ border-radius: 0.2rem;
+ /*background-color: hsl( 0, 0%, 60% );*/
+
+ background-color: var( --Q-operation-color-dark );
+ transform:
+ translate( var( --Q-before-x ), var( --Q-before-y ))
+ rotate( var( --Q-before-rotation ));
+ z-index: -10;
+ /*z-index: 10;*/
+ display: block;
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-palette .Q-circuit-operation-tile:after {
+
+ transform:
+ translate( var( --Q-after-x ), var( --Q-after-y ))
+ rotate( var( --Q-after-rotation ));
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-operation:hover .Q-circuit-operation-tile {
+
+ color: white;
+}
+
+
+
+
+.Q-circuit-operation-hadamard .Q-circuit-operation-tile {
+
+ /*--Q-operation-color-hue: var( --Q-color-red-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-red );*/
+
+ /*--Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 10% );*/
+
+
+/* background:
+ linear-gradient(
+
+ -33deg,
+ var( --Q-color-blue ) 20%,
+ #6f3c69 50%,
+ var( --Q-color-red ) 80%
+ );*/
+}
+.Q-circuit-operation-identity .Q-circuit-operation-tile,
+.Q-circuit-operation-control .Q-circuit-operation-tile,
+.Q-circuit-operation-target .Q-circuit-operation-tile {
+
+ /*--Q-operation-color-hue: var( --Q-color-orange-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-orange );*/
+ border-radius: 100%;
+}
+.Q-circuit-operation-identity .Q-circuit-operation-tile,
+.Q-circuit-operation-control .Q-circuit-operation-tile {
+
+ top: calc( 50% - 0.7rem );
+ left: calc( 50% - 0.7rem );
+ width: 1.4rem;
+ height: 1.4rem;
+ overflow: hidden;
+/* --Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 10% );*/
+}
+.Q-circuit-operation-pauli-x,
+.Q-circuit-operation-pauli-y,
+.Q-circuit-operation-pauli-z {
+
+ /*--Q-operation-color-hue: var( --Q-color-red-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-red );*/
+
+/* --Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 30% );*/
+}
+.Q-circuit-operation-swap .Q-circuit-operation-tile {
+
+ top: calc( 50% - 0.55rem );
+ left: calc( 50% - 0.55rem );
+ width: 1.2rem;
+ height: 1.2rem;
+ border-radius: 0;
+ transform-origin: center;
+ transform: rotate( 45deg );
+ font-size: 0;
+}
+
+.Q-parameter-box-input-container {
+ position: relative;
+ text-align: center;
+ grid-auto-columns: 4rem;
+ grid-auto-flow: column;
+}
+
+.Q-parameter-box-input {
+ position: relative;
+ border-radius: .2rem;
+ margin-left: 10px;
+ font-family: var( --Q-font-family-mono );
+}
+
+.Q-parameter-input-label {
+ position: relative;
+ color: var( --Q-color-blue );
+ font-family: var( --Q-font-family-mono );
+}
+
+
+
+
+ /********************/
+ /* */
+ /* Other states */
+ /* */
+/********************/
+
+
+.Q-circuit-palette > div:hover,
+.Q-circuit-board-foreground > div:hover {
+
+ outline: 2px solid var( --Q-hyperlink-internal-color );
+ outline-offset: -2px;
+}
+.Q-circuit-palette > div:hover .Q-circuit-operation-tile {
+
+ box-shadow: none;
+}
+/*.Q-circuit-palette > div:hover,*/
+.Q-circuit-board-foreground > div:hover {
+
+ background-color: white;
+ color: black;
+}
+
+
+
+
+
+
+.Q-circuit-clipboard > div,
+.Q-circuit-cell-selected {
+
+ background-color: white;
+}
+.Q-circuit-clipboard > div:before,
+.Q-circuit-cell-selected:before {
+
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ z-index: -10;
+ box-shadow:
+ 0 0 1rem rgba( 0, 0, 0, 0.2 ),
+ 0.4rem 0.4rem 0.2rem rgba( 0, 0, 0, 0.2 );
+ outline: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 50%
+ );
+ /*outline-offset: -1px;*/
+}
+
+
+
+
+.Q-circuit-clipboard > div {
+
+ background-color: white;
+}
+.Q-circuit-clipboard > div:before {
+
+ /*
+
+ This was very helpful!
+ https://blog.dudak.me/2014/css-shadows-under-adjacent-elements/
+
+ */
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: -10;
+ display: block;
+ box-shadow: 0.4rem 0.4rem 0.3rem rgba( 0, 0, 0, 0.2 );
+}
+
+
+
+
+
+
+
+ /***************/
+ /* */
+ /* Buttons */
+ /* */
+/***************/
+
+
+.Q-circuit-locked .Q-circuit-toggle-lock,
+.Q-circuit-locked .Q-circuit-toggle-lock:hover {
+
+ background-color: var( --Q-color-red );
+}
+.Q-circuit-toggle-lock {
+
+ z-index: 3;
+ left: 0;
+ top: 0;
+ grid-column: 1;
+ grid-row: 1;
+ cursor: pointer;
+ font-size: 1.1rem;
+ text-shadow: none;
+ font-weight: normal;
+}
+.Q-circuit-button-undo,
+.Q-circuit-button-redo {
+
+ font-size: 1.2rem;
+ line-height: 2.6rem;
+ font-weight: normal;
+}
+
+
+
+.Q-circuit p {
+
+ padding: 1rem;
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 66%
+ );
+}
+
+
+
diff --git a/build/q-old.js b/build/bundle.js
similarity index 53%
rename from build/q-old.js
rename to build/bundle.js
index ddcf90e..0cb62d7 100644
--- a/build/q-old.js
+++ b/build/bundle.js
@@ -1,2569 +1,3629 @@
+(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ runTimeout(drainQueue);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+process.prependListener = noop;
+process.prependOnceListener = noop;
+
+process.listeners = function (name) { return [] }
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}],3:[function(require,module,exports){
+//Logging functions
+
+function log(verbosity = 0.5, verbosityThreshold, ...remainingArguments) {
+ if (verbosity >= verbosityThreshold) console.log(...remainingArguments);
+ return "(log)";
+}
- if( Q.verbosity >= verbosityThreshold ) console.log( ...remainingArguments )
- return '(log)'
- },
- warn: function(){
+function error() {
+ console.error(...arguments);
+ return "(error)";
+}
- console.warn( ...arguments )
- return '(warn)'
- },
- error: function(){
+function warn() {
+ console.warn(...arguments);
+ return "(error)";
+}
- console.error( ...arguments )
- return '(error)'
- },
- extractDocumentation: function( f ){
+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("");
+}
- `
- 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.
- `
+function help(f) {
+ if (f === undefined) f = Q;
+ return extractDocumentation(f);
+}
- f = f.toString()
-
- const
- begin = f.indexOf( '`' ) + 1,
- end = f.indexOf( '`', begin ),
- lines = f.substring( begin, end ).split( '\n' )
+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 = " ";
- function countPrefixTabs( text ){
-
+ const padLengthLeft = Math.floor((length - text.length) / 2),
+ padLengthRight = length - text.length - padLengthLeft;
- // Is counting tabs “manuallyâ€
- // actually more performant than regex?
+ return text
+ .padStart(padLengthLeft + text.length, filler)
+ .padEnd(length, filler);
+ } else return text;
+}
- let count = index = 0
- while( text.charAt( index ++ ) === '\t' ) count ++
- return count
- }
+module.exports = { log, error, help, warn, toTitleCase, centerText };
+
+},{}],4:[function(require,module,exports){
+//math functions
+function hypotenuse(x, y) {
+ let a = Math.abs(x),
+ b = Math.abs(y);
+
+ if (a < 2048 && b < 2048) {
+ return Math.sqrt(a * a + b * b);
+ }
+ if (a < b) {
+ a = b;
+ b = x / y;
+ } else b = y / x;
+ return a * Math.sqrt(1 + b * b);
+}
+function logHypotenuse(x, y) {
+ const a = Math.abs(x),
+ b = Math.abs(y);
- //------------------- 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 (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)));
+}
- if( line ){
-
- const lineTabs = countPrefixTabs( line )
- if( tabs > lineTabs ) tabs = lineTabs
- }
- })
- lines.forEach( function( line, i ){
+function hyperbolicSine(n) {
+ return (Math.exp(n) - Math.exp(-n)) / 2;
+}
- if( line.trim() === '' ) line = '\n\n'
- lines[ i ] = line.substring( tabs ).replace( / {2}$/, '\n' )
- })
- return lines.join( '' )
- },
- help: function( f ){
+function hyperbolicCosine(n) {
+ return (Math.exp(n) + Math.exp(-n)) / 2;
+}
- if( f === undefined ) f = Q
- return Q.extractDocumentation( f )
- },
- constants: {},
- createConstant: function( key, value ){
+function round(n, d) {
+ if (typeof d !== "number") d = 0;
+ const f = Math.pow(10, d);
+ return Math.round(n * f) / f;
+}
- //Object.freeze( value )
- this[ key ] = value
- // Object.defineProperty( this, key, {
+function isUsefulNumber(n) {
+ return (
+ isNaN(n) === false &&
+ (typeof n === "number" || n instanceof Number) &&
+ n !== Infinity &&
+ n !== -Infinity
+ );
+}
- // value,
- // writable: false
- // })
- // Object.defineProperty( this.constants, key, {
+function isUsefulInteger(n) {
+ return isUsefulNumber(n) && Number.isInteger(n);
+}
- // value,
- // writable: false
- // })
- this.constants[ key ] = this[ key ]
- Object.freeze( this[ key ])
- },
- createConstants: function(){
- if( arguments.length % 2 !== 0 ){
+module.exports = { isUsefulNumber, isUsefulInteger, hypotenuse, logHypotenuse, hyperbolicCosine, hyperbolicSine, round };
+
+},{}],5:[function(require,module,exports){
+(function (process,global){(function (){
+const logger = require('./Logging');
+
+const constants = {};
+function dispatchCustomEventToGlobal(event_name, detail, terminate_on_error=false, silent=true) {
+ try {
+ const event = new CustomEvent(event_name, detail);
+ if(typeof window != undefined) {
+ window.dispatchEvent(event);
+ }
+ else {
+ //if window does exist, global == window is true. So maybe we can just do global.dispatchEvent instead of this wrapper?
+ global.dispatchEvent(event);
+ if(!silent) console.log(event);
+ }
+ } catch(e) {
+ //When running in node, CustomEvent and documents don't exist. We can emulate using a JSDOM package
+ if(!silent) logger.error("Could not dispatch custom event.");
+ if(terminate_on_error) process.exit();
+ }
+
+}
- return Q.error( 'Q attempted to create constants with invalid (KEY, VALUE) pairs.' )
- }
- for( let i = 0; i < arguments.length; i += 2 ){
+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]);
+}
- this.createConstant( arguments[ i ], arguments[ i + 1 ])
- }
- },
+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];
+}
- isUsefulNumber: function( n ){
+function colorIndexToHue(i) {
+ return i * 10;
+}
- return isNaN( n ) === false &&
- ( typeof n === 'number' || n instanceof Number ) &&
- n !== Infinity &&
- n !== -Infinity
- },
- isUsefulInteger: function( n ){
+createConstants(
+ "REVISION",
+ 19,
+
+ // Yeah... F’ing floating point numbers, Man!
+ // Here’s the issue:
+ // var a = new Q.ComplexNumber( 1, 2 )
+ // a.multiply(a).isEqualTo( a.power( new Q.ComplexNumber( 2, 0 )))
+ // That’s only true if Q.EPSILON >= Number.EPSILON * 6
+
+ "EPSILON",
+ Number.EPSILON * 6,
+
+ "RADIANS_TO_DEGREES",
+ 180 / Math.PI,
+
+
+ "ANIMALS",
+ [
+ "Aardvark",
+ "Albatross",
+ "Alligator",
+ "Alpaca",
+ "Ant",
+ "Anteater",
+ "Antelope",
+ "Ape",
+ "Armadillo",
+ "Baboon",
+ "Badger",
+ "Barracuda",
+ "Bat",
+ "Bear",
+ "Beaver",
+ "Bee",
+ "Bison",
+ "Boar",
+ "Buffalo",
+ "Butterfly",
+ "Camel",
+ "Caribou",
+ "Cat",
+ "Caterpillar",
+ "Cattle",
+ "Chamois",
+ "Cheetah",
+ "Chicken",
+ "Chimpanzee",
+ "Chinchilla",
+ "Chough",
+ "Clam",
+ "Cobra",
+ "Cod",
+ "Cormorant",
+ "Coyote",
+ "Crab",
+ "Crane",
+ "Crocodile",
+ "Crow",
+ "Curlew",
+ "Deer",
+ "Dinosaur",
+ "Dog",
+ "Dogfish",
+ "Dolphin",
+ "Donkey",
+ "Dotterel",
+ "Dove",
+ "Dragonfly",
+ "Duck",
+ "Dugong",
+ "Dunlin",
+ "Eagle",
+ "Echidna",
+ "Eel",
+ "Eland",
+ "Elephant",
+ "Elephant seal",
+ "Elk",
+ "Emu",
+ "Falcon",
+ "Ferret",
+ "Finch",
+ "Fish",
+ "Flamingo",
+ "Fly",
+ "Fox",
+ "Frog",
+ "Galago",
+ "Gaur",
+ "Gazelle",
+ "Gerbil",
+ "Giant Panda",
+ "Giraffe",
+ "Gnat",
+ "Gnu",
+ "Goat",
+ "Goose",
+ "Goldfinch",
+ "Goldfish",
+ "Gorilla",
+ "Goshawk",
+ "Grasshopper",
+ "Grouse",
+ "Guanaco",
+ "Guinea fowl",
+ "Guinea pig",
+ "Gull",
+ "Guppy",
+ "Hamster",
+ "Hare",
+ "Hawk",
+ "Hedgehog",
+ "Hen",
+ "Heron",
+ "Herring",
+ "Hippopotamus",
+ "Hornet",
+ "Horse",
+ "Human",
+ "Hummingbird",
+ "Hyena",
+ "Ide",
+ "Jackal",
+ "Jaguar",
+ "Jay",
+ "Jellyfish",
+ "Kangaroo",
+ "Koala",
+ "Koi",
+ "Komodo dragon",
+ "Kouprey",
+ "Kudu",
+ "Lapwing",
+ "Lark",
+ "Lemur",
+ "Leopard",
+ "Lion",
+ "Llama",
+ "Lobster",
+ "Locust",
+ "Loris",
+ "Louse",
+ "Lyrebird",
+ "Magpie",
+ "Mallard",
+ "Manatee",
+ "Marten",
+ "Meerkat",
+ "Mink",
+ "Mole",
+ "Monkey",
+ "Moose",
+ "Mouse",
+ "Mosquito",
+ "Mule",
+ "Narwhal",
+ "Newt",
+ "Nightingale",
+ "Octopus",
+ "Okapi",
+ "Opossum",
+ "Oryx",
+ "Ostrich",
+ "Otter",
+ "Owl",
+ "Ox",
+ "Oyster",
+ "Panther",
+ "Parrot",
+ "Partridge",
+ "Peafowl",
+ "Pelican",
+ "Penguin",
+ "Pheasant",
+ "Pig",
+ "Pigeon",
+ "Pony",
+ "Porcupine",
+ "Porpoise",
+ "Prairie Dog",
+ "Quail",
+ "Quelea",
+ "Rabbit",
+ "Raccoon",
+ "Rail",
+ "Ram",
+ "Raven",
+ "Reindeer",
+ "Rhinoceros",
+ "Rook",
+ "Ruff",
+ "Salamander",
+ "Salmon",
+ "Sand Dollar",
+ "Sandpiper",
+ "Sardine",
+ "Scorpion",
+ "Sea lion",
+ "Sea Urchin",
+ "Seahorse",
+ "Seal",
+ "Shark",
+ "Sheep",
+ "Shrew",
+ "Shrimp",
+ "Skunk",
+ "Snail",
+ "Snake",
+ "Sow",
+ "Spider",
+ "Squid",
+ "Squirrel",
+ "Starling",
+ "Stingray",
+ "Stinkbug",
+ "Stork",
+ "Swallow",
+ "Swan",
+ "Tapir",
+ "Tarsier",
+ "Termite",
+ "Tiger",
+ "Toad",
+ "Trout",
+ "Tui",
+ "Turkey",
+ "Turtle",
+ // U
+ "Vicuña",
+ "Viper",
+ "Vulture",
+ "Wallaby",
+ "Walrus",
+ "Wasp",
+ "Water buffalo",
+ "Weasel",
+ "Whale",
+ "Wolf",
+ "Wolverine",
+ "Wombat",
+ "Woodcock",
+ "Woodpecker",
+ "Worm",
+ "Wren",
+ // X
+ "Yak",
+ "Zebra",
+ ],
+ "ANIMALS3",
+ [
+ "ape",
+ "bee",
+ "cat",
+ "dog",
+ "elk",
+ "fox",
+ "gup",
+ "hen",
+ "ide",
+ "jay",
+ "koi",
+ "leo",
+ "moo",
+ "nit",
+ "owl",
+ "pig",
+ // Q ?
+ "ram",
+ "sow",
+ "tui",
+ // U ?
+ // V ?
+ // W ?
+ // X ?
+ "yak",
+ "zeb",
+ ],
+ "COLORS",
+ [
+ "Red", // 0 RED
+ "Scarlet", // 10
+ "Tawny", // 20
+ "Carrot", // 30
+ "Pumpkin", // 40
+ "Mustard", // 50
+ "Lemon", // 60 Yellow
+ "Lime", // 70
+ "Spring bud", // 80
+ "Spring grass", // 90
+ "Pear", // 100
+ "Kelly", // 110
+ "Green", // 120 GREEN
+ "Malachite", // 130
+ "Sea green", // 140
+ "Sea foam", // 150
+ "Aquamarine", // 160
+ "Turquoise", // 170
+ "Cyan", // 180 Cyan
+ "Pacific blue", // 190
+ "Baby blue", // 200
+ "Ocean blue", // 210
+ "Sapphire", // 220
+ "Azure", // 230
+ "Blue", // 240 BLUE
+ "Cobalt", // 250
+ "Indigo", // 260
+ "Violet", // 270
+ "Lavender", // 280
+ "Purple", // 290
+ "Magenta", // 300 Magenta
+ "Hot pink", // 310
+ "Fuschia", // 320
+ "Ruby", // 330
+ "Crimson", // 340
+ "Carmine", // 350
+ ]
+);
+
+module.exports = { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchCustomEventToGlobal, constants };
+
+}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./Logging":3,"_process":2}],6:[function(require,module,exports){
- return Q.isUsefulNumber( n ) && Number.isInteger( n )
- },
- loop: function(){},
+// 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');
- 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 )
+Circuit = function( bandwidth, timewidth ){
- 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 ){
+ // What number Circuit is this
+ // that we’re attempting to make here?
+
+ this.index = Circuit.index ++
- return ( Math.exp( n ) - Math.exp( -n )) / 2
- },
- hyperbolicCosine: function( n ){
- return ( Math.exp( n ) + Math.exp( -n )) / 2
- },
- round: function( n, d ){
+ // How many qubits (registers) shall we use?
- if( typeof d !== 'number' ) d = 0
- const f = Math.pow( 10, d )
- return Math.round( n * f ) / f
- },
- toTitleCase: function( text ){
+ if( !mathf.isUsefulInteger( bandwidth )) bandwidth = 3
+ this.bandwidth = bandwidth
- 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 = ' '
+ // How many operations can we perform on each qubit?
+ // Each operation counts as one moment; one clock tick.
- const
- padLengthLeft = Math.floor(( length - text.length ) / 2 ),
- padLengthRight = length - text.length - padLengthLeft
+ if( !mathf.isUsefulInteger( timewidth )) timewidth = 5
+ this.timewidth = timewidth
- return text
- .padStart( padLengthLeft + text.length, filler )
- .padEnd( length, filler )
- }
- else return text
- },
+ // 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
+
- namesIndex: 0,
- shuffledNames: [],
- shuffleNames$: function(){
+ // When our circuit is evaluated
+ // we store those results in this array.
- let m = []
- for( let c = 0; c < Q.COLORS.length; c ++ ){
+ this.results = []
+ this.matrix = null
- for( let a = 0; a < Q.ANIMALS.length; a ++ ){
- m.push([ c, a, Math.random() ])
- }
- }
- Q.shuffledNames = m.sort( function( a, b ){
+ // Undo / Redo history.
+ this.history = new History( this )
- 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
- }
+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.
-Q.createConstants(
+ if( text.raw !== undefined ) text = ''+text.raw
+ return Circuit.fromTableTransposed(
- 'REVISION', 19,
+ 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 ){
- // 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
- ]
-)
+ //const matches = item.match( /(^\w+)(#(\w+))*(\.(\d+))*/ )
+ const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ )
+ return {
+
+ gateSymbol: matches[ 1 ],
+ operationMomentId: matches[ 3 ],
+ mappingIndex: +matches[ 5 ]
+ }
+ })
+ })
+ )
+ },
-console.log( `
- QQQQQQ
-QQ QQ
-QQ QQ
-QQ QQ
-QQ QQ QQ
-QQ QQ
- QQQQ ${Q.REVISION}
-https://quantumjavascript.app
-` )
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Working out a new syntax here... Patience please!
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ fromText2: function( text ){
+ text = `
+ H C C
+ I C1 C1
+ I X1 S1
+ I X1 S1`
-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
+ // This is a quick way to enable `fromText()`
+ // to return a default new Circuit().
- … -3 -2 -1 0 +1 +2 +3 …
- ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄
- √2 𒆠π
+ if( text === undefined ) return new Circuit()
- 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
+ // 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.
- … -3𒊠-2𒊠-1𒊠0𒊠+1𒊠+2𒊠+3𒊠…
- ┄───┴───┴───┴───┴───┴───┴───┴───┄
+ if( text.raw !== undefined ) text = ''+text.raw
- 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
+ text
+ .trim()
+ .split( /\r?\n/ )
+ .filter( function( item ){ return item.length })
+ .map( function( item, r ){
- 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.
+ return item
+ .trim()
+ .split( /[-+\s+=+]/ )
+ .filter( function( item ){ return item.length })
+ .map( function( item, m ){
- `
+ // +++++++++++++++++++++++
+ // need to map LETTER[] optional NUMBER ]
- if( real instanceof Q.ComplexNumber ){
+ const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ )
- 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 ++
-}
+ //const matches = item.match( /(^\w+)(#(\w+))*(\.(\d+))*/ )
+ // const matches = item.match( /(^\w+)(\.(\w+))*(#(\d+))*/ )
+ // return {
+
+ // gateSymbol: matches[ 1 ],
+ // operationMomentId: matches[ 3 ],
+ // mappingIndex: +matches[ 5 ]
+ // }
+ })
+ })
+ },
-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 ? '' : ' '
+ fromTableTransposed: function( table ){
+ const
+ bandwidth = table.length,
+ timewidth = table.reduce( function( max, moments ){
- // We need the absolute values of each.
+ 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 ++ ){
- let
- rAbsolute = Math.abs( rNumber ),
- iAbsolute = Math.abs( iNumber )
+ 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 ++ ){
- // And an absolute value string.
+ 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 ){
- let
- rText = rAbsolute.toString(),
- iText = iAbsolute.toString()
+ // We’ve found a sibling !
+ const operationsIndex = circuit.operations.findIndex( function( operation ){
- // Is this an IMAGINARY-ONLY number?
- // Don’t worry: -0 === 0.
+ return (
- if( rNumber === 0 ){
+ 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' ){
- 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
- }
-
+ const
+ gate = Gate.findBySymbol( operation.gateSymbol ),
+ registerIndices = []
- // 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
- },
+ if( mathf.isUsefulInteger( operation.mappingIndex )){
+
+ registerIndices[ operation.mappingIndex ] = registerIndex
+ }
+ else registerIndices[ 0 ] = registerIndex
+ circuit.operations.push({
-
-
-
- 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 )
+ gate,
+ momentIndex,
+ registerIndices,
+ isControlled: false,
+ operationMomentId: operation.operationMomentId
+ })
+ }
+ }
+ }
+ circuit.sort$()
+ return circuit
},
- 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 (
+ controlled: function( U ){
+
- Math.abs( a - b.real ) < Q.EPSILON &&
- Math.abs( b.imaginary ) < Q.EPSILON
- )
- },
- function( a, b ){
+ // we should really just replace this with a nice Matrix.copy({}) command!!!!
- return (
+ // console.log( 'U?', U )
- Math.abs( a.real - b ) < Q.EPSILON &&
- Math.abs( a.imaginary ) < Q.EPSILON
- )
- },
- function( a, b ){
+ const
+ size = U.getWidth(),
+ result = Matrix.createIdentity( size * 2 )
- return (
+ // console.log( 'U', U.toTsv() )
+ // console.log( 'size', size )
+ // console.log( 'result', result.toTsv() )
- Math.abs( a.real - b.real ) < Q.EPSILON &&
- Math.abs( a.imaginary - b.imaginary ) < Q.EPSILON
- )
+ 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())
- absolute: function( n ){
-
- return Q.hypotenuse( n.real, n.imaginary )
- },
- conjugate: function( n ){
+ 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 )
+
- 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 )
+
+ for( let i = 0; i < qubitIndices.length; i ++ ){
+
+ //qubitIndices[ i ] = ( circuitBandwidth - 1 ) - qubitIndices[ i ]
+ qubitIndices[ i ] -= 1
}
- else if( a instanceof Q.ComplexNumber ){
+ // console.log( 'qubits AFTER manipulation', qubitIndices )
- 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 )
+
+ qubitIndices.reverse()
+ for( let i = 0; i < circuitBandwidth; i ++ ){
+
+ if( qubitIndices.indexOf( i ) == -1 ){
+
+ _qubits.push( i )
+ }
}
- else return Q.error( 'Q.ComplexNumber attempted to', name, 'with something that is neither a Number or Q.ComplexNumber:', a )
- },
+ // console.log( 'qubitIndices vs _qubits:' )
+ // console.log( 'qubitIndices', qubitIndices )
+ // console.log( '_qubits', _qubits )
+
- sine: function( n ){
+ const result = new Matrix.createZero( 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(
+ // const X = numeric.rep([n, n], 0);
+ // const Y = numeric.rep([n, n], 0);
- 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(
+ let i = n
+ while( i -- ){
- t1.real - b,
- t1.imaginary + a
- ))
- return new Q.ComplexNumber( Math.PI / 2 - t2.imaginary, t2.real )
- },
- arcTangent: function( n ){
+ 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 ){
- const
- a = n.real,
- b = n.imaginary
+ // 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() )
- if( a === 0 ){
+ // console.log( 'before write$', result.toTsv())
- if( b === 1 ) return new Q.ComplexNumber( 0, Infinity )
- if( b === -1 ) return new Q.ComplexNumber( 0, -Infinity )
- }
+ // console.log( 'U.read at ', istar, jstar, '=', U.read( istar, jstar ).toText())
+ result.write$( i, j, U.read( istar, jstar ))
- const
- d = a * a + ( 1 - b ) * ( 1 - b ),
- t = Q.ComplexNumber.log( new Q.ComplexNumber(
-
- ( 1 - b * b - a * a ) / d,
- a / d * -2
+ // 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);
- ))
- return new Q.ComplexNumber( t.imaginary / 2, t.real / 2 )
+ // console.log( 'expanded matrix to:', result.toTsv() )
+ return result
},
+ evaluate: function( circuit ){
- power: function( a, b ){
+ // console.log( circuit.toDiagram() )
- if( Q.ComplexNumber.isNumberLike( a )) a = new Q.ComplexNumber( a )
- if( Q.ComplexNumber.isNumberLike( b )) b = new Q.ComplexNumber( b )
+ misc.dispatchCustomEventToGlobal(
+ 'Circuit.evaluate began', {
- // Anything raised to the Zero power is 1.
+ detail: { circuit }
+ }
+ );
- if( b.isZero() ) return Q.ComplexNumber.ONE
+ // Our circuit’s operations must be in the correct order
+ // before we attempt to step through them!
- // 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??
+ circuit.sort$()
- if( a.isZero() &&
- b.real > 0 &&
- b.imaginary >= 0 ){
- return Q.ComplexNumber.ZERO
- }
+ // Create a new matrix (or more precisely, a vector)
+ // that is a 1 followed by all zeros.
+ //
+ // ┌ ┐
+ // │ 1 │
+ // │ 0 │
+ // │ 0 │
+ // │ . │
+ // │ . │
+ // │ . │
+ // └ ┘
- // If our exponent is Real (has no Imaginary component)
- // then we’re really just raising to a power.
-
- if( b.imaginary === 0 ){
+ const state = new Matrix( 1, Math.pow( 2, circuit.bandwidth ))
+ state.write$( 0, 0, 1 )
- 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 ))
- }
- }
- }
+ // Create a state matrix from this circuit’s input qubits.
+
+ // const state2 = circuit.qubits.reduce( function( state, qubit, i ){
- 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
+ // if( i > 0 ) return state.multiplyTensor( qubit )
+ // else return state
- return new Q.ComplexNumber(
+ // }, circuit.qubits[ 0 ])
+ // console.log( 'Initial state', state2.toTsv() )
+ // console.log( 'multiplied', state2.multiplyTensor( state ).toTsv() )
- 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(
+ const operationsTotal = circuit.operations.length
+ let operationsCompleted = 0
+ let matrix = circuit.operations.reduce( function( state, operation, i ){
- 'multiply', a, b,
- function( a, b ){
-
- return new Q.ComplexNumber( a * b )
- },
- function( a, b ){
- return new Q.ComplexNumber(
+ let U
+ if( operation.registerIndices.length < Infinity ){
+
+ if( operation.isControlled ){
+ //if( operation.registerIndices.length > 1 ){
- a * b.real,
- a * b.imaginary
- )
- },
- function( a, b ){
+ // 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() )
- 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.
+ // 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 ++ ){
- return new Q.ComplexNumber(
-
- firsts + lasts,
- outers + inners
- )
+ U = Circuit.controlled( U )
+ // console.log( 'qubitIndex #', j, 'U = Circuit.controlled( U )', U.toTsv() )
+ }
}
- )
- },
- 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 ){
+ // 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!
- return new Q.ComplexNumber(
+ const registerIndices = operation.registerIndices.slice()
+ state = Circuit.expandMatrix(
- a.real / b,
- a.imaginary / b
- )
- },
- function( a, b ){
+ circuit.bandwidth,
+ U,
+ registerIndices
+ ).multiply( state )
+
- // 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 ),
+ operationsCompleted ++
+ const progress = operationsCompleted / operationsTotal
- // The .imaginary will be ZERO for sure,
- // so this forces a ComplexNumber.divide( Number ) ;)
-
- denominator = b.multiply( conjugate ).real
+ misc.dispatchCustomEventToGlobal('Circuit.evaluate progressed', { detail: {
- return numerator.divide( denominator )
- }
- )
- },
- add: function( a, b ){
-
- return Q.ComplexNumber.operate(
+ circuit,
+ progress,
+ operationsCompleted,
+ operationsTotal,
+ momentIndex: operation.momentIndex,
+ registerIndices: operation.registerIndices,
+ gate: operation.gate.name,
+ state
- 'add', a, b,
- function( a, b ){
+ }})
- return new Q.ComplexNumber( a + b )
- },
- function( a, b ){
- return new Q.ComplexNumber(
+ // 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' )
+
- b.real + a,
- b.imaginary
- )
- },
- function( a, b ){
+ return state
+
+ }, state )
- 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(
+ const outcomes = matrix.rows.reduce( function( outcomes, row, i ){
- 'subtract', a, b,
- function( a, b ){
+ outcomes.push({
- return new Q.ComplexNumber( a - b )
- },
- function( a, b ){
+ state: '|'+ parseInt( i, 10 ).toString( 2 ).padStart( circuit.bandwidth, '0' ) +'⟩',
+ probability: Math.pow( row[ 0 ].absolute(), 2 )
+ })
+ return outcomes
+
+ }, [] )
- return new Q.ComplexNumber(
- b.real - a,
- b.imaginary
- )
- },
- function( a, b ){
- return new Q.ComplexNumber(
+ circuit.needsEvaluation = false
+ circuit.matrix = matrix
+ circuit.results = outcomes
- a.real - b,
- a.imaginary
- )
- },
- function( a, b ){
- return new Q.ComplexNumber(
- a.real - b.real,
- a.imaginary - b.imaginary
- )
- }
- )
- }
-})
+ misc.dispatchCustomEventToGlobal('Circuit.evaluate completed', { detail: {
+ // circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: {
+ circuit,
+ results: outcomes
+ }})
-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 )
-)
+
+ return matrix
+ }
+})
+
-Object.assign( Q.ComplexNumber.prototype, {
- // NON-destructive operations.
+Object.assign( Circuit.prototype, {
clone: function(){
- return new Q.ComplexNumber( this.real, this.imaginary )
- },
- reduce: function(){
+ const
+ original = this,
+ clone = original.copy()
+ clone.qubits = original.qubits.slice()
+ clone.results = original.results.slice()
+ clone.needsEvaluation = original.needsEvaluation
- // Note: this *might* kill function chaining.
-
- if( this.imaginary === 0 ) return this.real
- return this
+ return clone
},
- toText: function( roundToDecimal, padPositive ){
-
-
- // Note: this will kill function chaining.
+ evaluate$: function(){
- return Q.ComplexNumber.toText( this.real, this.imaginary, roundToDecimal, padPositive )
+ Circuit.evaluate( this )
+ return this
},
+ report$: function( length ){
-
- isNaN: function( n ){
+ if( this.needsEvaluation ) this.evaluate$()
+ if( !mathf.isUsefulInteger( length )) length = 20
- return Q.ComplexNumber.isNaN( this )// Returned boolean will kill function chaining.
- },
- isZero: function( n ){
+ const
+ circuit = this,
+ text = this.results.reduce( function( text, outcome, i ){
- return Q.ComplexNumber.isZero( this )// Returned boolean will kill function chaining.
- },
- isFinite: function( n ){
+ const
+ probabilityPositive = Math.round( outcome.probability * length ),
+ probabilityNegative = length - probabilityPositive
- 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 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'
- return Q.ComplexNumber.areEqual( this, b )// Returned boolean will kill function chaining.
+ }, '' ) + '\n'
+ return text
},
+ try$: function(){
+ if( this.needsEvaluation ) this.evaluate$()
- absolute: function(){
-
- return Q.ComplexNumber.absolute( this )// Returned number will kill function chaining.
- },
- conjugate: function(){
-
- return Q.ComplexNumber.conjugate( this )
- },
-
+
+ // We need to “stack” our probabilities from 0..1.
+
+ const outcomesStacked = new Array( this.results.length )
+ this.results.reduce( function( sum, outcome, i ){
- power: function( b ){
+ sum += outcome.probability
+ outcomesStacked[ i ] = sum
+ return sum
+
+ }, 0 )
+
- return Q.ComplexNumber.power( this, b )
- },
- squareRoot: function(){
+ // 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 Q.ComplexNumber.squareRoot( this )
- },
- log: function(){
+ return randomNumber <= index
+ })
+
- return Q.ComplexNumber.log( this )
+ // 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
},
- 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 )
- },
+ ////////////////
+ // //
+ // Output //
+ // //
+ ////////////////
+ // This is absolutely required by toTable.
+ sort$: function(){
- // 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(){
+ // Sort this circuit’s operations
+ // primarily by momentIndex,
+ // then by the first registerIndex.
- return this.copy$( this.conjugate() )
- },
- power$: function( b ){
+ this.operations.sort( function( a, b ){
- return this.copy$( this.power( b ))
- },
- squareRoot$: function(){
+ if( a.momentIndex === b.momentIndex ){
- return this.copy$( this.squareRoot() )
- },
- log$: function(){
- return this.copy$( this.log() )
- },
- multiply$: function( b ){
+ // 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 this.copy$( this.multiply( b ))
- },
- divide$: function( b ){
+ return Math.min( ...a.registerIndices ) - Math.min( b.registerIndices )
+ }
+ else {
- return this.copy$( this.divide( b ))
+ return a.momentIndex - b.momentIndex
+ }
+ })
+ return this
},
- add$: function( b ){
+
- return this.copy$( this.add( b ))
- },
- subtract$: function( b ){
- return this.copy$( this.subtract( b ))
- }
-})
+ ///////////////////
+ // //
+ // Exporters //
+ // //
+ ///////////////////
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ // 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
-Q.Matrix = function(){
+ // Sure, this is equal to table.length
+ // but isn’t legibility and convenience everything?
- // We’re keeping track of how many matrices are
- // actually being generated. Just curiosity.
+ table.timewidth = this.timewidth
+
- this.index = Q.Matrix.index ++
+ // 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
+
- let matrixWidth = null
+ // 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 ){
- // 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 ])){
+ const operations = new Array( circuit.bandwidth )
+ operations.fill( 0 ).forEach( function( element, index, array ){
- matrixWidth = arguments[ 0 ]
- this.rows = new Array( matrixWidth ).fill( 0 ).map( function(){
+ array[ index ] = {
- return new Array( matrixWidth ).fill( 0 )
+ symbol: 'I',
+ symbolDisplay: 'I',
+ name: 'Identity',
+ nameCss: 'identity',
+ gateInputIndex: 0,
+ bandwidth: 0,
+ thisGateAmongMultiQubitGatesIndex: 0,
+ aSiblingIsAbove: false,
+ aSiblingIsBelow: false
+ }
+ })
+ array[ index ] = operations
})
- }
- 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 {
+ // 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 = {}
- // 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.
+ this.operations.forEach( function( operation, operationIndex, operations ){
- 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 )
- }
+ // 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 ] ++
- // But for convenience we can also organize by columns.
- // Note this represents the transposed version of itself!
+ // By default, an operation’s CSS name
+ // is its regular name, all lowercase,
+ // with all spaces replaced by hyphens.
- 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 )
- }
+ let nameCss = operation.gate.name.toLowerCase().replace( /\s+/g, '-' )
- // console.log( x, y, matrix.rows[ y ][ x ])
+ operation.registerIndices.forEach( function( registerIndex, indexAmongSiblings ){
- Object.defineProperty( column, y, {
-
- get: function(){ return matrix.rows[ y ][ x ]},
- set: function( n ){ matrix.rows[ y ][ x ] = n }
- })
- }
- this.columns.push( column )
- }
-}
-
-
-
+ let isMultiRegisterOperation = false
+ if( operation.registerIndices.length > 1 ){
+ isMultiRegisterOperation = true
+ if( indexAmongSiblings === operation.registerIndices.length - 1 ){
+ nameCss = 'target'
+ }
+ else {
- ///////////////////////////
- // //
- // Static properties //
- // //
-///////////////////////////
+ nameCss = 'control'
+ }
+ // May need to re-visit the code above in consideration of SWAPs.
-Object.assign( Q.Matrix, {
+ }
+ table[ operation.momentIndex - 1 ][ registerIndex - 1 ] = {
- 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,
+ 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
+ }
+ })
+/*
- 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 ){
+Non-fatal problem to solve here:
- return matrix.columns.length
- },
- getHeight: function( matrix ){
+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!!!
- 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 )
- },
+ // if( operationIndex === operations.length - 1 ){
+
+ table[ momentIndex - 1 ].gateTypesUsedThisMoment = gateTypesUsedThisMoment
+ // }
+ })
- 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.
+ table.forEach( function( moment, m ){
- from: function( format ){
+ moment.forEach( function( operation, o ){
- 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 ){
+ if( operation.isMultiRegisterOperation ){
- return new Q.Matrix( ...array )
- },
- fromXsv: function( input, rowSeparator, valueSeparator ){
+ if( moment.gateTypesUsedThisMoment[ operation.symbol ] > 1 ){
- `
- 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).
+ operation.symbolDisplay = operation.symbol +'.'+ ( operation.gatesOfThisTypeNow - 1 )
+ }
+ operation.symbolDisplay += '#'+ operation.indexAmongSiblings
+ }
+ })
+ })
- `
- if( typeof rowSeparator !== 'string' ) rowSeparator = '\n'
- if( typeof valueSeparator !== 'string' ) valueSeparator = '\t'
+ // Now we can easily read down each moment
+ // and establish the moment’s character width.
+ // Very useful for text-based diagrams ;)
- const
- inputRows = input.split( rowSeparator ),
- outputRows = []
+ table.forEach( function( moment ){
- inputRows.forEach( function( inputRow ){
+ const maximumWidth = moment.reduce( function( maximumWidth, operation ){
- inputRow = inputRow.trim()
- if( inputRow === '' ) return
+ return Math.max( maximumWidth, operation.symbolDisplay.length )
- const outputRow = []
- inputRow.split( valueSeparator ).forEach( function( cellValue ){
-
- outputRow.push( parseFloat( cellValue ))
- })
- outputRows.push( outputRow )
+ }, 1 )
+ moment.maximumCharacterWidth = maximumWidth
})
- 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 ){
+ // 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 )
- return Q.Matrix.fromXsv(
- html
- .replace( /\r?\n|\r||/g, '' )
- .replace( /<\/td>(\s*)<\/tr>/g, ' ' )
- .match( /(.*)<\/table>/i )[ 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( '' )
- // Export TO a format.
+ for( let x = 0; x < table.timewidth; x ++ ){
- toXsv: function( matrix, rowSeparator, valueSeparator ){
-
- return matrix.rows.reduce( function( xsv, row ){
+ for( let y = 0; y < table.bandwidth; y ++ ){
- return xsv + rowSeparator + row.reduce( function( xsv, cell, c ){
+ let cellString = table[ x ][ y ].symbolDisplay.padEnd( table[ x ].maximumCharacterWidth, '-' )
+ if( makeAllMomentsEqualWidth && x < table.timewidth - 1 ){
- return xsv + ( c > 0 ? valueSeparator : '' ) + cell.toText()
-
- }, '' )
-
- }, '' )
+ 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' )
},
- toCsv: function( matrix ){
+ toDiagram: function( makeAllMomentsEqualWidth ){
- return Q.Matrix.toXsv( matrix, '\n', ',' )
- },
- toTsv: function( matrix ){
+ `
+ Create a text representation of this circuit
+ using fancy box-drawing characters.
+ `
- return Q.Matrix.toXsv( matrix, '\n', '\t' )
- },
+ 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
- // Operate NON-destructive.
+ output[ 0 ] += logger.centerText( 'm'+ ( x + 1 ), padToLength + 4 )
+ for( let y = 0; y < table.bandwidth; y ++ ){
- add: function( matrix0, matrix1 ){
+ let
+ operation = table[ x ][ y ],
+ first = '',
+ second = '',
+ third = ''
- if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
- Q.Matrix.isMatrixLike( matrix1 ) !== true ){
+ if( operation.symbol === 'I' ){
- 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}.`)
+ first += ' '
+ second += '──'
+ third += ' '
+
+ first += ' '.padEnd( padToLength )
+ second += logger.centerText( '○', padToLength, '─' )
+ third += ' '.padEnd( padToLength )
- return new Q.Matrix( ...matrix0.rows.reduce( function( resultMatrixRow, row, r ){
+ first += ' '
+ if( x < table.timewidth - 1 ) second += '──'
+ else second += ' '
+ third += ' '
+ }
+ else {
- resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue, c ){
+ if( operation.isMultiRegisterOperation ){
- // resultMatrixColumn.push( cellValue + matrix1.rows[ r ][ c ])
- resultMatrixColumn.push( cellValue.add( matrix1.rows[ r ][ c ]))
- return resultMatrixColumn
+ first += '╭─'
+ third += '╰─'
+ }
+ else {
+
+ first += '┌─'
+ third += '└─'
+ }
+ second += '┤ '
+
+ first += '─'.padEnd( padToLength, '─' )
+ second += logger.centerText( operation.symbolDisplay, padToLength )
+ third += '─'.padEnd( padToLength, '─' )
- }, [] ))
- return resultMatrixRow
- }, [] ))
- },
- multiplyScalar: function( matrix, scalar ){
+ if( operation.isMultiRegisterOperation ){
- if( Q.Matrix.isMatrixLike( matrix ) !== true ){
+ first += '─╮'
+ third += '─╯'
+ }
+ else {
- return Q.error( `Q.Matrix attempted to scale something that was not a matrix.` )
- }
- if( typeof scalar !== 'number' ){
+ first += '─┐'
+ third += '─┘'
+ }
+ second += x < table.timewidth - 1 ? ' ├' : ' │'
- 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 ){
+ if( operation.isMultiRegisterOperation ){
- resultMatrixRow.push( row.reduce( function( resultMatrixColumn, cellValue ){
+ let n = ( operation.multiRegisterOperationIndex * 2 ) % ( table[ x ].maximumCharacterWidth + 1 ) + 1
+ if( operation.siblingExistsAbove ){
- // resultMatrixColumn.push( cellValue * scalar )
- resultMatrixColumn.push( cellValue.multiply( scalar ))
- return resultMatrixColumn
-
- }, [] ))
- return resultMatrixRow
+ 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' )
},
- 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
- `
+ // Oh yes my friends... WebGL is coming!
- if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
- Q.Matrix.isMatrixLike( matrix1 ) !== true ){
+ toShader: function(){
- return Q.error( `Q.Matrix attempted to multiply something that was not a matrix.` )
- }
- if( matrix0.columns.length !== matrix1.rows.length ){
+ },
+ toGoogleCirq: function(){
+/*
- 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
+cirq.GridQubit(4,5)
- const sum = new Q.ComplexNumber()
- matrix1Column.forEach( function( matrix1CellValue, index ){// Work down the column of OTHER matrix
+https://cirq.readthedocs.io/en/stable/tutorial.html
- sum.add$( matrix0Row[ index ].multiply( matrix1CellValue ))
- })
- resultMatrixRow.push( sum )
- })
- resultMatrix.push( resultMatrixRow )
- })
- //return new Q.Matrix( ...resultMatrix )
- return new this( ...resultMatrix )
+*/
+ const header = `import cirq`
+
+ return headers
},
- multiplyTensor: function( matrix0, matrix1 ){
+ toAmazonBraket: function(){
+ let isValidBraketCircuit = true
+ const header = `import boto3
+from braket.aws import AwsDevice
+from braket.circuits import Circuit
+from braket.devices import LocalSimulator
- `
- https://en.wikipedia.org/wiki/Kronecker_product
- https://en.wikipedia.org/wiki/Tensor_product
- `
+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
+ }
- if( Q.Matrix.isMatrixLike( matrix0 ) !== true ||
- Q.Matrix.isMatrixLike( matrix1 ) !== true ){
+ 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 Q.error( `Q.Matrix attempted to tensor something that was not a matrix.` )
- }
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
- const
- resultMatrix = [],
- resultMatrixWidth = matrix0.columns.length * matrix1.columns.length,
- resultMatrixHeight = matrix0.rows.length * matrix1.rows.length
+ }, '' ) + ')'
+ }
+ 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 ){
- for( let y = 0; y < resultMatrixHeight; y ++ ){
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
- const resultMatrixRow = []
- for( let x = 0; x < resultMatrixWidth; x ++ ){
+ }, '' ) + ')'
+ }
+ // 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 ){
- 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
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
- resultMatrixRow.push(
+ }, '' ) + ((operation.gate.has_parameters) ?
+ Object.values( operation.gate.parameters ).reduce( function( string, parameter ) {
+ return string + "," + parameter
+ }, '')
+ : '') + ')'
- //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 )
- }
-})
+ }, '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
+ }
+ */
- //////////////////////////////
- // //
- // Prototype properties //
- // //
-//////////////////////////////
+ return '\\Qcircuit @C=1.0em @R=0.7em {\n' +
+ this.toTable()
+ .reduce( function( array, moment, m ){
+ moment.forEach( function( operation, o, operations ){
-Object.assign( Q.Matrix.prototype, {
+ let command = 'qw'
+ if( operation.symbol !== 'I' ){
- isValidRow: function( r ){
+ if( operation.isMultiRegisterOperation ){
- return Q.Matrix.isWithinRange( r, 0, this.rows.length - 1 )
- },
- isValidColumn: function( c ){
+ if( operation.indexAmongSiblings === 0 ){
- return Q.Matrix.isWithinRange( c, 0, this.columns.length - 1 )
- },
- isValidAddress: function( x, y ){
+ 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 this.isValidRow( y ) && this.isValidColumn( x )
- },
- getWidth: function(){
+ return Math.max( maximumCharacterWidth, operation.latexCommand.length )
+
+ }, 0 )
+ moment.forEach( function( operation, o ){
- return Q.Matrix.getWidth( this )
- },
- getHeight: function(){
+ array[ o ] += '& \\'+ operation.latexCommand.padEnd( maximumCharacterWidth ) +' '
+ })
+ return array
- return Q.Matrix.getHeight( this )
+ }, new Array( this.bandwidth ).fill( '\n\t' ))
+ .join( '\\\\' ) +
+ '\n}'
},
- // 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 )
- },
+ //////////////
+ // //
+ // Edit //
+ // //
+ //////////////
- toArray: function(){
+ get: function( momentIndex, registerIndex ){
- return this.rows
- },
- toXsv: function( rowSeparator, valueSeparator ){
-
- return Q.Matrix.toXsv( this, rowSeparator, valueSeparator )
- },
- toCsv: function(){
+ return this.operations.find( function( op ){
- return Q.Matrix.toXsv( this, '\n', ',' )
+ return op.momentIndex === momentIndex &&
+ op.registerIndices.includes( registerIndex )
+ })
},
- toTsv: function(){
+ clear$: function( momentIndex, registerIndices ){
- return Q.Matrix.toXsv( this, '\n', '\t' )
- },
- toHtml: function(){
-
- return this.rows.reduce( function( html, row ){
+ const circuit = this
- return html + row.reduce( function( html, cell ){
- return html +'\n\t\t'+ cell.toText() +' '
-
- }, '\n\t' ) + '\n\t '
+ // Validate our arguments.
- }, '\n'
- },
-
-
-
+ 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 )
- // Write is DESTRUCTIVE by nature. Not cuz I hate ya.
- write$: function( x, y, n ){
+ // 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.)
- `
- Equivalent to
- this.columns[ x ][ y ] = n
- or
- this.rows[ y ][ x ] = n
- but with safety checks.
- `
+ const foundOperations = circuit.operations.reduce( function( filtered, operation, o ){
- if( this.isValidAddress( x, y )){
+ if( operation.momentIndex === momentIndex &&
+ operation.registerIndices.some( function( registerIndex ){
- 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 ){
+ return registerIndices.includes( registerIndex )
+ })
+ ) filtered.push({
- 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 )
+ index: o,
+ momentIndex: operation.momentIndex,
+ registerIndices: operation.registerIndices,
+ gate: operation.gate
+ })
+ return filtered
- 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 ){
+ // 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.
- return this.copy$( Q.Matrix.fromArray( array ))
- },
- fromCsv$: function( csv ){
+ foundOperations.reduce( function( deletionsSoFar, operation ){
- return this.copy$( Q.Matrix.fromCsv( csv ))
- },
- fromTsv$: function( tsv ){
+ circuit.operations.splice( operation.index - deletionsSoFar, 1 )
+ return deletionsSoFar + 1
- return this.copy$( Q.Matrix.fromTsv( tsv ))
- },
- fromHtml$: function( html ){
+ }, 0 )
- return this.copy$( Q.Matrix.fromHtml( html ))
- },
+ // IMPORTANT!
+ // Operations must be sorted properly
+ // for toTable to work reliably with
+ // multi-register operations!!
+
+ this.sort$()
+ // Let’s make history.
- // Operate NON-destructive.
+ if( foundOperations.length ){
- add: function( otherMatrix ){
+ this.history.record$({
- return Q.Matrix.add( this, otherMatrix )
- },
- multiplyScalar: function( scalar ){
+ redo: {
+
+ name: 'clear$',
+ func: circuit.clear$,
+ args: Array.from( arguments )
+ },
+ undo: foundOperations.reduce( function( undos, operation ){
- return Q.Matrix.multiplyScalar( this, scalar )
- },
- multiply: function( otherMatrix ){
+ undos.push({
- return Q.Matrix.multiply( this, otherMatrix )
- },
- multiplyTensor: function( otherMatrix ){
+ name: 'set$',
+ func: circuit.set$,
+ args: [
- return Q.Matrix.multiplyTensor( this, otherMatrix )
- },
+ 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 ){
- // Operate DESTRUCTIVE.
+ misc.dispatchCustomEventToGlobal(
- add$: function( otherMatrix ){
+ 'Circuit.clear$', { detail: {
- return this.copy$( this.add( otherMatrix ))
+ circuit,
+ momentIndex,
+ registerIndices: operation.registerIndices
+ }}
+ )
+ })
+ }
+
+
+ // Enable that “fluent interface” method chaining :)
+
+ return circuit
},
- multiplyScalar$: function( scalar ){
+
- return this.copy$( this.multiplyScalar( scalar ))
- }
-})
+ 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
- //////////////////////////
- // //
- // Static constants //
- // //
-//////////////////////////
+ // 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 )
-Q.Matrix.createConstants(
+ // Is this a valid moment index?
+
+ if( mathf.isUsefulNumber( momentIndex ) !== true ||
+ Number.isInteger( momentIndex ) !== true ||
+ momentIndex < 1 || momentIndex > this.timewidth ){
- 'IDENTITY_2X2', Q.Matrix.createIdentity( 2 ),
- 'IDENTITY_3X3', Q.Matrix.createIdentity( 3 ),
- 'IDENTITY_4X4', Q.Matrix.createIdentity( 4 ),
+ return logger.error( `Circuit attempted to add a gate to circuit #${ this.index } at a moment index that is not valid:`, momentIndex )
+ }
- 'CONSTANT0_2X2', new Q.Matrix(
- [ 1, 1 ],
- [ 0, 0 ]),
- 'CONSTANT1_2X2', new Q.Matrix(
- [ 0, 0 ],
- [ 1, 1 ]),
+ // Are these valid register indices?
- 'NEGATION_2X2', new Q.Matrix(
- [ 0, 1 ],
- [ 1, 0 ]),
+ 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 ){
- '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 ])
-)
+ // 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 )
+ }
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ // Ok, now we can check if this set$ command
+ // is redundant.
+ const
+ isRedundant = !!circuit.operations.find( function( operation ){
+ return (
-Q.Qubit = function( a, b, symbol, name ){
-
+ momentIndex === operation.momentIndex &&
+ gate === operation.gate &&
+ registerIndices.length === operation.registerIndices.length &&
+ registerIndices.every( val => operation.registerIndices.includes( val ))
+ )
+ })
- // 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 ){
+ // If it’s NOT redundant
+ // then we’re clear to proceed.
- b = a.rows[ 1 ][ 0 ]
- a = a.rows[ 0 ][ 0 ]
- }
- else {
+ if( isRedundant !== true ){
- // All of our internal math now uses complex numbers
- // rather than Number literals
- // so we’d better convert!
+ // 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 )
+
- if( typeof a === 'number' ) a = new Q.ComplexNumber( a, 0 )
- if( typeof b === 'number' ) b = new Q.ComplexNumber( b, 0 )
+ // 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 = {
- // 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.
+ gate,
+ momentIndex,
+ registerIndices,
+ isControlled
+ }
+ //perform parameter update here!!!
+ if(gate.has_parameters) gate.updateMatrix$.apply( gate, Object.values(parameters) )
+ this.operations.push( operation )
- if( a instanceof Q.ComplexNumber !== true ) a = new Q.ComplexNumber( 1, 0 )
- if( b instanceof Q.ComplexNumber !== true ){
+
+ // IMPORTANT!
+ // Operations must be sorted properly
+ // for toTable to work reliably with
+ // multi-register operations!!
+
+ this.sort$()
- // 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?
+ // Let’s make history.
+ const redo_args = Array.from( arguments )
+ Object.assign( redo_args[ redo_args.length - 1 ], parameters )
+ this.history.record$({
- b = Q.ComplexNumber.ONE.subtract( Math.pow( a.absolute(), 2 )).squareRoot()
- }
- }
+ redo: {
+
+ name: 'set$',
+ func: circuit.set$,
+ args: redo_args
+ },
+ undo: [{
+ name: 'clear$',
+ func: circuit.clear$,
+ args: [ momentIndex, registerIndices ]
+ }]
+ })
- // Sanity check!
- // Does this constraint hold true? |ð’‚|² + |ð’ƒ|² = 1
+
+ // Emit an event that we have set an operation
+ // on this circuit.
- 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.` )
+ misc.dispatchCustomEventToGlobal(
- Q.Matrix.call( this, [ a ],[ b ])
- this.index = Q.Qubit.index ++
+ 'Circuit.set$', { detail: {
+ circuit,
+ operation
+ }}
+ )
+ }
+ return circuit
+ },
- // 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 }
- })
+ determineRanges: function( options ){
+ if( options === undefined ) options = {}
+ let {
- // Used for Dirac notation: |?⟩
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
- if( typeof symbol === 'string' ) this.symbol = symbol
- if( typeof name === 'string' ) this.name = name
- if( this.symbol === undefined || this.name === undefined ){
+ } = options
- const found = Object.values( Q.Qubit.constants ).find( function( qubit ){
+ 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.` )
- return (
+ 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.` )
- a.isEqualTo( qubit.alpha ) &&
- b.isEqualTo( qubit.beta )
- )
- })
- if( found === undefined ){
+ 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'
+ )
- this.symbol = '?'
- this.name = 'Unnamed'
- }
- else {
+ return {
- if( this.symbol === undefined ) this.symbol = found.symbol
- if( this.name === undefined ) this.name = found.name
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
}
- }
-}
-Q.Qubit.prototype = Object.create( Q.Matrix.prototype )
-Q.Qubit.prototype.constructor = Q.Qubit
+ },
+ copy: function( options, isACutOperation ){
+ const original = this
+ let {
-Object.assign( Q.Qubit, {
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
- index: 0,
- help: function(){ return Q.help( this )},
- constants: {},
- createConstant: Q.createConstant,
- createConstants: Q.createConstants,
-
+ } = this.determineRanges( options )
+ const copy = new Circuit( qubitRange, momentRange )
+ original.operations
+ .filter( function( operation ){
+ return ( operation.registerIndices.every( function( registerIndex ){
- findBy: function( key, value ){
+ return (
- return (
-
- Object
- .values( Q.Qubit.constants )
- .find( function( item ){
+ operation.momentIndex >= momentFirstIndex &&
+ operation.momentIndex < momentLastIndex &&
+ operation.registerIndex >= qubitFirstIndex &&
+ operation.registerIndex < qubitLastIndex
+ )
+ }))
+ })
+ .forEach( function( operation ){
- if( typeof value === 'string' &&
- typeof item[ key ] === 'string' ){
+ const adjustedRegisterIndices = operation.registerIndices.map( function( registerIndex ){
- return value.toLowerCase() === item[ key ].toLowerCase()
- }
- return value === item[ key ]
+ return registerIndex - registerFirstIndex
})
- )
- },
- findBySymbol: function( symbol ){
+ copy.set$(
- return Q.Qubit.findBy( 'symbol', symbol )
- },
- findByName: function( name ){
+ operation.gate,
+ 1 + m - momentFirstIndex,
+ adjustedRegisterIndices
+ )
+ })
- return Q.Qubit.findBy( 'name', name )
- },
- findByBeta: function( beta ){
- if( beta instanceof Q.ComplexNumber === false ){
+ // 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.
- beta = new Q.ComplexNumber( beta )
- }
- return Object.values( Q.Qubit.constants ).find( function( qubit ){
+ // UPDATE !!!!
+ // will come back to fix!!
+ // with new style it's now just a matter of
+ // splicing out these out of circuit.operations
- return qubit.beta.isEqualTo( beta )
- })
- },
- areEqual: function( qubit0, qubit1 ){
- return (
+
+ if( isACutOperation === true ){
- qubit0.alpha.isEqualTo( qubit1.alpha ) &&
- qubit0.beta.isEqualTo( qubit1.beta )
- )
- },
- collapse: function( qubit ){
+ /*
+ for( let m = momentFirstIndex; m < momentLastIndex; m ++ ){
- 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 ){
+ original.moments[ m ] = new Array( original.bandwidth )
+ .fill( 0 )
+ .map( function( qubit, q ){
- return new Q.Qubit( 1, 0 )
+ return {
+
+ gate: Q.Gate.IDENTITY,
+ registerIndices: [ q ]
+ }
+ })
+ }*/
}
- else return new Q.Qubit( 0, 1 )
+ return copy
},
- 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:
- `
+ cut$: function( options ){
- 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 )
+ return this.copy( options, true )
},
- 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
+ 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?)
-
- // 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
- }
+ */
+ spliceCut$: function( options ){
- // Bloch vector’s axes are wonked.
- // Let’s “correct†them for use with Three.js, etc.
+ let {
- const position = {
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
- x: vector.y,
- y: vector.z,
- z: vector.x
- }
+ } = this.determineRanges( options )
- return {
+ // 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).
- // Wow does this make tweening easier down the road.
+ if( qubitRange !== this.bandwidth &&
+ momentRange !== this.timewidth ){
- alphaReal: qubit.alpha.real,
- alphaImaginary: qubit.alpha.imaginary,
- betaReal: qubit.beta.real,
- betaImaginary: qubit.beta.imaginary,
+ return logger.error( `Circuit attempted to splice circuit #${this.index} by an area that did not include all qubits _or_ all moments.` )
+ }
- // Ummm... I’m only returnig the REAL portions. Please forgive me!
+ // If the selection area covers all qubits for 1 or more moments
+ // then splice the moments array.
+
+ if( qubitRange === this.bandwidth ){
- 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 ){
+ // 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 ){
- //basically from a Pauli Rotation
- }
+ 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 ){
-Q.Qubit.createConstants(
+ // First, let’s splice the inputs array.
+ this.inputs.splice( qubitFirstIndex, qubitRange )
+ //@@ this.inputs.splice( qubitFirstIndex, qubitRange, qubitsToPaste?? )
+
- // Opposing pairs:
- // |H⟩ and |V⟩
- // |D⟩ and |A⟩
- // |R⟩ and |L⟩
+ // Now we can make the proper adjustments
+ // to each of our moments.
- '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
-)
+ 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 ){
-Object.assign( Q.Qubit.prototype, {
+ operation.qubitIndices = operation.qubitIndices.map( function( index ){
- copy$: function( matrix ){
+ return index >= qubitLastIndex ? index - qubitRange : index
+ })
+ return operation
+ })
+ })
+ this.bandwidth -= qubitRange
+ }
+
- 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 )
+ // Final clean up.
- 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 )
+ this.removeHangingOperations$()
+ this.fillEmptyOperations$()
- 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
+ return this// Or should we return the cut area?!
},
- clone: function(){
+ splicePaste$: 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(){
+ // 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!
- 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 ){
+ paste$: function( other, atMoment = 0, atQubit = 0, shouldClean = true ){
- return this.copy$( Q.Qubit.applyGate( this, gate ))
- },
-})
+ 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$(
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ 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.' )
+
-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.
+ if( shouldClean ) this.removeHangingOperations$()
+ this.fillEmptyOperations$()
+ return this
- const
- scope = this,
- foundConstant = Object
- .values( Q.Gate.constants )
- .find( function( gate ){
+ },
+ expand$: function(){
- return gate.symbol === scope.symbol
- })
+ // expand either bandwidth or timewidth, fill w identity
- //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'
+ this.fillEmptyOperations$()
+ return thiis
+ },
- // 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 ){
+ trim$: function( options ){
- return new Q.Qubit( this.matrix.multiply( qubit ))
- }
- }
- else {
+ `
+ 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.
+ `
- this.applyToQubit = function( qubit ){ return qubit }
- }
- }
-}
+ let {
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
+ momentFirstIndex,
+ momentRange,
+ momentLastIndex
+ } = this.determineRanges( options )
-Object.assign( Q.Gate, {
+ // First, trim the moments down to desired size.
- index: 0,
- constants: {},
- createConstant: Q.createConstant,
- createConstants: Q.createConstants,
- findBy: function( key, value ){
+ this.moments = this.moments.slice( momentFirstIndex, momentLastIndex )
+ this.timewidth = momentRange
- return (
-
- Object
- .values( Q.Gate.constants )
- .find( function( item ){
- if( typeof value === 'string' &&
- typeof item[ key ] === 'string' ){
+ // Then, trim the bandwidth down.
- return value.toLowerCase() === item[ key ].toLowerCase()
- }
- return value === item[ key ]
- })
- )
- },
- findBySymbol: function( symbol ){
+ this.inputs = this.inputs.slice( qubitFirstIndex, qubitLastIndex )
+ this.bandwidth = qubitRange
- return Q.Gate.findBy( 'symbol', symbol )
- },
- findByName: function( name ){
- return Q.Gate.findBy( 'name', name )
+ // 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
}
})
-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 ){
+// 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 ){
- this[ key ] = value
- return this
- },
- setSymbol$: function( value ){
+ const
+ gateConstantName = entry[ 0 ],
+ gate = entry[ 1 ],
+ set$ = function( momentIndex, registerIndexOrIndices, parameters ){
- return this.set$( 'symbol', value )
+ this.set$( gate, momentIndex, registerIndexOrIndices, parameters )
+ return this
}
+ Circuit.prototype[ gate.name ] = set$,
+ Circuit.prototype[ gate.name.toLowerCase() ] = set$,
+ Circuit.prototype[ gate.nameCss ] = set$,
+ Circuit.prototype[ gate.nameCss.toLowerCase() ] = set$,
+ Circuit.prototype[ gate.symbol ] = set$
+ Circuit.prototype[ gate.symbol.toLowerCase() ] = set$
})
-
-Q.Gate.createConstants(
+/*
+const bells = [
- // Operate on a single qubit.
+ // Verbose without shortcuts.
- 'IDENTITY', new Q.Gate({
+ new Circuit( 2, 2 )
+ .set$( Q.Gate.HADAMARD, 1, [ 1 ])
+ .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]),
+
+ new Circuit( 2, 2 )
+ .set$( Q.Gate.HADAMARD, 1, 1 )
+ .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]),
+
+
+ // Uses Q.Gate.findBySymbol() to lookup gates.
+
+ new Circuit( 2, 2 )
+ .set$( 'H', 1, [ 1 ])
+ .set$( 'X', 2, [ 1 , 2 ]),
+
+ new Circuit( 2, 2 )
+ .set$( 'H', 1, 1 )
+ .set$( 'X', 2, [ 1 , 2 ]),
+
+
+ // Convenience gate functions -- constant name.
+
+ new Circuit( 2, 2 )
+ .HADAMARD( 1, [ 1 ])
+ .PAULI_X( 2, [ 1, 2 ]),
+
+ new Circuit( 2, 2 )
+ .HADAMARD( 1, 1 )
+ .PAULI_X( 2, [ 1, 2 ]),
+
+
+ // Convenience gate functions -- uppercase symbol.
+
+ new Circuit( 2, 2 )
+ .H( 1, [ 1 ])
+ .X( 2, [ 1, 2 ]),
+
+ new Circuit( 2, 2 )
+ .H( 1, 1 )
+ .X( 2, [ 1, 2 ]),
+
+
+ // Convenience gate functions -- lowercase symbol.
+
+ new Circuit( 2, 2 )
+ .h( 1, [ 1 ])
+ .x( 2, [ 1, 2 ]),
+
+ new Circuit( 2, 2 )// Perhaps the closest to Braket style.
+ .h( 1, 1 )
+ .x( 2, [ 1, 2 ]),
+
+
+ // Q function -- bandwidth / timewidth arguments.
+
+ Q( 2, 2 )
+ .h( 1, [ 1 ])
+ .x( 2, [ 1, 2 ]),
+
+ Q( 2, 2 )
+ .h( 1, 1 )
+ .x( 2, [ 1, 2 ]),
+
+
+ // Q function -- text block argument
+ // with operation symbols
+ // and operation component IDs.
+
+ Q`
+ H-X.0#0
+ I-X.0#1`,
+
+
+ // Q function -- text block argument
+ // using only component IDs
+ // (ie. no operation symbols)
+ // because the operation that the
+ // components should belong to is NOT ambiguous.
+
+ Q`
+ H-X#0
+ I-X#1`,
+
+
+ // Q function -- text block argument
+ // as above, but using only whitespace
+ // to partition between moments.
+
+ Q`
+ H X#0
+ I X#1`
+],
+bellsAreEqual = !!bells.reduce( function( a, b ){
+
+ return a.toText() === b.toText() ? a : NaN
+
+})
+if( bellsAreEqual ){
+
+ console.log( `\n\nYES. All of ${ bells.length } our “Bell” circuits are equal.\n\n`, bells )
+}
+*/
+
+
+
+
+
+
+
+Circuit.createConstants(
+
+ 'BELL', new Circuit.fromText(`
+
+ H X#0
+ I X#1
+ `),
+ // 'GROVER', Q`
+
+ // H X *#0 X#0 I X#0 I I I X#0 I I I X#0 I X H X I *#0
+ // H X I X#1 *#0 X#1 *#0 X#0 I I I X#0 X I H X I I I I
+ // H X I I I I I X#1 *#0 X#1 *#0 X#1 *#0 X#1 I *#0 X H X I
+ // H X *#1 I *#1 I *#1 I *#1 I *#1 I *#1 I I *#1 X H X *#1
+ // `
+
+ //https://docs.microsoft.com/en-us/quantum/concepts/circuits?view=qsharp-preview
+ // 'TELEPORT', Q.(`
+
+ // I-I--H-M---v
+ // H-C0-I-M-v-v
+ // I-C1-I-I-X-Z-
+ // `)
+)
+
+
+module.exports = {Circuit};
+
+
+},{"./Logging":3,"./Math-Functions":4,"./Misc":5,"./Q-ComplexNumber":7,"./Q-Gate":8,"./Q-History":9,"./Q-Matrix":10,"./Q-Qubit":11}],7:[function(require,module,exports){
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+const { warn, error, help } = require('./Logging');
+const mathf = require('./Math-Functions');
+const misc = require('./Misc');
+const EPSILON = misc.constants.EPSILON;
+ComplexNumber = function (real, imaginary) {
+ `
+ The set of “real numbers” (ℝ) contains any number that can be expressed
+ along an infinite timeline. https://en.wikipedia.org/wiki/Real_number
+
+ … -3 -2 -1 0 +1 +2 +3 …
+ ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄
+ √2 𝒆 π
+
+
+ Meanwhile, “imaginary numbers” (𝕀) consist of a real (ℝ) multiplier and
+ the symbol 𝒊, which is the impossible solution to the equation 𝒙² = −1.
+ Note that no number when multiplied by itself can ever result in a
+ negative product, but the concept of 𝒊 gives us a way to reason around
+ this imaginary scenario nonetheless.
+ https://en.wikipedia.org/wiki/Imaginary_number
+
+ … -3𝒊 -2𝒊 -1𝒊 0𝒊 +1𝒊 +2𝒊 +3𝒊 …
+ ┄───┴───┴───┴───┴───┴───┴───┴───┄
+
+
+ A “complex number“ (ℂ) is a number that can be expressed in the form
+ 𝒂 + 𝒃𝒊, where 𝒂 is the real component (ℝ) and 𝒃𝒊 is the imaginary
+ component (𝕀). https://en.wikipedia.org/wiki/Complex_number
+
+
+ Operation functions on ComplexNumber instances generally accept as
+ arguments both sibling instances and pure Number instances, though the
+ value returned is always an instance of ComplexNumber.
+
+ `;
+
+ if (real instanceof ComplexNumber) {
+ imaginary = real.imaginary;
+ real = real.real;
+ warn(
+ "ComplexNumber tried to create a new instance with an argument that is already a ComplexNumber — and that’s weird!"
+ );
+ } else if (real === undefined) real = 0;
+ if (imaginary === undefined) imaginary = 0;
+ if (
+ (ComplexNumber.isNumberLike(real) !== true && isNaN(real) !== true) ||
+ (ComplexNumber.isNumberLike(imaginary) !== true &&
+ isNaN(imaginary) !== true)
+ )
+ return error(
+ "ComplexNumber attempted to create a new instance but the arguments provided were not actual numbers."
+ );
+
+ this.real = real;
+ this.imaginary = imaginary;
+ this.index = ComplexNumber.index++;
+};
+
+Object.assign(ComplexNumber, {
+ index: 0,
+ help: function () {
+ return help(this);
+ },
+ constants: {},
+ createConstant: function (key, value) {
+ //Object.freeze( value )
+ this[key] = value;
+ // Object.defineProperty( this, key, {
+
+ // value,
+ // writable: false
+ // })
+ // Object.defineProperty( this.constants, key, {
+
+ // value,
+ // writable: false
+ // })
+ this.constants[key] = this[key];
+ Object.freeze(this[key]);
+ },
+ createConstants: function () {
+ if (arguments.length % 2 !== 0) {
+ return error(
+ "Q attempted to create constants with invalid (KEY, VALUE) pairs."
+ );
+ }
+ for (let i = 0; i < arguments.length; i += 2) {
+ this.createConstant(arguments[i], arguments[i + 1]);
+ }
+ },
+
+ toText: function (rNumber, iNumber, roundToDecimal, padPositive) {
+ // Should we round these numbers?
+ // Our default is yes: to 3 digits.
+ // Otherwise round to specified decimal.
+
+ if (typeof roundToDecimal !== "number") roundToDecimal = 3;
+ const factor = Math.pow(10, roundToDecimal);
+ rNumber = Math.round(rNumber * factor) / factor;
+ iNumber = Math.round(iNumber * factor) / factor;
+
+ // Convert padPositive
+ // from a potential Boolean
+ // to a String.
+ // If we don’t receive a FALSE
+ // then we’ll pad the positive numbers.
+
+ padPositive = padPositive === false ? "" : " ";
+
+ // We need the absolute values of each.
+
+ let rAbsolute = Math.abs(rNumber),
+ iAbsolute = Math.abs(iNumber);
+
+ // And an absolute value string.
+
+ let rText = rAbsolute.toString(),
+ iText = iAbsolute.toString();
+
+ // Is this an IMAGINARY-ONLY number?
+ // Don’t worry: -0 === 0.
+
+ if (rNumber === 0) {
+ if (iNumber === Infinity) return padPositive + "∞i";
+ if (iNumber === -Infinity) return "-∞i";
+ if (iNumber === 0) return padPositive + "0";
+ if (iNumber === -1) return "-i";
+ if (iNumber === 1) return padPositive + "i";
+ if (iNumber >= 0) return padPositive + iText + "i";
+ if (iNumber < 0) return "-" + iText + "i";
+ return iText + "i"; // NaN
+ }
+
+ // This number contains a real component
+ // and may also contain an imaginary one as well.
+
+ if (rNumber === Infinity) rText = padPositive + "∞";
+ else if (rNumber === -Infinity) rText = "-∞";
+ else if (rNumber >= 0) rText = padPositive + rText;
+ else if (rNumber < 0) rText = "-" + rText;
+
+ if (iNumber === Infinity) return rText + " + ∞i";
+ if (iNumber === -Infinity) return rText + " - ∞i";
+ if (iNumber === 0) return rText;
+ if (iNumber === -1) return rText + " - i";
+ if (iNumber === 1) return rText + " + i";
+ if (iNumber > 0) return rText + " + " + iText + "i";
+ if (iNumber < 0) return rText + " - " + iText + "i";
+ return rText + " + " + iText + "i"; // NaN
+ },
+
+ isNumberLike: function (n) {
+ return isNaN(n) === false && (typeof n === "number" || n instanceof Number);
+ },
+ isNaN: function (n) {
+ return isNaN(n.real) || isNaN(n.imaginary);
+ },
+ isZero: function (n) {
+ return (
+ (n.real === 0 || n.real === -0) &&
+ (n.imaginary === 0 || n.imaginary === -0)
+ );
+ },
+ isFinite: function (n) {
+ return isFinite(n.real) && isFinite(n.imaginary);
+ },
+ isInfinite: function (n) {
+ return !(this.isNaN(n) || this.isFinite(n));
+ },
+ areEqual: function (a, b) {
+ return ComplexNumber.operate(
+ "areEqual",
+ a,
+ b,
+ function (a, b) {
+ return Math.abs(a - b) < EPSILON;
+ },
+ function (a, b) {
+ return (
+ Math.abs(a - b.real) < EPSILON && Math.abs(b.imaginary) < EPSILON
+ );
+ },
+ function (a, b) {
+ return (
+ Math.abs(a.real - b) < EPSILON && Math.abs(a.imaginary) < EPSILON
+ );
+ },
+ function (a, b) {
+ return (
+ Math.abs(a.real - b.real) < EPSILON &&
+ Math.abs(a.imaginary - b.imaginary) < EPSILON
+ );
+ }
+ );
+ },
+
+ absolute: function (n) {
+ return mathf.hypotenuse(n.real, n.imaginary);
+ },
+ conjugate: function (n) {
+ return new ComplexNumber(n.real, n.imaginary * -1);
+ },
+ operate: function (
+ name,
+ a,
+ b,
+ numberAndNumber,
+ numberAndComplex,
+ complexAndNumber,
+ complexAndComplex
+ ) {
+ if (ComplexNumber.isNumberLike(a)) {
+ if (ComplexNumber.isNumberLike(b)) return numberAndNumber(a, b);
+ else if (b instanceof ComplexNumber) return numberAndComplex(a, b);
+ else
+ return error(
+ "ComplexNumber attempted to",
+ name,
+ "with the number",
+ a,
+ "and something that is neither a Number or ComplexNumber:",
+ b
+ );
+ } else if (a instanceof ComplexNumber) {
+ if (ComplexNumber.isNumberLike(b)) return complexAndNumber(a, b);
+ else if (b instanceof ComplexNumber) return complexAndComplex(a, b);
+ else
+ return error(
+ "ComplexNumber attempted to",
+ name,
+ "with the complex number",
+ a,
+ "and something that is neither a Number or ComplexNumber:",
+ b
+ );
+ } else
+ return error(
+ "ComplexNumber attempted to",
+ name,
+ "with something that is neither a Number or ComplexNumber:",
+ a
+ );
+ },
+
+ sine: function (n) {
+ const a = n.real,
+ b = n.imaginary;
+
+ return new ComplexNumber(
+ Math.sin(a) * mathf.hyperbolicCosine(b),
+ Math.cos(a) * mathf.hyperbolicSine(b)
+ );
+ },
+ cosine: function (n) {
+ const a = n.real,
+ b = n.imaginary;
+
+ return new ComplexNumber(
+ Math.cos(a) * mathf.hyperbolicCosine(b),
+ -Math.sin(a) * mathf.hyperbolicSine(b)
+ );
+ },
+ arcCosine: function (n) {
+ const a = n.real,
+ b = n.imaginary,
+ t1 = ComplexNumber.squareRoot(
+ new ComplexNumber(b * b - a * a + 1, a * b * -2)
+ ),
+ t2 = ComplexNumber.log(new ComplexNumber(t1.real - b, t1.imaginary + a));
+ return new ComplexNumber(Math.PI / 2 - t2.imaginary, t2.real);
+ },
+ arcTangent: function (n) {
+ const a = n.real,
+ b = n.imaginary;
+
+ if (a === 0) {
+ if (b === 1) return new ComplexNumber(0, Infinity);
+ if (b === -1) return new ComplexNumber(0, -Infinity);
+ }
+
+ const d = a * a + (1 - b) * (1 - b),
+ t = ComplexNumber.log(
+ new ComplexNumber((1 - b * b - a * a) / d, (a / d) * -2)
+ );
+ return new ComplexNumber(t.imaginary / 2, t.real / 2);
+ },
+
+ power: function (a, b) {
+ if (ComplexNumber.isNumberLike(a)) a = new ComplexNumber(a);
+ if (ComplexNumber.isNumberLike(b)) b = new ComplexNumber(b);
+
+ // Anything raised to the Zero power is 1.
+
+ if (b.isZero()) return ComplexNumber.ONE;
+
+ // Zero raised to any power is 0.
+ // Note: What happens if b.real is zero or negative?
+ // What happens if b.imaginary is negative?
+ // Do we really need those conditionals??
+
+ if (a.isZero() && b.real > 0 && b.imaginary >= 0) {
+ return ComplexNumber.ZERO;
+ }
+
+ // If our exponent is Real (has no Imaginary component)
+ // then we’re really just raising to a power.
+
+ if (b.imaginary === 0) {
+ if (a.real >= 0 && a.imaginary === 0) {
+ return new ComplexNumber(Math.pow(a.real, b.real), 0);
+ } else if (a.real === 0) {
+ // If our base is Imaginary (has no Real component).
+
+ switch (((b.real % 4) + 4) % 4) {
+ case 0:
+ return new ComplexNumber(Math.pow(a.imaginary, b.real), 0);
+ case 1:
+ return new ComplexNumber(0, Math.pow(a.imaginary, b.real));
+ case 2:
+ return new ComplexNumber(-Math.pow(a.imaginary, b.real), 0);
+ case 3:
+ return new ComplexNumber(0, -Math.pow(a.imaginary, b.real));
+ }
+ }
+ }
+
+ const arctangent2 = Math.atan2(a.imaginary, a.real),
+ logHypotenuse = mathf.logHypotenuse(a.real, a.imaginary),
+ x = Math.exp(b.real * logHypotenuse - b.imaginary * arctangent2),
+ y = b.imaginary * logHypotenuse + b.real * arctangent2;
+
+ return new ComplexNumber(x * Math.cos(y), x * Math.sin(y));
+ },
+ squareRoot: function (a) {
+ const result = new ComplexNumber(0, 0),
+ absolute = ComplexNumber.absolute(a);
+
+ if (a.real >= 0) {
+ if (a.imaginary === 0) {
+ result.real = Math.sqrt(a.real); // and imaginary already equals 0.
+ } else {
+ result.real = Math.sqrt(2 * (absolute + a.real)) / 2;
+ }
+ } else {
+ result.real = Math.abs(a.imaginary) / Math.sqrt(2 * (absolute - a.real));
+ }
+ if (a.real <= 0) {
+ result.imaginary = Math.sqrt(2 * (absolute - a.real)) / 2;
+ } else {
+ result.imaginary =
+ Math.abs(a.imaginary) / Math.sqrt(2 * (absolute + a.real));
+ }
+ if (a.imaginary < 0) result.imaginary *= -1;
+ return result;
+ },
+ log: function (a) {
+ return new ComplexNumber(
+ mathf.logHypotenuse(a.real, a.imaginary),
+ Math.atan2(a.imaginary, a.real)
+ );
+ },
+ multiply: function (a, b) {
+ return ComplexNumber.operate(
+ "multiply",
+ a,
+ b,
+ function (a, b) {
+ return new ComplexNumber(a * b);
+ },
+ function (a, b) {
+ return new ComplexNumber(a * b.real, a * b.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real * b, a.imaginary * b);
+ },
+ function (a, b) {
+ // FOIL Method that shit.
+ // https://en.wikipedia.org/wiki/FOIL_method
+
+ const firsts = a.real * b.real,
+ outers = a.real * b.imaginary,
+ inners = a.imaginary * b.real,
+ lasts = a.imaginary * b.imaginary * -1; // Because i² = -1.
+
+ return new ComplexNumber(firsts + lasts, outers + inners);
+ }
+ );
+ },
+ divide: function (a, b) {
+ return ComplexNumber.operate(
+ "divide",
+ a,
+ b,
+ function (a, b) {
+ return new ComplexNumber(a / b);
+ },
+ function (a, b) {
+ return new ComplexNumber(a).divide(b);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real / b, a.imaginary / b);
+ },
+ function (a, b) {
+ // Ermergerd I had to look this up because it’s been so long.
+ // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/complex-conjugates-and-dividing-complex-numbers/a/dividing-complex-numbers-review
+
+ const conjugate = b.conjugate(),
+ numerator = a.multiply(conjugate),
+ // The .imaginary will be ZERO for sure,
+ // so this forces a ComplexNumber.divide( Number ) ;)
+
+ denominator = b.multiply(conjugate).real;
+
+ return numerator.divide(denominator);
+ }
+ );
+ },
+ add: function (a, b) {
+ return ComplexNumber.operate(
+ "add",
+ a,
+ b,
+ function (a, b) {
+ return new ComplexNumber(a + b);
+ },
+ function (a, b) {
+ return new ComplexNumber(b.real + a, b.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real + b, a.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real + b.real, a.imaginary + b.imaginary);
+ }
+ );
+ },
+ subtract: function (a, b) {
+ return ComplexNumber.operate(
+ "subtract",
+ a,
+ b,
+ function (a, b) {
+ return new ComplexNumber(a - b);
+ },
+ function (a, b) {
+ return new ComplexNumber(b.real - a, b.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real - b, a.imaginary);
+ },
+ function (a, b) {
+ return new ComplexNumber(a.real - b.real, a.imaginary - b.imaginary);
+ }
+ );
+ },
+});
+
+ComplexNumber.createConstants(
+ "ZERO",
+ new ComplexNumber(0, 0),
+ "ONE",
+ new ComplexNumber(1, 0),
+ "E",
+ new ComplexNumber(Math.E, 0),
+ "PI",
+ new ComplexNumber(Math.PI, 0),
+ "I",
+ new ComplexNumber(0, 1),
+ "EPSILON",
+ new ComplexNumber(EPSILON, EPSILON),
+ "INFINITY",
+ new ComplexNumber(Infinity, Infinity),
+ "NAN",
+ new ComplexNumber(NaN, NaN)
+);
+
+Object.assign(ComplexNumber.prototype, {
+ // NON-destructive operations.
+
+ clone: function () {
+ return new ComplexNumber(this.real, this.imaginary);
+ },
+ reduce: function () {
+ // Note: this *might* kill function chaining.
+
+ if (this.imaginary === 0) return this.real;
+ return this;
+ },
+ toText: function (roundToDecimal, padPositive) {
+ // Note: this will kill function chaining.
+
+ return ComplexNumber.toText(
+ this.real,
+ this.imaginary,
+ roundToDecimal,
+ padPositive
+ );
+ },
+
+ isNaN: function (n) {
+ return ComplexNumber.isNaN(this); // Returned boolean will kill function chaining.
+ },
+ isZero: function (n) {
+ return ComplexNumber.isZero(this); // Returned boolean will kill function chaining.
+ },
+ isFinite: function (n) {
+ return ComplexNumber.isFinite(this); // Returned boolean will kill function chaining.
+ },
+ isInfinite: function (n) {
+ return ComplexNumber.isInfinite(this); // Returned boolean will kill function chaining.
+ },
+ isEqualTo: function (b) {
+ return ComplexNumber.areEqual(this, b); // Returned boolean will kill function chaining.
+ },
+
+ absolute: function () {
+ return ComplexNumber.absolute(this); // Returned number will kill function chaining.
+ },
+ conjugate: function () {
+ return ComplexNumber.conjugate(this);
+ },
+
+ power: function (b) {
+ return ComplexNumber.power(this, b);
+ },
+ squareRoot: function () {
+ return ComplexNumber.squareRoot(this);
+ },
+ log: function () {
+ return ComplexNumber.log(this);
+ },
+ multiply: function (b) {
+ return ComplexNumber.multiply(this, b);
+ },
+ divide: function (b) {
+ return ComplexNumber.divide(this, b);
+ },
+ add: function (b) {
+ return ComplexNumber.add(this, b);
+ },
+ subtract: function (b) {
+ return ComplexNumber.subtract(this, b);
+ },
+
+ // DESTRUCTIVE operations.
+
+ copy$: function (b) {
+ if (b instanceof ComplexNumber !== true)
+ return error(
+ `ComplexNumber attempted to copy something that was not a complex number in to this complex number #${this.index}.`,
+ this
+ );
+
+ this.real = b.real;
+ this.imaginary = b.imaginary;
+ return this;
+ },
+ conjugate$: function () {
+ return this.copy$(this.conjugate());
+ },
+ power$: function (b) {
+ return this.copy$(this.power(b));
+ },
+ squareRoot$: function () {
+ return this.copy$(this.squareRoot());
+ },
+ log$: function () {
+ return this.copy$(this.log());
+ },
+ multiply$: function (b) {
+ return this.copy$(this.multiply(b));
+ },
+ divide$: function (b) {
+ return this.copy$(this.divide(b));
+ },
+ add$: function (b) {
+ return this.copy$(this.add(b));
+ },
+ subtract$: function (b) {
+ return this.copy$(this.subtract(b));
+ },
+});
+
+module.exports = { ComplexNumber };
+
+},{"./Logging":3,"./Math-Functions":4,"./Misc":5}],8:[function(require,module,exports){
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+const mathf = require('./Math-Functions');
+const logger = require('./Logging');
+const { ComplexNumber } = require('./Q-ComplexNumber');
+const {Matrix} = require('./Q-Matrix');
+Gate = function( params ){
+
+ Object.assign( this, params )
+ this.index = Gate.index ++
+
+ if( typeof this.symbol !== 'string' ) this.symbol = '?'
+ 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 )
+ },
+ findByNameCss: function( nameCss ) {
+ return Gate.findBy( 'nameCss', nameCss )
+ }
+})
+
+
+
+
+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: 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,
- applyToQubit: function( state ){}
+ matrix: Matrix.IDENTITY_2X2,
}),
- '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
@@ -2572,33 +3632,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'
@@ -2607,47 +3667,39 @@ 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( mathf.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(
- [ 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({
+ '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.
symbolSvg: '',
name: 'Bloch sphere',
nameCss: 'bloch',
- applyToQubit: function( qubit ){
+ // applyToQubit: function( qubit ){
- // Create Bloch sphere visualizer instance.
- }
+ // // Create Bloch sphere visualizer instance.
+ // }
}),
- 'RX', new Q.Gate({
+ 'RX', new Gate({
symbol: 'Rx',
symbolAmazonBraket: 'rx',
@@ -2657,23 +3709,15 @@ 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( 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( 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({
+ 'RY', new Gate({
symbol: 'Ry',
symbolAmazonBraket: 'ry',
@@ -2683,23 +3727,15 @@ 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( 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( 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({
+ 'RZ', new Gate({
symbol: 'Rz',
symbolAmazonBraket: 'rz',
@@ -2709,23 +3745,15 @@ 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( 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( 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({
+ 'UNITARY', new Gate({
symbol: 'U',
symbolAmazonBraket: 'unitary',
@@ -2737,103 +3765,86 @@ Q.Gate.createConstants(
"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) ) {
+
+ 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 = 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(
- [ a, b ],
- [ c, d ])
- return new Q.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',
@@ -2843,58 +3854,47 @@ 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( mathf.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(
- [ 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({
+ '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',
@@ -2904,28 +3904,18 @@ 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( 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
},
- 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({
+ 'ISING-XY', new Gate({
symbol: 'XY',
symbolAmazonBraket: 'xy',
@@ -2935,28 +3925,18 @@ Q.Gate.createConstants(
parameters: { "phi" : 1 },
updateMatrix$: function( phi ) {
- if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
- this.matrix = new Q.Matrix(
+ 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 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(
- [ 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({
+ 'ISING-YY', new Gate({
symbol: 'YY',
symbolAmazonBraket: 'yy',
@@ -2966,28 +3946,18 @@ 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( 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
},
- 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({
+ 'ISING-ZZ', new Gate({
symbol: 'ZZ',
symbolAmazonBraket: 'zz',
@@ -2997,28 +3967,18 @@ 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( 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
},
- 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({
+ 'CPhase00', new Gate({
symbol: '00', //placeholder
symbolAmazonBraket: 'cphaseshift00',
@@ -3028,28 +3988,18 @@ 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( 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
},
- 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({
+ 'CPhase01', new Gate({
symbol: '01', //placeholder
symbolAmazonBraket: 'cphaseshift01',
@@ -3059,28 +4009,18 @@ Q.Gate.createConstants(
parameters: { "phi" : 1 },
updateMatrix$: function( phi ) {
- if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
- this.matrix = new Q.Matrix(
+ if( mathf.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(
- [ 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({
+ 'CPhase10', new Gate({
symbol: '10', //placeholder
symbolAmazonBraket: 'cphaseshift10',
@@ -3090,35 +4030,25 @@ Q.Gate.createConstants(
parameters: { "phi" : 1 },
updateMatrix$: function( phi ) {
- if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
- this.matrix = new Q.Matrix(
+ if( mathf.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(
- [ 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({
+ '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],
@@ -3145,15 +4075,15 @@ Q.Gate.createConstants(
+module.exports = { Gate };
+},{"./Logging":3,"./Math-Functions":4,"./Q-ComplexNumber":7,"./Q-Matrix":10}],9:[function(require,module,exports){
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
+const {dispatchCustomEventToGlobal} = require('./Misc');
-Q.History = function( instance ){
+History = function( instance ){
this.instance = instance
this.entries = [[{
@@ -3168,38 +4098,34 @@ Q.History = function( instance ){
-Object.assign( Q.History.prototype, {
+Object.assign( History.prototype, {
assess: function(){
const instance = this.instance
if( this.index > 0 ){
- window.dispatchEvent( new CustomEvent(
-
- 'Q.History undo is capable', { detail: { instance }}
- ))
+ dispatchCustomEventToGlobal(
+ 'History undo is capable', { detail: { instance }}
+ );
}
else {
- window.dispatchEvent( new CustomEvent(
-
- 'Q.History undo is depleted', { detail: { instance }}
- ))
+ dispatchCustomEventToGlobal(
+ 'History undo is depleted', { detail: { instance }}
+ )
}
if( this.index + 1 < this.entries.length ){
- window.dispatchEvent( new CustomEvent(
-
- 'Q.History redo is capable', { detail: { instance }}
- ))
+ dispatchCustomEventToGlobal(
+ 'History redo is capable', { detail: { instance }}
+ )
}
else {
- window.dispatchEvent( new CustomEvent(
-
- 'Q.History redo is depleted', { detail: { instance }}
- ))
+ dispatchCustomEventToGlobal(
+ 'History redo is depleted', { detail: { instance }}
+ )
}
return this
},
@@ -3214,7 +4140,7 @@ Object.assign( Q.History.prototype, {
// Are we recording this history?
// Usually, yes.
- // But if our history state is “playbackâ€
+ // But if our history state is “playback”
// then we will NOT record this.
if( this.isRecording ){
@@ -3291,2200 +4217,1670 @@ Object.assign( Q.History.prototype, {
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
+ // It’s now safe to turn recording back on.
- 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.` )
+ this.isRecording = true
- 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,
+ // 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.
- '\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'
- )
+ this.assess()
+ return true
+ },
+ undo$: function(){ return this.step$( -1 )},
+ redo$: function(){ return this.step$( 1 )},
+ report: function(){
- return {
+ const argsParse = function( output, entry, i ){
- qubitFirstIndex,
- qubitRange,
- qubitLastIndex,
- momentFirstIndex,
- momentRange,
- momentLastIndex
+ 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 ){
- copy: function( options, isACutOperation ){
+ 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 ){
- const original = this
- let {
+ output += '\n ⟲ Undo '+ i +' ── '+ entry.name +' '
+ if( entry.args ) output += entry.args.reduce( argsParse, '' )
+ return output
- registerFirstIndex,
- registerRange,
- registerLastIndex,
- momentFirstIndex,
- momentRange,
- momentLastIndex
+ }, '' )
- } = this.determineRanges( options )
+ return output
- const copy = new Q.Circuit( registerRange, momentRange )
+ }, '' )
+ return output
+
+ }, 'History entry cursor: '+ this.index )
+ }
+})
- original.operations
- .filter( function( operation ){
- return ( operation.registerIndices.every( function( registerIndex ){
- return (
+module.exports = { History };
+},{"./Misc":5}],10:[function(require,module,exports){
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
- operation.momentIndex >= momentFirstIndex &&
- operation.momentIndex < momentLastIndex &&
- operation.registerIndex >= registerFirstIndex &&
- operation.registerIndex < registerLastIndex
- )
- }))
- })
- .forEach( function( operation ){
+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);
+ }
+};
- const adjustedRegisterIndices = operation.registerIndices.map( function( registerIndex ){
+///////////////////////////
+// //
+// Static properties //
+// //
+///////////////////////////
- return registerIndex - registerFirstIndex
- })
- copy.set$(
+Object.assign(Matrix, {
+ index: 0,
+ help: function () {
+ return logger.help(this);
+ },
+ constants: {}, // Only holds references; an easy way to look up what constants exist.
+ createConstant: function (key, value) {
+ this[key] = value;
+ this.constants[key] = this[key];
+ Object.freeze(this[key]);
+ },
+ createConstants: function () {
+ if (arguments.length % 2 !== 0) {
+ return logger.error(
+ "Q attempted to create constants with invalid (KEY, VALUE) pairs."
+ );
+ }
+ for (let i = 0; i < arguments.length; i += 2) {
+ this.createConstant(arguments[i], arguments[i + 1]);
+ }
+ },
+
+ isMatrixLike: function (obj) {
+ //return obj instanceof Matrix || Matrix.prototype.isPrototypeOf( obj )
+ return obj instanceof this || this.prototype.isPrototypeOf(obj);
+ },
+ isWithinRange: function (n, minimum, maximum) {
+ return (
+ typeof n === "number" && n >= minimum && n <= maximum && n == parseInt(n)
+ );
+ },
+ getWidth: function (matrix) {
+ return matrix.columns.length;
+ },
+ getHeight: function (matrix) {
+ return matrix.rows.length;
+ },
+ haveEqualDimensions: function (matrix0, matrix1) {
+ return (
+ matrix0.rows.length === matrix1.rows.length &&
+ matrix0.columns.length === matrix1.columns.length
+ );
+ },
+ areEqual: function (matrix0, matrix1) {
+ if (matrix0 instanceof Matrix !== true) return false;
+ if (matrix1 instanceof Matrix !== true) return false;
+ if (Matrix.haveEqualDimensions(matrix0, matrix1) !== true) return false;
+ return matrix0.rows.reduce(function (state, row, r) {
+ return (
+ state &&
+ row.reduce(function (state, cellValue, c) {
+ return state && cellValue.isEqualTo(matrix1.rows[r][c]);
+ }, true)
+ );
+ }, true);
+ },
+
+ createSquare: function (size, f) {
+ if (typeof size !== "number") size = 2;
+ if (typeof f !== "function")
+ f = function () {
+ return 0;
+ };
+ const data = [];
+ for (let y = 0; y < size; y++) {
+ const row = [];
+ for (let x = 0; x < size; x++) {
+ row.push(f(x, y));
+ }
+ data.push(row);
+ }
+ return new Matrix(...data);
+ },
+ createZero: function (size) {
+ return new Matrix.createSquare(size);
+ },
+ createOne: function (size) {
+ return new Matrix.createSquare(size, function () {
+ return 1;
+ });
+ },
+ createIdentity: function (size) {
+ return new Matrix.createSquare(size, function (x, y) {
+ return x === y ? 1 : 0;
+ });
+ },
+
+ // Import FROM a format.
+
+ from: function (format) {
+ if (typeof format !== "string") format = "Array";
+ const f = Matrix["from" + format];
+ format = format.toLowerCase();
+ if (typeof f !== "function")
+ return logger.error(
+ `Matrix could not find an importer for “${format}” data.`
+ );
+ return f;
+ },
+ fromArray: function (array) {
+ return new Matrix(...array);
+ },
+ fromXsv: function (input, rowSeparator, valueSeparator) {
+ `
+ Ingest string data organized by row, then by column
+ where rows are separated by one token (default: \n)
+ and column values are separated by another token
+ (default: \t).
- operation.gate,
- 1 + m - momentFirstIndex,
- adjustedRegisterIndices
- )
- })
+ `;
+
+ 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.
- // 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.
+ SEE ALSO
- // UPDATE !!!!
- // will come back to fix!!
- // with new style it's now just a matter of
- // splicing out these out of circuit.operations
+ 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 //
+// //
+//////////////////////////////
-
- if( isACutOperation === true ){
+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" + cell.toText() + " ";
+ }, "\n\t") +
+ "\n\t "
+ );
+ }, "\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));
+ },
+});
- /*
- for( let m = momentFirstIndex; m < momentLastIndex; m ++ ){
+//////////////////////////
+// //
+// Static constants //
+// //
+//////////////////////////
- original.moments[ m ] = new Array( original.bandwidth )
- .fill( 0 )
- .map( function( qubit, q ){
+Matrix.createConstants(
+ "IDENTITY_2X2",
+ Matrix.createIdentity(2),
+ "IDENTITY_3X3",
+ Matrix.createIdentity(3),
+ "IDENTITY_4X4",
+ Matrix.createIdentity(4),
+
+ "CONSTANT0_2X2",
+ new Matrix([1, 1], [0, 0]),
+
+ "CONSTANT1_2X2",
+ new Matrix([0, 0], [1, 1]),
+
+ "NEGATION_2X2",
+ new Matrix([0, 1], [1, 0]),
+
+ "TEST_MAP_9X9",
+ new Matrix(
+ [11, 21, 31, 41, 51, 61, 71, 81, 91],
+ [12, 22, 32, 42, 52, 62, 72, 82, 92],
+ [13, 23, 33, 43, 53, 63, 73, 83, 93],
+ [14, 24, 34, 44, 54, 64, 74, 84, 94],
+ [15, 25, 35, 45, 55, 65, 75, 85, 95],
+ [16, 26, 36, 46, 56, 66, 76, 86, 96],
+ [17, 27, 37, 47, 57, 67, 77, 87, 97],
+ [18, 28, 38, 48, 58, 68, 78, 88, 98],
+ [19, 29, 39, 49, 59, 69, 79, 89, 99]
+ )
+);
+
+module.exports = { Matrix };
+},{"./Logging":3,"./Q-ComplexNumber":7}],11:[function(require,module,exports){
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+const { Matrix } = require("./Q-Matrix");
+const { Gate } = require("./Q-Gate");
+const { ComplexNumber } = require("./Q-ComplexNumber");
+const misc = require("./Misc");
+const logger = require("./Logging");
+
+Qubit = function (a, b, symbol, name) {
+ // If we’ve received an instance of Matrix as our first argument
+ // then we’ll assume there are no further arguments
+ // and just use that matrix as our new Qubit instance.
+
+ if (Matrix.isMatrixLike(a) && b === undefined) {
+ b = a.rows[1][0];
+ a = a.rows[0][0];
+ } else {
+ // All of our internal math now uses complex numbers
+ // rather than Number literals
+ // so we’d better convert!
+
+ if (typeof a === "number") a = new ComplexNumber(a, 0);
+ if (typeof b === "number") b = new ComplexNumber(b, 0);
+
+ // If we receive undefined (or garbage inputs)
+ // let’s try to make it useable.
+ // This way we can always call Qubit with no arguments
+ // to make a new qubit available for computing with.
+
+ if (a instanceof ComplexNumber !== true) a = new ComplexNumber(1, 0);
+ if (b instanceof ComplexNumber !== true) {
+ // 1 - |𝒂|² = |𝒃|²
+ // So this does NOT account for if 𝒃 ought to be imaginary or not.
+ // Perhaps for completeness we could randomly decide
+ // to flip the real and imaginary components of 𝒃 after this line?
+
+ b = ComplexNumber.ONE.subtract(Math.pow(a.absolute(), 2)).squareRoot();
+ }
+ }
+
+ // Sanity check!
+ // Does this constraint hold true? |𝒂|² + |𝒃|² = 1
+
+ if (
+ Math.pow(a.absolute(), 2) + Math.pow(b.absolute(), 2) - 1 >
+ misc.constants.EPSILON
+ )
+ return logger.error(
+ `Qubit could not accept the initialization values of a=${a} and b=${b} because their squares do not add up to 1.`
+ );
+
+ Matrix.call(this, [a], [b]);
+ this.index = Qubit.index++;
+
+ // Convenience getters and setters for this qubit’s
+ // controll bit and target bit.
+
+ Object.defineProperty(this, "alpha", {
+ get: function () {
+ return this.rows[0][0];
+ },
+ set: function (n) {
+ this.rows[0][0] = n;
+ },
+ });
+ Object.defineProperty(this, "beta", {
+ get: function () {
+ return this.rows[1][0];
+ },
+ set: function (n) {
+ this.rows[1][0] = n;
+ },
+ });
+
+ // Used for Dirac notation: |?⟩
+
+ if (typeof symbol === "string") this.symbol = symbol;
+ if (typeof name === "string") this.name = name;
+ if (this.symbol === undefined || this.name === undefined) {
+ const found = Object.values(Qubit.constants).find(function (qubit) {
+ return a.isEqualTo(qubit.alpha) && b.isEqualTo(qubit.beta);
+ });
+ if (found === undefined) {
+ this.symbol = "?";
+ this.name = "Unnamed";
+ } else {
+ if (this.symbol === undefined) this.symbol = found.symbol;
+ if (this.name === undefined) this.name = found.name;
+ }
+ }
+};
+//Qubit inherits from Matrix.
+Qubit.prototype = Object.create(Matrix.prototype);
+Qubit.prototype.constructor = Qubit;
+
+Object.assign(Qubit, {
+ index: 0,
+ help: function () {
+ return logger.help(this);
+ },
+ constants: {},
+ createConstant: function (key, value) {
+ this[key] = value;
+ this.constants[key] = this[key];
+ Object.freeze(this[key]);
+ },
+ createConstants: function () {
+ if (arguments.length % 2 !== 0) {
+ return logger.error(
+ "Q attempted to create constants with invalid (KEY, VALUE) pairs."
+ );
+ }
+ for (let i = 0; i < arguments.length; i += 2) {
+ this.createConstant(arguments[i], arguments[i + 1]);
+ }
+ },
+
+ findBy: function (key, value) {
+ return Object.values(Qubit.constants).find(function (item) {
+ if (typeof value === "string" && typeof item[key] === "string") {
+ return value.toLowerCase() === item[key].toLowerCase();
+ }
+ return value === item[key];
+ });
+ },
+ findBySymbol: function (symbol) {
+ return Qubit.findBy("symbol", symbol);
+ },
+ findByName: function (name) {
+ return Qubit.findBy("name", name);
+ },
+ findByBeta: function (beta) {
+ if (beta instanceof ComplexNumber === false) {
+ beta = new ComplexNumber(beta);
+ }
+ return Object.values(Qubit.constants).find(function (qubit) {
+ return qubit.beta.isEqualTo(beta);
+ });
+ },
+ areEqual: function (qubit0, qubit1) {
+ return (
+ qubit0.alpha.isEqualTo(qubit1.alpha) && qubit0.beta.isEqualTo(qubit1.beta)
+ );
+ },
+ collapse: function (qubit) {
+ const alpha2 = Math.pow(qubit.alpha.absolute(), 2),
+ beta2 = Math.pow(qubit.beta.absolute(), 2),
+ randomNumberRange = Math.pow(2, 32) - 1,
+ randomNumber = new Uint32Array(1);
+
+ // console.log( 'alpha^2', alpha2 )
+ // console.log( 'beta^2', beta2 )
+ window.crypto.getRandomValues(randomNumber);
+ const randomNumberNormalized = randomNumber / randomNumberRange;
+ if (randomNumberNormalized <= alpha2) {
+ return new Qubit(1, 0);
+ } else return new Qubit(0, 1);
+ },
+ applyGate: function (qubit, gate, ...args) {
+ //TODO test...currently you're updating the gate's matrix property rather than returning a separate instance of the matrix.
+ //this is okay if "gate" is not one of the constants. otherwise, it's bad.
+ `
+ This is means of inverting what comes first:
+ the Gate or the Qubit?
+ If the Gate only operates on a single qubit,
+ then it doesn’t matter and we can do this:
+ `;
+
+ if (gate instanceof Gate === false)
+ return logger.error(
+ `Qubit attempted to apply something that was not a gate to this qubit #${qubit.index}.`
+ );
+ if (gate == Gate.findBy(gate.symbol))
+ return logger.error(`Qubit attempted to apply a reference to the gate constant ${gate.symbol} rather than
+ a copy. This is disallowed.`);
+ else {
+ gate.updateMatrix$(...args);
+ return new Qubit(gate.matrix.multiply(qubit));
+ }
+ },
+ toText: function (qubit) {
+ //return `|${qubit.beta.toText()}⟩`
+ return qubit.alpha.toText() + "\n" + qubit.beta.toText();
+ },
+ toStateVectorText: function (qubit) {
+ return `|${qubit.beta.toText()}⟩`;
+ },
+ toStateVectorHtml: function (qubit) {
+ return `${qubit.beta.toText()} `;
+ },
+
+ // This code was a pain in the ass to figure out.
+ // I’m not fluent in trigonometry
+ // and none of the quantum primers actually lay out
+ // how to convert arbitrary qubit states
+ // to Bloch Sphere representation.
+ // Oh, they provide equivalencies for specific states, sure.
+ // I hope this is useful to you
+ // unless you are porting this to a terrible language
+ // like C# or Java or something ;)
+
+ toBlochSphere: function (qubit) {
+ `
+ Based on this qubit’s state return the
+ Polar angle θ (theta),
+ azimuth angle ϕ (phi),
+ Bloch vector,
+ corrected surface coordinate.
- return {
+ https://en.wikipedia.org/wiki/Bloch_sphere
+ `;
+
+ // Polar angle θ (theta).
+
+ const theta = ComplexNumber.arcCosine(qubit.alpha).multiply(2);
+ if (isNaN(theta.real)) theta.real = 0;
+ if (isNaN(theta.imaginary)) theta.imaginary = 0;
+
+ // Azimuth angle ϕ (phi).
+
+ const phi = ComplexNumber.log(
+ qubit.beta.divide(ComplexNumber.sine(theta.divide(2)))
+ ).divide(ComplexNumber.I);
+ if (isNaN(phi.real)) phi.real = 0;
+ if (isNaN(phi.imaginary)) phi.imaginary = 0;
+
+ // Bloch vector.
+
+ const vector = {
+ x: ComplexNumber.sine(theta).multiply(ComplexNumber.cosine(phi)).real,
+ y: ComplexNumber.sine(theta).multiply(ComplexNumber.sine(phi)).real,
+ z: ComplexNumber.cosine(theta).real,
+ };
+
+ // Bloch vector’s axes are wonked.
+ // Let’s “correct” them for use with Three.js, etc.
+
+ const position = {
+ x: vector.y,
+ y: vector.z,
+ z: vector.x,
+ };
+
+ return {
+ // Wow does this make tweening easier down the road.
+
+ alphaReal: qubit.alpha.real,
+ alphaImaginary: qubit.alpha.imaginary,
+ betaReal: qubit.beta.real,
+ betaImaginary: qubit.beta.imaginary,
+
+ // Ummm... I’m only returnig the REAL portions. Please forgive me!
+
+ theta: theta.real,
+ phi: phi.real,
+ vector, // Wonked YZX vector for maths because maths.
+ position, // Un-wonked XYZ for use by actual 3D engines.
+ };
+ },
+ fromBlochVector: function (x, y, z) {
+ //basically from a Pauli Rotation
+ },
+});
+
+Qubit.createConstants(
+ // Opposing pairs:
+ // |H⟩ and |V⟩
+ // |D⟩ and |A⟩
+ // |R⟩ and |L⟩
+
+ "HORIZONTAL",
+ new Qubit(1, 0, "H", "Horizontal"), // ZERO.
+ "VERTICAL",
+ new Qubit(0, 1, "V", "Vertical"), // ONE.
+ "DIAGONAL",
+ new Qubit(Math.SQRT1_2, Math.SQRT1_2, "D", "Diagonal"),
+ "ANTI_DIAGONAL",
+ new Qubit(Math.SQRT1_2, -Math.SQRT1_2, "A", "Anti-diagonal"),
+ "RIGHT_HAND_CIRCULAR_POLARIZED",
+ new Qubit(
+ Math.SQRT1_2,
+ new ComplexNumber(0, -Math.SQRT1_2),
+ "R",
+ "Right-hand Circular Polarized"
+ ), // RHCP
+ "LEFT_HAND_CIRCULAR_POLARIZED",
+ new Qubit(
+ Math.SQRT1_2,
+ new ComplexNumber(0, Math.SQRT1_2),
+ "L",
+ "Left-hand Circular Polarized"
+ ) // LHCP
+);
+
+Object.assign(Qubit.prototype, {
+ copy$: function (matrix) {
+ if (Matrix.isMatrixLike(matrix) !== true)
+ return logger.error(
+ `Qubit attempted to copy something that was not a matrix in this qubit #${qubit.index}.`,
+ this
+ );
+
+ if (Matrix.haveEqualDimensions(matrix, this) !== true)
+ return logger.error(
+ `Qubit cannot copy matrix#${matrix.index} of dimensions ${matrix.columns.length}x${matrix.rows.length} in to this qubit #${this.index} of dimensions ${this.columns.length}x${this.rows.length} because their dimensions do not match.`,
+ this
+ );
+
+ const that = this;
+ matrix.rows.forEach(function (row, r) {
+ row.forEach(function (n, c) {
+ that.rows[r][c] = n;
+ });
+ });
+ this.dirac = matrix.dirac;
+ return this;
+ },
+ clone: function () {
+ return new Qubit(this.alpha, this.beta);
+ },
+ isEqualTo: function (otherQubit) {
+ return Qubit.areEqual(this, otherQubit); // Returns a Boolean, breaks function chaining!
+ },
+ collapse: function () {
+ return Qubit.collapse(this);
+ },
+ applyGate: function (gate, ...args) {
+ return Qubit.applyGate(this, gate, ...args);
+ },
+ toText: function () {
+ return Qubit.toText(this); // Returns a String, breaks function chaining!
+ },
+ toStateVectorText: function () {
+ return Qubit.toStateVectorText(this); // Returns a String, breaks function chaining!
+ },
+ toStateVectorHtml: function () {
+ return Qubit.toStateVectorHtml(this); // Returns a String, breaks function chaining!
+ },
+ toBlochSphere: function () {
+ return Qubit.toBlochSphere(this); // Returns an Object, breaks function chaining!
+ },
+ collapse$: function () {
+ return this.copy$(Qubit.collapse(this));
+ },
+ applyGate$: function (gate) {
+ return this.copy$(Qubit.applyGate(this, gate));
+ },
+});
+
+module.exports = { Qubit };
+
+},{"./Logging":3,"./Misc":5,"./Q-ComplexNumber":7,"./Q-Gate":8,"./Q-Matrix":10}],12:[function(require,module,exports){
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
- gate: Q.Gate.IDENTITY,
- registerIndices: [ q ]
- }
- })
- }*/
- }
- return copy
- },
- cut$: function( options ){
+const misc = require('./Misc');
+const mathf = require('./Math-Functions');
+const {Circuit} = require('./Q-Circuit');
- return this.copy( options, true )
- },
+Q = function () {
+ // Did we send arguments of the form
+ // ( bandwidth, timewidth )?
+ if (
+ arguments.length === 2 &&
+ Array.from(arguments).every(function (argument) {
+ return mathf.isUsefulInteger(argument);
+ })
+ ) {
+ return new Circuit(arguments[0], arguments[1]);
+ }
+ // Otherwise assume we are creating a circuit
+ // from a text block.
+ return Circuit.fromText(arguments[0]);
+};
- /*
+console.log(`
+ QQQQQQ
+QQ QQ
+QQ QQ
+QQ QQ
+QQ QQ QQ
+QQ QQ
+ QQQQ ${misc.constants.REVISION}
- 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?)
+https://quantumjavascript.app
- */
+`);
- spliceCut$: function( options ){
+module.exports = {Q};
- let {
- qubitFirstIndex,
- qubitRange,
- qubitLastIndex,
- momentFirstIndex,
- momentRange,
- momentLastIndex
+},{"./Math-Functions":4,"./Misc":5,"./Q-Circuit":6}],13:[function(require,module,exports){
+const logger = require('./Logging');
+const misc = require('./Misc');
+const mathf = require('./Math-Functions');
+const {ComplexNumber} = require('./Q-ComplexNumber');
+const {Gate} = require('./Q-Gate');
+const {Qubit} = require('./Q-Qubit');
+const {Matrix} = require('./Q-Matrix');
+const {History} = require('./Q-History');
+const {Circuit} = require('./Q-Circuit');
+const {Q} = require('./Q.js');
- } = this.determineRanges( options )
+module.exports = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q};
+},{"./Logging":3,"./Math-Functions":4,"./Misc":5,"./Q-Circuit":6,"./Q-ComplexNumber":7,"./Q-Gate":8,"./Q-History":9,"./Q-Matrix":10,"./Q-Qubit":11,"./Q.js":12}],14:[function(require,module,exports){
- // 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).
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
- 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.` )
- }
+const {Qubit} = require('quantum-js-util');
+BlochSphere = function( onValueChange ){
- // If the selection area covers all qubits for 1 or more moments
- // then splice the moments array.
-
- if( qubitRange === this.bandwidth ){
+ Object.assign( this, {
+ isRotating: false,
+ radius: 1,
+ radiusSafe: 1.01,
+ axesLineWidth: 0.01,
+ arcLineWidth: 0.015,
+ state: Qubit.LEFT_HAND_CIRCULAR_POLARIZED.toBlochSphere(),
+ target: Qubit.HORIZONTAL.toBlochSphere(),
+ group: new THREE.Group(),
+ onValueChange
+ })
- // 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
+ // Create the surface of the Bloch sphere.
- //@@ And how do we implement splicePaste$() here?
- }
+ const surface = new THREE.Mesh(
+ new THREE.SphereGeometry( this.radius, 64, 64 ),
+ new THREE.MeshPhongMaterial({
- // 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 ){
+ side: THREE.FrontSide,
+ map: BlochSphere.makeSurface(),
+ transparent: true,
+ opacity: 0.97
+ })
+ )
+ surface.receiveShadow = true
+ this.group.add( surface )
- // 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.
+ // Create the X, Y, and Z axis lines.
- this.moments = this.moments.map( function( operations ){
+ const
+ xAxis = new THREE.Mesh(
-
- // Remove operations that pertain to the removed qubits.
- // Renumber the remaining operations’ qubitIndices.
-
- return operations.reduce( function( accumulator, operation ){
+ new THREE.BoxGeometry( this.axesLineWidth, this.axesLineWidth, this.radius * 2.5 ),
+ new THREE.MeshBasicMaterial({ color: BlochSphere.xAxisColor })
+ ),
+ yAxis = new THREE.Mesh(
- if( operation.qubitIndices.every( function( index ){
+ new THREE.BoxGeometry( this.radius * 2.5, this.axesLineWidth, this.axesLineWidth ),
+ new THREE.MeshBasicMaterial({ color: BlochSphere.yAxisColor })
+ ),
+ zAxis = new THREE.Mesh(
- return index < qubitFirstIndex || index >= qubitLastIndex
-
- })) accumulator.push( operation )
- return accumulator
-
- }, [])
- .map( function( operation ){
+ new THREE.BoxGeometry( this.axesLineWidth, this.radius * 2.5, this.axesLineWidth ),
+ new THREE.MeshBasicMaterial({ color: BlochSphere.zAxisColor })
+ )
- operation.qubitIndices = operation.qubitIndices.map( function( index ){
+ this.group.add( xAxis, yAxis, zAxis )
- return index >= qubitLastIndex ? index - qubitRange : index
- })
- return operation
- })
- })
- this.bandwidth -= qubitRange
- }
-
- // Final clean up.
+ // Create X, Y, and Z arrow heads,
+ // indicating positive directions for all three.
- this.removeHangingOperations$()
- this.fillEmptyOperations$()
-
+ const
+ arrowLength = 0.101,// I know, weird, right?
+ arrowHeadLength = 0.1,
+ arrowHeadWidth = 0.1
+
+ this.group.add( new THREE.ArrowHelper(
+
+ new THREE.Vector3( 0, 0, 1.00 ),
+ new THREE.Vector3( 0, 0, 1.25 ),
+ arrowLength,
+ BlochSphere.xAxisColor,// Red
+ arrowHeadLength,
+ arrowHeadWidth
+ ))
+ this.group.add( new THREE.ArrowHelper(
+
+ new THREE.Vector3( 1.00, 0, 0 ),
+ new THREE.Vector3( 1.25, 0, 0 ),
+ arrowLength,
+ BlochSphere.yAxisColor,// Green
+ arrowHeadLength,
+ arrowHeadWidth
+ ))
+ this.group.add( new THREE.ArrowHelper(
+
+ new THREE.Vector3( 0, 1.00, 0 ),
+ new THREE.Vector3( 0, 1.25, 0 ),
+ arrowLength,
+ BlochSphere.zAxisColor,// Blue
+ arrowHeadLength,
+ arrowHeadWidth
+ ))
- return this// Or should we return the cut area?!
- },
- splicePaste$: function(){
+ // Create the X, Y, and Z axis labels.
+
+ const
+ axesLabelStyle = {
+ width: 128,
+ height: 128,
+ fillStyle: BlochSphere.vectorColor,//'#505962',
+ font: 'bold italic 64px Georgia, "Times New Roman", serif'
},
-
+ xAxisLabel = new THREE.Sprite(
+ new THREE.SpriteMaterial({
+ map: Object.assign( SurfaceText( axesLabelStyle ))
+ })
+ ),
+ yAxisLabel = new THREE.Sprite(
+ new THREE.SpriteMaterial({
- // 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!
+ map: Object.assign( SurfaceText( axesLabelStyle ))
+ })
+ ),
+ zAxisLabel = new THREE.Sprite(
+
+ new THREE.SpriteMaterial({
+ map: Object.assign( SurfaceText( axesLabelStyle ))
+ })
+ )
- paste$: function( other, atMoment = 0, atQubit = 0, shouldClean = true ){
+ xAxisLabel.material.map.print( 'x' )
+ xAxisLabel.position.set( 0, 0, 1.45 )
+ xAxisLabel.scale.set( 0.25, 0.25, 0.25 )
+ xAxis.add( xAxisLabel )
- 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 ){
+ yAxisLabel.material.map.print( 'y' )
+ yAxisLabel.position.set( 1.45, 0, 0 )
+ yAxisLabel.scale.set( 0.25, 0.25, 0.25 )
+ yAxis.add( yAxisLabel )
- moment.forEach( function( operation ){
+ zAxisLabel.material.map.print( 'z' )
+ zAxisLabel.position.set( 0, 1.45, 0 )
+ zAxisLabel.scale.set( 0.25, 0.25, 0.25 )
+ zAxis.add( zAxisLabel )
- //console.log( 'past over w this:', m + atMoment, operation )
- scope.set$(
+ this.blochColor = new THREE.Color()
- 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 ){
+ // Create the line from the sphere’s origin
+ // out to where the Bloch vector intersects
+ // with the sphere’s surface.
- // 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.' )
+ this.blochVector = new THREE.Mesh(
-
+ new THREE.BoxGeometry( 0.04, 0.04, this.radius ),
+ new THREE.MeshBasicMaterial({ color: BlochSphere.vectorColor })
+ )
+ this.blochVector.geometry.translate( 0, 0, 0.5 )
+ this.group.add( this.blochVector )
- if( shouldClean ) this.removeHangingOperations$()
- this.fillEmptyOperations$()
- return this
+ // Create the cone that indicates the Bloch vector
+ // and points to where that vectors
+ // intersects with the surface of the sphere.
- },
- expand$: function(){
+ this.blochPointer = new THREE.Mesh(
- // expand either bandwidth or timewidth, fill w identity
+ new THREE.CylinderBufferGeometry( 0, 0.5, 1, 32, 1 ),
+ new THREE.MeshPhongMaterial({ color: BlochSphere.vectorColor })
+ )
+ this.blochPointer.geometry.translate( 0, -0.5, 0 )
+ this.blochPointer.geometry.rotateX( Math.PI / 2 )
+ this.blochPointer.geometry.scale( 0.2, 0.2, 0.2 )
+ this.blochPointer.lookAt( new THREE.Vector3() )
+ this.blochPointer.receiveShadow = true
+ this.blochPointer.castShadow = true
+ this.group.add( this.blochPointer )
- this.fillEmptyOperations$()
- return thiis
- },
+ // Create the Theta ring that will belt the sphere.
+ const
+ arcR = this.radiusSafe * Math.sin( Math.PI / 2 ),
+ arcH = this.radiusSafe * Math.cos( Math.PI / 2 ),
+ thetaGeometry = BlochSphere.createLatitudeArc( arcR, 128, Math.PI / 2, Math.PI * 2 ),
+ thetaLine = new MeshLine(),
+ thetaPhiMaterial = new MeshLineMaterial({
+
+ color: BlochSphere.thetaPhiColor,//0x505962,
+ lineWidth: this.arcLineWidth * 3,
+ sizeAttenuation: true
+ })
+ thetaGeometry.rotateX( Math.PI / 2 )
+ thetaGeometry.rotateY( Math.PI / 2 )
+ thetaGeometry.translate( 0, arcH, 0 )
+ thetaLine.setGeometry( thetaGeometry )
+ this.thetaMesh = new THREE.Mesh(
+ thetaLine.geometry,
+ thetaPhiMaterial
+ )
+ this.group.add( this.thetaMesh )
+ // Create the Phi arc that will draw from the north pole
+ // down to wherever the Theta arc rests.
- trim$: function( options ){
+ this.phiGeometry = BlochSphere.createLongitudeArc( this.radiusSafe, 64, 0, Math.PI * 2 ),
+ this.phiLine = new MeshLine()
+ this.phiLine.setGeometry( this.phiGeometry )
+ this.phiMesh = new THREE.Mesh(
- `
- 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.
- `
+ this.phiLine.geometry,
+ thetaPhiMaterial
+ )
+ this.group.add( this.phiMesh )
- let {
- qubitFirstIndex,
- qubitRange,
- qubitLastIndex,
- momentFirstIndex,
- momentRange,
- momentLastIndex
- } = this.determineRanges( options )
+ // Time to put plans to action.
- // First, trim the moments down to desired size.
+ BlochSphere.prototype.setTargetState.call( this )
+}
- 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$()
+ ////////////////
+ // //
+ // Static //
+ // //
+////////////////
- return this
- }
-})
+Object.assign( BlochSphere, {
+ xAxisColor: 0x333333,// Was 0xCF1717 (red)
+ yAxisColor: 0x333333,// Was 0x59A112 (green)
+ zAxisColor: 0x333333,// Was 0x0F66BD (blue)
+ vectorColor: 0xFFFFFF,// Was 0xF2B90D (yellow)
+ thetaPhiColor: 0x333333,// Was 0xF2B90D (yellow)
+ // It’s important that we build the texture
+ // right here and now, rather than load an image.
+ // Why? Because if we load a pre-existing image
+ // we run into CORS problems using file:/// !
+ makeSurface: function(){
+ const
+ width = 2048,
+ height = width / 2
-// 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.
+ 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 )
-Object.entries( Q.Gate.constants ).forEach( function( entry ){
- const
- gateConstantName = entry[ 0 ],
- gate = entry[ 1 ],
- set$ = function( momentIndex, registerIndexOrIndices ){
+ // Create the base hue gradient for our texture.
- 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
+ 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 )
-/*
-const bells = [
+ // 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.
- // Verbose without shortcuts.
+ 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 )
- new Q.Circuit( 2, 2 )
- .set$( Q.Gate.HADAMARD, 1, [ 1 ])
- .set$( Q.Gate.PAULI_X, 2, [ 1 , 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 )
- new Q.Circuit( 2, 2 )
- .set$( Q.Gate.HADAMARD, 1, 1 )
- .set$( Q.Gate.PAULI_X, 2, [ 1 , 2 ]),
+ // Create lines of latitude and longitude.
+ // Note this is an inverse Mercatur projection ;)
- // Uses Q.Gate.findBySymbol() to lookup gates.
+ context.fillStyle = 'hsla( 0, 0%, 0%, 0.2 )'
+ const yStep = height / 16
+ for( let y = 0; y <= height; y += yStep ){
- new Q.Circuit( 2, 2 )
- .set$( 'H', 1, [ 1 ])
- .set$( 'X', 2, [ 1 , 2 ]),
+ context.fillRect( 0, y, width, 1 )
+ }
+ const xStep = width / 16
+ for( let x = 0; x <= width; x += xStep ){
- new Q.Circuit( 2, 2 )
- .set$( 'H', 1, 1 )
- .set$( 'X', 2, [ 1 , 2 ]),
+ context.fillRect( x, 0, 1, height )
+ }
- // Convenience gate functions -- constant name.
+ // Prepare the THREE texture and return it
+ // so we can use it as a material map.
- new Q.Circuit( 2, 2 )
- .HADAMARD( 1, [ 1 ])
- .PAULI_X( 2, [ 1, 2 ]),
+ const texture = new THREE.CanvasTexture( canvas )
+ texture.needsUpdate = true
+ return texture
+ },
- 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 ]),
+ createLongitudeArc: function( radius, segments, thetaStart, thetaLength ){
- new Q.Circuit( 2, 2 )
- .H( 1, 1 )
- .X( 2, [ 1, 2 ]),
+ 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).
- // Convenience gate functions -- lowercase symbol.
+ if( thetaLength >= Math.PI * 2 ){
- new Q.Circuit( 2, 2 )
- .h( 1, [ 1 ])
- .x( 2, [ 1, 2 ]),
+ geometry.vertices.push( geometry.vertices[ 0 ].clone() )
+ }
+ return geometry
+ },
+ createLatitudeArc: function( radius, segments, phiStart, phiLength ){
- new Q.Circuit( 2, 2 )// Perhaps the closest to Braket style.
- .h( 1, 1 )
- .x( 2, [ 1, 2 ]),
+ 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 ){
- // Q function -- bandwidth / timewidth arguments.
+ let {
- Q( 2, 2 )
- .h( 1, [ 1 ])
- .x( 2, [ 1, 2 ]),
+ radius,
+ phiStart,
+ phiLength,
+ thetaStart,
+ thetaLength,
+ latitudeLinesTotal,
+ longitudeLinesTotal,
+ latitudeLineSegments,
+ longitudeLineSegments,
+ latitudeLinesAttributes,
+ longitudeLinesAttributes
- Q( 2, 2 )
- .h( 1, 1 )
- .x( 2, [ 1, 2 ]),
+ } = 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 }
- // Q function -- text block argument
- // with operation symbols
- // and operation component IDs.
+ const
+ sphere = new THREE.Group(),
+ latitudeLinesMaterial = new THREE.LineBasicMaterial( latitudeLinesAttributes ),
+ longitudeLinesMaterial = new THREE.LineBasicMaterial( longitudeLinesAttributes )
- 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`,
+ // Lines of longitude.
+ // https://en.wikipedia.org/wiki/Longitude
+ for(
+
+ let
+ phiDelta = phiLength / longitudeLinesTotal,
+ phi = phiStart,
+ arc = BlochSphere.createLongitudeArc( radius, longitudeLineSegments, thetaStart + Math.PI / 2, thetaLength );
+ phi < phiStart + phiLength + phiDelta;
+ phi += phiDelta ){
+
+ const geometry = arc.clone()
+ geometry.rotateY( phi )
+ sphere.add( new THREE.Line( geometry, longitudeLinesMaterial ))
+ }
- // 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 ){
+ // Lines of latitude.
+ // https://en.wikipedia.org/wiki/Latitude
- return a.toText() === b.toText() ? a : NaN
+ for (
-})
-if( bellsAreEqual ){
+ let
+ thetaDelta = thetaLength / latitudeLinesTotal,
+ theta = thetaStart;
+ theta < thetaStart + thetaLength;
+ theta += thetaDelta ){
+
+ if( theta === 0 ) continue
+
+ const
+ arcR = radius * Math.sin( theta ),
+ arcH = radius * Math.cos( theta ),
+ geometry = BlochSphere.createLatitudeArc( arcR, latitudeLineSegments, phiStart, phiLength )
+
+ geometry.rotateX( Math.PI / 2 )
+ geometry.rotateY( Math.PI / 2 )
+ geometry.translate( 0, arcH, 0 )
+ sphere.add( new THREE.Line( geometry, latitudeLinesMaterial ))
+ }
- console.log( `\n\nYES. All of ${ bells.length } our “Bell” circuits are equal.\n\n`, bells )
-}
-*/
+ return sphere
+ }
+})
-Q.Circuit.createConstants(
+ ///////////////
+ // //
+ // Proto //
+ // //
+///////////////
- 'BELL', Q`
- H X#0
- I X#1
- `,
- // 'GROVER', Q`
+Object.assign( BlochSphere.prototype, {
- // 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
- // `
+ update: function(){
- //https://docs.microsoft.com/en-us/quantum/concepts/circuits?view=qsharp-preview
- // 'TELEPORT', Q.(`
+ if( this.isRotating ) this.group.rotation.y += Math.PI / 4096
+ },
+ setTargetState: function( target ){
- // I-I--H-M---v
- // H-C0-I-M-v-v
- // I-C1-I-I-X-Z-
- // `)
-)
-
+ if( target === undefined ) target = Qubit.HORIZONTAL.toBlochSphere()
+ // Always take the shortest path around
+ // even if it crosses the 0˚ / 360˚ boundary,
+ // ie. between Anti-Diagonal (-90˚) and
+ // Right0-and circular polarized (180˚).
+ const
+ rangeHalf = Math.PI,
+ distance = this.state.phi - target.phi
+ if( Math.abs( distance ) > rangeHalf ){
+ this.state.phi += Math.sign( distance ) * rangeHalf * -2
+ }
+ // Cheap hack to test if we need to update values
+ // from within the updateBlochVector method.
+ Object.assign( this.target, target )
+
+ // Create the tween.
+ window.tween = new TWEEN.Tween( this.state )
+ .to( target, 1000 )
+ .easing( TWEEN.Easing.Quadratic.InOut )
+ .onUpdate( this.updateBlochVector.bind( this ))
+ .start()
+ },
+ updateBlochVector: function( state ){
+ // Move the big-ass surface pointer.
+ if( state.theta !== this.target.theta ||
+ state.phi !== this.target.phi ){
+ this.blochPointer.position.set(
+
+ Math.sin( state.theta ) * Math.sin( state.phi ),
+ Math.cos( state.theta ),
+ Math.sin( state.theta ) * Math.cos( state.phi )
+ )
+ this.blochPointer.lookAt( new THREE.Vector3() )
+ this.blochVector.lookAt( this.blochPointer.getWorldPosition( new THREE.Vector3() ))
-/*
+ // Determine the correct HSL color
+ // based on Phi and Theta.
+ let hue = state.phi * THREE.Math.RAD2DEG
+ if( hue < 0 ) hue = 360 + hue
+ this.blochColor.setHSL(
-%%HTML
-
-
-
+ 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 ){
-%%javascript
-Q.braket( element )
+ // 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 ++ ){
-//%%javascript
+ 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 )
+ }
+ }
+})
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+module.exports = {BlochSphere}
-Q.Circuit.Editor = function( circuit, targetEl ){
+},{"quantum-js-util":13}],15:[function(require,module,exports){
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+const {Q, Circuit, Gate, logger, misc, mathf } = require('quantum-js-util');
+Editor = function( circuit, targetEl ){
// First order of business,
// we require a valid circuit.
- if( circuit instanceof Q.Circuit !== true ) circuit = new Q.Circuit()
+ if( circuit instanceof Circuit !== true ) circuit = new Circuit()
this.circuit = circuit
- this.index = Q.Circuit.Editor.index ++
+ this.index = Editor.index ++
- // Q.Circuit.Editor is all about the DOM
+ // Editor is all about the DOM
// so we’re going to get some use out of this
// stupid (but convenient) shorthand here.
@@ -5493,6 +5889,8 @@ Q.Circuit.Editor = function( circuit, targetEl ){
return document.createElement( 'div' )
}
+
+
@@ -5584,12 +5982,12 @@ Q.Circuit.Editor = function( circuit, targetEl ){
undoButton.setAttribute( 'title', 'Undo' )
undoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
undoButton.innerHTML = '⟲'
- window.addEventListener( 'Q.History undo is depleted', function( event ){
+ window.addEventListener( '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 ){
+ window.addEventListener( 'History undo is capable', function( event ){
if( event.detail.instance === circuit )
undoButton.removeAttribute( 'Q-disabled' )
@@ -5606,12 +6004,12 @@ Q.Circuit.Editor = function( circuit, targetEl ){
redoButton.setAttribute( 'title', 'Redo' )
redoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
redoButton.innerHTML = '⟳'
- window.addEventListener( 'Q.History redo is depleted', function( event ){
+ window.addEventListener( '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 ){
+ window.addEventListener( 'History redo is capable', function( event ){
if( event.detail.instance === circuit )
redoButton.removeAttribute( 'Q-disabled' )
@@ -5651,9 +6049,9 @@ Q.Circuit.Editor = function( circuit, targetEl ){
const boardContainerEl = createDiv()
circuitEl.appendChild( boardContainerEl )
boardContainerEl.classList.add( 'Q-circuit-board-container' )
- //boardContainerEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
+ //boardContainerEl.addEventListener( 'touchstart', Editor.onPointerPress )
boardContainerEl.addEventListener( 'mouseleave', function(){
- Q.Circuit.Editor.unhighlightAll( circuitEl )
+ Editor.unhighlightAll( circuitEl )
})
const boardEl = createDiv()
@@ -5677,7 +6075,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
rowEl.style.position = 'relative'
rowEl.style.gridRowStart = i + 2
rowEl.style.gridColumnStart = 1
- rowEl.style.gridColumnEnd = Q.Circuit.Editor.momentIndexToGridColumn( circuit.timewidth ) + 1
+ rowEl.style.gridColumnEnd = Editor.momentIndexToGridColumn( circuit.timewidth ) + 1
rowEl.setAttribute( 'register-index', i + 1 )
const wireEl = createDiv()
@@ -5694,7 +6092,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
const columnEl = createDiv()
backgroundEl.appendChild( columnEl )
columnEl.style.gridRowStart = 2
- columnEl.style.gridRowEnd = Q.Circuit.Editor.registerIndexToGridRow( circuit.bandwidth ) + 1
+ columnEl.style.gridRowEnd = Editor.registerIndexToGridRow( circuit.bandwidth ) + 1
columnEl.style.gridColumnStart = i + 3
columnEl.setAttribute( 'moment-index', i + 1 )
}
@@ -5731,7 +6129,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
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.style.gridRowStart = Editor.registerIndexToGridRow( registerIndex )
registersymbolEl.innerText = registerIndex
}
@@ -5742,7 +6140,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
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.style.gridRowStart = Editor.registerIndexToGridRow( circuit.bandwidth + 1 )
addRegisterEl.innerText = '+'
@@ -5758,7 +6156,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
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.style.gridColumnStart = Editor.momentIndexToGridColumn( momentIndex )
momentsymbolEl.innerText = momentIndex
}
@@ -5769,7 +6167,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
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.style.gridColumnStart = Editor.momentIndexToGridColumn( circuit.timewidth + 1 )
addMomentEl.innerText = '+'
@@ -5784,7 +6182,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
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.style.gridRowStart = Editor.registerIndexToGridRow( rowIndex )
inputEl.innerText = qubit.beta.toText()
foregroundEl.appendChild( inputEl )
})
@@ -5793,23 +6191,23 @@ Q.Circuit.Editor = function( circuit, targetEl ){
// Add operations.
circuit.operations.forEach( function( operation ){
- Q.Circuit.Editor.set( circuitEl, operation )
+ Editor.set( circuitEl, operation )
})
// Add event listeners.
- circuitEl.addEventListener( 'mousedown', Q.Circuit.Editor.onPointerPress )
- circuitEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
+ circuitEl.addEventListener( 'mousedown', Editor.onPointerPress )
+ circuitEl.addEventListener( 'touchstart', Editor.onPointerPress )
window.addEventListener(
- 'Q.Circuit.set$',
- Q.Circuit.Editor.prototype.onExternalSet.bind( this )
+ 'Circuit.set$',
+ Editor.prototype.onExternalSet.bind( this )
)
window.addEventListener(
- 'Q.Circuit.clear$',
- Q.Circuit.Editor.prototype.onExternalClear.bind( this )
+ 'Circuit.clear$',
+ Editor.prototype.onExternalClear.bind( this )
)
@@ -5830,8 +6228,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
// that includes how to reference the circuit via code
// and an ASCII diagram for reference.
- Q.log( 0.5,
-
+ logger.warn( 0.5,
`\n\nCreated a DOM interface for $('#${ this.domId }').circuit\n\n`,
circuit.toDiagram(),
'\n\n\n'
@@ -5839,15 +6236,15 @@ Q.Circuit.Editor = function( circuit, targetEl ){
}
-// Augment Q.Circuit to have this functionality.
+// Augment Circuit to have this functionality.
-Q.Circuit.toDom = function( circuit, targetEl ){
+Circuit.toDom = function( circuit, targetEl ){
- return new Q.Circuit.Editor( circuit, targetEl ).domElement
+ return new Editor( circuit, targetEl ).domElement
}
-Q.Circuit.prototype.toDom = function( targetEl ){
+Circuit.prototype.toDom = function( targetEl ){
- return new Q.Circuit.Editor( this, targetEl ).domElement
+ return new Editor( this, targetEl ).domElement
}
@@ -5857,10 +6254,10 @@ Q.Circuit.prototype.toDom = function( targetEl ){
-Object.assign( Q.Circuit.Editor, {
+Object.assign( Editor, {
index: 0,
- help: function(){ return Q.help( this )},
+ help: function(){ return logger.help( this )},
dragEl: null,
gridColumnToMomentIndex: function( gridColumn ){ return +gridColumn - 2 },
momentIndexToGridColumn: function( momentIndex ){ return momentIndex + 2 },
@@ -5876,7 +6273,7 @@ Object.assign( Q.Circuit.Editor, {
// based on our 4rem × 4rem grid setup.
const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize )
- return 1 + Math.floor( p / ( rem * Q.Circuit.Editor.gridSize ))
+ return 1 + Math.floor( p / ( rem * Editor.gridSize ))
},
gridToPoint: function( g ){
@@ -5886,7 +6283,7 @@ Object.assign( Q.Circuit.Editor, {
// and return the minimum point value it contains.
const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize )
- return rem * Q.Circuit.Editor.gridSize * ( g - 1 )
+ return rem * Editor.gridSize * ( g - 1 )
},
getInteractionCoordinates: function( event, pageOrClient ){
@@ -5898,11 +6295,16 @@ Object.assign( Q.Circuit.Editor, {
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 )
@@ -5916,12 +6318,12 @@ Object.assign( Q.Circuit.Editor, {
}
//ltnln: added missing Braket operations.
- paletteEl.classList.add( 'Q-circuit-palette' )
+ 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 gate = Gate.findBySymbol( symbol )
const operationEl = document.createElement( 'div' )
paletteEl.appendChild( operationEl )
@@ -5933,7 +6335,7 @@ Object.assign( Q.Circuit.Editor, {
const tileEl = document.createElement( 'div' )
operationEl.appendChild( tileEl )
tileEl.classList.add( 'Q-circuit-operation-tile' )
- if( symbol !== Q.Gate.CURSOR.symbol ) tileEl.innerText = symbol
+ if( symbol !== Gate.CURSOR.symbol ) tileEl.innerText = symbol
;[ 'before', 'after' ].forEach( function( layer ){
@@ -5943,9 +6345,13 @@ Object.assign( Q.Circuit.Editor, {
})
})
- paletteEl.addEventListener( 'mousedown', Q.Circuit.Editor.onPointerPress )
- paletteEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
+ paletteEl.addEventListener( 'mousedown', Editor.onPointerPress )
+ paletteEl.addEventListener( 'touchstart', Editor.onPointerPress )
return paletteEl
+ },
+ toDom: function( circuit, targetEl ){
+
+ return new Editor( circuit, targetEl ).domElement
}
})
@@ -5961,18 +6367,18 @@ Object.assign( Q.Circuit.Editor, {
/////////////////////////
-Q.Circuit.Editor.prototype.onExternalClear = function( event ){
+Editor.prototype.onExternalClear = function( event ){
if( event.detail.circuit === this.circuit ){
- Q.Circuit.Editor.clear( this.domElement, {
+ Editor.clear( this.domElement, {
momentIndex: event.detail.momentIndex,
registerIndices: event.detail.registerIndices
})
}
}
-Q.Circuit.Editor.clear = function( circuitEl, operation ){
+Editor.clear = function( circuitEl, operation ){
const momentIndex = operation.momentIndex
operation.registerIndices.forEach( function( registerIndex ){
@@ -6003,14 +6409,14 @@ Q.Circuit.Editor.clear = function( circuitEl, operation ){
///////////////////////
-Q.Circuit.Editor.prototype.onExternalSet = function( event ){
+Editor.prototype.onExternalSet = function( event ){
if( event.detail.circuit === this.circuit ){
- Q.Circuit.Editor.set( this.domElement, event.detail.operation )
+ Editor.set( this.domElement, event.detail.operation )
}
}
-Q.Circuit.Editor.set = function( circuitEl, operation ){
+Editor.set = function( circuitEl, operation ){
const
backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ),
foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
@@ -6029,15 +6435,15 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){
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 )
+ 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 !== Q.Gate.CURSOR.symbol ) tileEl.innerText = operation.gate.symbol
+ if( operation.gate.symbol !== Gate.CURSOR.symbol ) tileEl.innerText = operation.gate.symbol
// Add operation link wires
@@ -6072,9 +6478,9 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){
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.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' )
@@ -6094,7 +6500,7 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){
-Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
+Editor.isValidControlCandidate = function( circuitEl ){
const
selectedOperations = Array
@@ -6191,7 +6597,7 @@ Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
const gates = selectedOperations.reduce( function( gates, operationEl ){
const gateSymbol = operationEl.getAttribute( 'gate-symbol' )
- if( !Q.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1
+ if( !mathf.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1
else gates[ gateSymbol ] ++
return gates
@@ -6223,7 +6629,7 @@ Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
// and one or more of a regular single gate
// that is NOT already controlled.
- if( gates[ Q.Gate.CURSOR.symbol ] === 1 &&
+ if( gates[ Gate.CURSOR.symbol ] === 1 &&
Object.keys( gates ).length === 2 &&
totalNotControlled === selectedOperations.length ){
@@ -6235,7 +6641,7 @@ Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
// 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 &&
+ if( gates[ Gate.CURSOR.symbol ] === undefined &&
Object.keys( gates ).length === 1 &&
totalControlled > 0 &&
totalNotControlled > 0 ){
@@ -6248,9 +6654,9 @@ Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
return false
}
-Q.Circuit.Editor.createControl = function( circuitEl ){
+Editor.createControl = function( circuitEl ){
- if( Q.Circuit.Editor.isValidControlCandidate( circuitEl ) !== true ) return this
+ if( Editor.isValidControlCandidate( circuitEl ) !== true ) return this
const
@@ -6278,7 +6684,7 @@ Q.Circuit.Editor.createControl = function( circuitEl ){
control = existingControlEl || selectedOperations
.find( function( el ){
- return el.getAttribute( 'gate-symbol' ) === Q.Gate.CURSOR.symbol
+ return el.getAttribute( 'gate-symbol' ) === Gate.CURSOR.symbol
}),
targets = selectedOperations
.reduce( function( targets, el ){
@@ -6318,8 +6724,8 @@ Q.Circuit.Editor.createControl = function( circuitEl ){
// Update our toolbar button states.
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
return this
}
@@ -6327,7 +6733,7 @@ Q.Circuit.Editor.createControl = function( circuitEl ){
-Q.Circuit.Editor.isValidSwapCandidate = function( circuitEl ){
+Editor.isValidSwapCandidate = function( circuitEl ){
const
selectedOperations = Array
@@ -6344,7 +6750,7 @@ Q.Circuit.Editor.isValidSwapCandidate = function( circuitEl ){
areBothCursors = selectedOperations.every( function( operationEl ){
- return operationEl.getAttribute( 'gate-symbol' ) === Q.Gate.CURSOR.symbol
+ return operationEl.getAttribute( 'gate-symbol' ) === Gate.CURSOR.symbol
})
if( areBothCursors ) return true
@@ -6353,9 +6759,9 @@ Q.Circuit.Editor.isValidSwapCandidate = function( circuitEl ){
return false
}
-Q.Circuit.Editor.createSwap = function( circuitEl ){
+Editor.createSwap = function( circuitEl ){
- if( Q.Circuit.Editor.isValidSwapCandidate( circuitEl ) !== true ) return this
+ if( Editor.isValidSwapCandidate( circuitEl ) !== true ) return this
const
selectedOperations = Array
@@ -6384,7 +6790,7 @@ Q.Circuit.Editor.createSwap = function( circuitEl ){
})
circuit.set$(
- Q.Gate.SWAP,
+ Gate.SWAP,
momentIndex,
registerIndices
)
@@ -6392,8 +6798,8 @@ Q.Circuit.Editor.createSwap = function( circuitEl ){
// Update our toolbar button states.
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
return this
}
@@ -6401,23 +6807,23 @@ Q.Circuit.Editor.createSwap = function( circuitEl ){
-Q.Circuit.Editor.onSelectionChanged = function( circuitEl ){
+Editor.onSelectionChanged = function( circuitEl ){
const controlButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-control' )
- if( Q.Circuit.Editor.isValidControlCandidate( circuitEl )){
+ if( 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 )){
+ if( Editor.isValidSwapCandidate( circuitEl )){
swapButtonEl.removeAttribute( 'Q-disabled' )
}
else swapButtonEl.setAttribute( 'Q-disabled', true )
}
-Q.Circuit.Editor.onCircuitChanged = function( circuitEl ){
+Editor.onCircuitChanged = function( circuitEl ){
const circuit = circuitEl.circuit
window.dispatchEvent( new CustomEvent(
@@ -6435,7 +6841,7 @@ Q.Circuit.Editor.onCircuitChanged = function( circuitEl ){
-Q.Circuit.Editor.unhighlightAll = function( circuitEl ){
+Editor.unhighlightAll = function( circuitEl ){
Array.from( circuitEl.querySelectorAll(
@@ -6460,7 +6866,7 @@ Q.Circuit.Editor.unhighlightAll = function( circuitEl ){
//////////////////////
-Q.Circuit.Editor.onPointerMove = function( event ){
+Editor.onPointerMove = function( event ){
// We need our cursor coordinates straight away.
@@ -6471,7 +6877,7 @@ Q.Circuit.Editor.onPointerMove = function( event ){
// and also see if one of those is a circuit board container.
const
- { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ),
+ { x, y } = Editor.getInteractionCoordinates( event ),
foundEls = document.elementsFromPoint( x, y ),
boardContainerEl = foundEls.find( function( el ){
@@ -6482,7 +6888,7 @@ Q.Circuit.Editor.onPointerMove = function( event ){
// Are we in the middle of a circuit clipboard drag?
// If so we need to move that thing!
- if( Q.Circuit.Editor.dragEl !== null ){
+ if( Editor.dragEl !== null ){
// ex. Don’t scroll on touch devices!
@@ -6494,11 +6900,11 @@ Q.Circuit.Editor.onPointerMove = function( event ){
// 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'
+ Editor.dragEl.style.left = ( x + window.pageXOffset + Editor.dragEl.offsetX ) +'px'
+ Editor.dragEl.style.top = ( y + window.pageYOffset + 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( !boardContainerEl && Editor.dragEl.circuitEl ) Editor.dragEl.classList.add( 'Q-circuit-clipboard-danger' )
+ else Editor.dragEl.classList.remove( 'Q-circuit-clipboard-danger' )
}
@@ -6533,6 +6939,8 @@ 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 ){
@@ -6596,10 +7004,10 @@ Q.Circuit.Editor.onPointerMove = function( event ){
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 )
+ columnIndex = Editor.pointToGrid( xLocal ),
+ rowIndex = Editor.pointToGrid( yLocal ),
+ momentIndex = Editor.gridColumnToMomentIndex( columnIndex ),
+ registerIndex = Editor.gridRowToRegisterIndex( rowIndex )
// If this hover is “out of bounds”
@@ -6639,15 +7047,15 @@ Q.Circuit.Editor.onPointerMove = function( event ){
///////////////////////
-Q.Circuit.Editor.onPointerPress = function( event ){
+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 )
+ if( Editor.dragEl !== null ){
+
+ Editor.onPointerRelease( event )
return
}
const
@@ -6670,7 +7078,7 @@ Q.Circuit.Editor.onPointerPress = function( event ){
dragEl = document.createElement( 'div' )
dragEl.classList.add( 'Q-circuit-clipboard' )
- const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
+ const { x, y } = Editor.getInteractionCoordinates( event )
// Are we dealing with a circuit interface?
@@ -6697,7 +7105,7 @@ Q.Circuit.Editor.onPointerPress = function( event ){
circuitEl.classList.add( 'Q-circuit-locked' )
lockEl.innerText = '🔒'
- Q.Circuit.Editor.unhighlightAll( circuitEl )
+ Editor.unhighlightAll( circuitEl )
}
@@ -6719,7 +7127,7 @@ Q.Circuit.Editor.onPointerPress = function( event ){
if( circuitIsLocked ) {
- Q.warn( `User attempted to interact with a circuit editor but it was locked.` )
+ logger.warn( `User attempted to interact with a circuit editor but it was locked.` )
return
}
@@ -6762,16 +7170,16 @@ Q.Circuit.Editor.onPointerPress = function( event ){
if( undoEl && circuit.history.undo$() ){
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
}
if( redoEl && circuit.history.redo$() ){
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
}
- if( controlEl ) Q.Circuit.Editor.createControl( circuitEl )
- if( swapEl ) Q.Circuit.Editor.createSwap( circuitEl )
+ if( controlEl ) Editor.createControl( circuitEl )
+ if( swapEl ) Editor.createSwap( circuitEl )
if( addMomentEl ) console.log( '→ Add moment' )
if( addRegisterEl ) console.log( '→ Add register' )
@@ -6795,8 +7203,8 @@ Q.Circuit.Editor.onPointerPress = function( event ){
const
momentIndex = +cellEl.getAttribute( 'moment-index' ),
registerIndex = +cellEl.getAttribute( 'register-index' ),
- columnIndex = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ),
- rowIndex = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
+ columnIndex = Editor.momentIndexToGridColumn( momentIndex ),
+ rowIndex = Editor.registerIndexToGridRow( registerIndex )
// Looks like our circuit is NOT locked
@@ -6853,7 +7261,7 @@ Q.Circuit.Editor.onPointerPress = function( event ){
el.classList.add( 'Q-circuit-cell-selected' )
})
}
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
+ Editor.onSelectionChanged( circuitEl )
}
@@ -6890,9 +7298,9 @@ Q.Circuit.Editor.onPointerPress = function( event ){
// 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' ))
+ const operation = Gate.findBySymbol(operationEl.getAttribute( 'gate-symbol' ))
if( operation.has_parameters ) {
- Q.Circuit.Editor.onDoubleclick( event, operationEl )
+ Editor.onDoubleclick( event, operationEl )
return
}
}
@@ -6955,8 +7363,8 @@ Q.Circuit.Editor.onPointerPress = function( event ){
const
momentIndex = +el.getAttribute( 'moment-index' ),
registerIndex = +el.getAttribute( 'register-index' ),
- columnIndex = Q.Circuit.Editor.momentIndexToGridColumn( momentIndex ),
- rowIndex = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
+ columnIndex = Editor.momentIndexToGridColumn( momentIndex ),
+ rowIndex = Editor.registerIndexToGridRow( registerIndex )
columnIndexMin = Math.min( columnIndexMin, columnIndex )
rowIndexMin = Math.min( rowIndexMin, rowIndex )
@@ -6998,8 +7406,8 @@ Q.Circuit.Editor.onPointerPress = function( event ){
const
boardEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
bounds = boardEl.getBoundingClientRect(),
- minX = Q.Circuit.Editor.gridToPoint( columnIndexMin ),
- minY = Q.Circuit.Editor.gridToPoint( rowIndexMin )
+ minX = Editor.gridToPoint( columnIndexMin ),
+ minY = Editor.gridToPoint( rowIndexMin )
dragEl.offsetX = bounds.left + minX - x
dragEl.offsetY = bounds.top + minY - y
@@ -7013,7 +7421,7 @@ Q.Circuit.Editor.onPointerPress = function( event ){
const
bounds = operationEl.getBoundingClientRect(),
- { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
+ { x, y } = Editor.getInteractionCoordinates( event )
dragEl.appendChild( operationEl.cloneNode( true ))
dragEl.originEl = paletteEl
@@ -7028,11 +7436,11 @@ Q.Circuit.Editor.onPointerPress = function( event ){
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' ))
+ operationSkeleton = 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!!
+ //upon exiting, we should update the circuit!!!
circuitEl.circuit.set$(
operationEl.getAttribute( 'gate-symbol' ),
+operationEl.getAttribute( 'moment-index' ),
@@ -7040,6 +7448,7 @@ Q.Circuit.Editor.onPointerPress = function( event ){
[ +operationEl.getAttribute( 'register-index' )],
parameters
)
+ //on exiting the parameter-input-box, we should update the circuit!!
parameterEl.innerHTML = ""
return
}
@@ -7051,8 +7460,8 @@ Q.Circuit.Editor.onPointerPress = function( event ){
// and trigger a draw of it in the correct spot.
document.body.appendChild( dragEl )
- Q.Circuit.Editor.dragEl = dragEl
- Q.Circuit.Editor.onPointerMove( event )
+ Editor.dragEl = dragEl
+ Editor.onPointerMove( event )
}
@@ -7067,14 +7476,13 @@ Q.Circuit.Editor.onPointerPress = function( event ){
/////////////////////////
-Q.Circuit.Editor.onPointerRelease = function( event ){
+Editor.onPointerRelease = function( event ){
// If there’s no dragEl then bail immediately.
- if( Q.Circuit.Editor.dragEl === null ) return
+ 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()
@@ -7086,8 +7494,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// 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 { 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) => {
@@ -7106,20 +7513,20 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// If we were dragging from a palette
// we can just stop dragging.
- if( Q.Circuit.Editor.dragEl.circuitEl ){
+ if( Editor.dragEl.circuitEl ){
- Array.from( Q.Circuit.Editor.dragEl.children ).forEach( function( el ){
+ Array.from( Editor.dragEl.children ).forEach( function( el ){
- Q.Circuit.Editor.dragEl.originEl.appendChild( 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' )
})
- Q.Circuit.Editor.onSelectionChanged( Q.Circuit.Editor.dragEl.circuitEl )
+ Editor.onSelectionChanged( Editor.dragEl.circuitEl )
}
- document.body.removeChild( Q.Circuit.Editor.dragEl )
- Q.Circuit.Editor.dragEl = null
+ document.body.removeChild( Editor.dragEl )
+ Editor.dragEl = null
}
@@ -7128,15 +7535,15 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
if( !boardContainerEl ){
- if( Q.Circuit.Editor.dragEl.circuitEl ){
+ if( Editor.dragEl.circuitEl ){
const
- originCircuitEl = Q.Circuit.Editor.dragEl.circuitEl
+ originCircuitEl = Editor.dragEl.circuitEl
originCircuit = originCircuitEl.circuit
originCircuit.history.createEntry$()
Array
- .from( Q.Circuit.Editor.dragEl.children )
+ .from( Editor.dragEl.children )
.forEach( function( child ){
originCircuit.clear$(
@@ -7145,8 +7552,8 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
child.origin.registerIndex
)
})
- Q.Circuit.Editor.onSelectionChanged( originCircuitEl )
- Q.Circuit.Editor.onCircuitChanged( originCircuitEl )
+ Editor.onSelectionChanged( originCircuitEl )
+ Editor.onCircuitChanged( originCircuitEl )
}
@@ -7154,12 +7561,12 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// Let’s keep a private reference to
// the current clipboard.
- let clipboardToDestroy = Q.Circuit.Editor.dragEl
+ let clipboardToDestroy = Editor.dragEl
// Now we can remove our dragging reference.
- Q.Circuit.Editor.dragEl = null
+ Editor.dragEl = null
// Add our CSS animation routine
@@ -7211,13 +7618,13 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
bounds = boardContainerEl.getBoundingClientRect(),
droppedAtX = x - bounds.left + boardContainerEl.scrollLeft,
droppedAtY = y - bounds.top + boardContainerEl.scrollTop,
- droppedAtMomentIndex = Q.Circuit.Editor.gridColumnToMomentIndex(
+ droppedAtMomentIndex = Editor.gridColumnToMomentIndex(
- Q.Circuit.Editor.pointToGrid( droppedAtX )
+ Editor.pointToGrid( droppedAtX )
),
- droppedAtRegisterIndex = Q.Circuit.Editor.gridRowToRegisterIndex(
+ droppedAtRegisterIndex = Editor.gridRowToRegisterIndex(
- Q.Circuit.Editor.pointToGrid( droppedAtY )
+ Editor.pointToGrid( droppedAtY )
),
foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
@@ -7225,9 +7632,9 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// 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 ){
+ if( Editor.dragEl.circuitEl === circuitEl &&
+ Editor.dragEl.momentIndex === droppedAtMomentIndex &&
+ Editor.dragEl.registerIndex === droppedAtRegisterIndex ){
returnToOrigin()
return
@@ -7255,9 +7662,9 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// 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,
+ draggedOperations = Array.from( Editor.dragEl.children ),
+ draggedMomentDelta = droppedAtMomentIndex - Editor.dragEl.momentIndex,
+ draggedRegisterDelta = droppedAtRegisterIndex - Editor.dragEl.registerIndex,
setCommands = []
@@ -7278,10 +7685,10 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
momentIndexTarget = droppedAtMomentIndex,
registerIndexTarget = droppedAtRegisterIndex
- if( Q.Circuit.Editor.dragEl.circuitEl ){
+ if( Editor.dragEl.circuitEl ){
- momentIndexTarget += childEl.origin.momentIndex - Q.Circuit.Editor.dragEl.momentIndex
- registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex
+ momentIndexTarget += childEl.origin.momentIndex - Editor.dragEl.momentIndex
+ registerIndexTarget += childEl.origin.registerIndex - Editor.dragEl.registerIndex
}
@@ -7311,7 +7718,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
foundComponents = Array.from(
- Q.Circuit.Editor.dragEl.querySelectorAll(
+ Editor.dragEl.querySelectorAll(
`[moment-index="${ childEl.origin.momentIndex }"]`+
`[register-indices="${ registerIndicesString }"]`
@@ -7325,7 +7732,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
return aRegisterIndicesIndex - bRegisterIndicesIndex
}),
- allComponents = Array.from( Q.Circuit.Editor.dragEl.circuitEl.querySelectorAll(
+ allComponents = Array.from( Editor.dragEl.circuitEl.querySelectorAll(
`[moment-index="${ childEl.origin.momentIndex }"]`+
`[register-indices="${ registerIndicesString }"]`
@@ -7341,7 +7748,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// because that will be an identity / control / null gate.
// We need to look at slot 1.
- component1 = Q.Circuit.Editor.dragEl.querySelector(
+ component1 = Editor.dragEl.querySelector(
`[moment-index="${ childEl.origin.momentIndex }"]`+
`[register-index="${ registerIndices[ 1 ] }"]`
@@ -7360,7 +7767,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
draggedOperations.forEach( function( childEl ){
- Q.Circuit.Editor.dragEl.circuitEl.circuit.clear$(
+ Editor.dragEl.circuitEl.circuit.clear$(
childEl.origin.momentIndex,
childEl.origin.registerIndex
@@ -7375,7 +7782,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
if( registerIndices.length === foundComponents.length ){
- const operationSkeleton = Q.Gate.findBySymbol( gatesymbol )
+ const operationSkeleton = Gate.findBySymbol( gatesymbol )
parameters = {}
if( operationSkeleton.has_parameters ) {
Object.keys( operationSkeleton.parameters ).forEach( key => {
@@ -7397,9 +7804,9 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
const siblingDelta = registerIndex - childEl.origin.registerIndex
registerIndexTarget = droppedAtRegisterIndex
- if( Q.Circuit.Editor.dragEl.circuitEl ){
+ if( Editor.dragEl.circuitEl ){
- registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex + siblingDelta
+ registerIndexTarget += childEl.origin.registerIndex - Editor.dragEl.registerIndex + siblingDelta
}
return registerIndexTarget
}),
@@ -7416,7 +7823,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// and we’re staying within the same moment index
// that might be ok!
- else if( Q.Circuit.Editor.dragEl.circuitEl === circuitEl &&
+ else if( Editor.dragEl.circuitEl === circuitEl &&
momentIndexTarget === childEl.origin.momentIndex ){
@@ -7455,7 +7862,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
const
componentRegisterIndex = +component.getAttribute( 'register-index' ),
- registerGrabDelta = componentRegisterIndex - Q.Circuit.Editor.dragEl.registerIndex
+ registerGrabDelta = componentRegisterIndex - Editor.dragEl.registerIndex
// Now put it where it wants to go,
@@ -7485,10 +7892,10 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// ie. if a dragged sibling overwrote a seated one.
.filter( function( entry ){
- return Q.isUsefulInteger( entry )
+ return mathf.isUsefulInteger( entry )
})
- const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) )
+ const operationSkeleton = Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) )
parameters = {}
if( operationSkeleton.has_parameters ) {
Object.keys( operationSkeleton.parameters ).forEach( key => {
@@ -7499,8 +7906,8 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// 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 :
+ fixedRegistersIndices.length < 2 && Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ Gate.NOOP :
childEl.getAttribute( 'gate-symbol' ),
momentIndexTarget,
fixedRegistersIndices,
@@ -7511,7 +7918,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
else {
remainingComponents.forEach( function( componentEl, i ){
//circuit.set$(
- const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
+ const operationSkeleton = Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
parameters = {}
if( operationSkeleton.has_parameters ) {
Object.keys( operationSkeleton.parameters ).forEach( key => {
@@ -7520,9 +7927,9 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
}
setCommands.push([
- +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ +componentEl.getAttribute( 'register-indices-index' ) && !Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
gatesymbol :
- Q.Gate.NOOP,
+ Gate.NOOP,
+componentEl.getAttribute( 'moment-index' ),
+componentEl.getAttribute( 'register-index' ),
parameters
@@ -7535,7 +7942,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// all the components that were part of the drag.
foundComponents.forEach( function( componentEl ){
- const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
+ const operationSkeleton = Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
parameters = {}
if( operationSkeleton.has_parameters ) {
Object.keys( operationSkeleton.parameters ).forEach( key => {
@@ -7545,9 +7952,9 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
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( 'register-indices-index' ) && !Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
componentEl.getAttribute( 'gate-symbol' ) :
- Q.Gate.NOOP,
+ Gate.NOOP,
+componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta,
+componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta,
parameters
@@ -7589,11 +7996,11 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// (and not a circuit palette)
// make sure the old positions are cleared away.
- if( Q.Circuit.Editor.dragEl.circuitEl ){
+ if( Editor.dragEl.circuitEl ){
draggedOperations.forEach( function( childEl ){
- Q.Circuit.Editor.dragEl.circuitEl.circuit.clear$(
+ Editor.dragEl.circuitEl.circuit.clear$(
childEl.origin.momentIndex,
childEl.origin.registerIndex
@@ -7609,7 +8016,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
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' ))
+ const operationSkeleton = Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ))
if(operationSkeleton.is_multi_qubit ) {
registerIndices.push( registerIndexTarget == circuit.bandwidth ? registerIndexTarget - 1 : registerIndexTarget + 1)
}
@@ -7640,20 +8047,20 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// Are we capable of making controls? Swaps?
- Q.Circuit.Editor.onSelectionChanged( circuitEl )
- Q.Circuit.Editor.onCircuitChanged( circuitEl )
+ 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( Q.Circuit.Editor.dragEl.circuitEl &&
- Q.Circuit.Editor.dragEl.circuitEl !== circuitEl ){
+ if( Editor.dragEl.circuitEl &&
+ Editor.dragEl.circuitEl !== circuitEl ){
- const originCircuitEl = Q.Circuit.Editor.dragEl.circuitEl
- Q.Circuit.Editor.onSelectionChanged( originCircuitEl )
- Q.Circuit.Editor.onCircuitChanged( originCircuitEl )
+ const originCircuitEl = Editor.dragEl.circuitEl
+ Editor.onSelectionChanged( originCircuitEl )
+ Editor.onCircuitChanged( originCircuitEl )
}
@@ -7662,8 +8069,8 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// It’s been a long journey.
// I love you all.
- document.body.removeChild( Q.Circuit.Editor.dragEl )
- Q.Circuit.Editor.dragEl = null
+ document.body.removeChild( Editor.dragEl )
+ Editor.dragEl = null
}
@@ -7673,12 +8080,9 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// //
/////////////////////////
//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 )
-
+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) => {
@@ -7691,14 +8095,10 @@ 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 = Editor.createNewElement( 'button', parameterEl, 'Q-parameter-box-exit')
exit.appendChild(document.createTextNode( '⬅' ))
parameterEl.setAttribute( "operation-moment-index", operationEl.getAttribute( 'moment-index' ))
parameterEl.setAttribute( "operation-register-index", operationEl.getAttribute( 'register-index' ))
-
-
//here we generate queries for each parameter that the gate operation takes!
const parameters = Object.keys(operation.parameters)
parameters.forEach( element => {
@@ -7706,46 +8106,35 @@ 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 = 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 = 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
+ let parameterValue = +textbox.value;
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()
+ if( parameterValue === null || parameterValue === Infinity ) textbox.value = oldValue.toString()
else {
operationEl.setAttribute( element, parameterValue )
- textbox.value = parameterValue.toString()
+ textbox.value = parameterValue
}
})
}
})
-
parameterEl.classList.toggle('overlay')
parameterEl.style.display = 'block'
}
+
///////////////////
// //
// Listeners //
@@ -7753,44 +8142,21 @@ Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) {
///////////////////
-// These listeners must be applied
+// These listeners must be appliedm
// 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
-
-
-
+window.addEventListener( 'mousemove', Editor.onPointerMove )
+window.addEventListener( 'touchmove', Editor.onPointerMove )
+window.addEventListener( 'mouseup', Editor.onPointerRelease )
+window.addEventListener( 'touchend', Editor.onPointerRelease )
+module.exports = {Editor}
+},{"quantum-js-util":13}],16:[function(require,module,exports){
+const {Editor} = require('./Q-Circuit-Editor');
+const {circuit} = require('quantum-js-util');
+const {BlochSphere} = require('./Q-BlochSphere');
+console.log("Welcome to Q.js! The GUI experience!\n");
-
-%%javascript
-Q.braket( element )
-
-
-
-
-*/
-
-
-
-//%%javascript
-
-
-
-Q.braket = function(){
+braket = function(){
// Create the HTML bits we need,
@@ -7807,13 +8173,16 @@ Q.braket = function(){
circuit = new Q( args[0] )
}
else {
- circuit = new Q( args[0], args[1] )
+ if(args[0] <= 0 || args[1] <= 0) circuit = new Q(4, 8);
+ else circuit = new Q( args[0], args[1] )
}
container = document.createElement( 'div' )
- container.appendChild( Q.Circuit.Editor.createPalette() )
+ let paletteEl = Editor.createPalette();
+ paletteEl.style.width = "50%";
+ container.appendChild( paletteEl );
container.appendChild( circuit.toDom() )
element.html( container )
-
+
// We’re going to take this SLOOOOOOOOWLY
// because there are many potential things to debug.
@@ -7848,13 +8217,14 @@ Q.braket = function(){
}
})
- window.addEventListener( 'Q.Circuit.evaluate completed', function( event ) {
+ window.addEventListener( 'Circuit.evaluate completed', function( event ) {
if( event.detail.circuit === circuit ) {
nextNextCell.set_text( circuit.report$() )
}
})
+
// nextCell.render()
// console.log( 'thisCell', thisCell )
@@ -7871,4 +8241,6 @@ Q.braket = function(){
// Jupyter.notebook.get_cell(t_index).render()
}
-module.exports = Q
\ No newline at end of file
+
+module.exports = {Editor, BlochSphere, braket};
+},{"./Q-BlochSphere":14,"./Q-Circuit-Editor":15,"quantum-js-util":13}]},{},[1]);
diff --git a/build/q.css b/build/q.css
index de56074..e4a6620 100644
--- a/build/q.css
+++ b/build/q.css
@@ -1,6 +1,6 @@
/*
- Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
*/
@charset "utf-8";
@@ -11,8 +11,8 @@
/*
This file is in the process of being separated
- in to “essential global Q.js styles” which will
- remain here in Q.css, and “documentation-specific”
+ in to “essential global Q.js styles†which will
+ remain here in Q.css, and “documentation-specificâ€
styles which will be removed from here and placed
within the /other/documentation.css file instead.
@@ -319,7 +319,7 @@ svg, :root {
/*
- The below still need to be prefaced with “Q-”
+ The below still need to be prefaced with “Q-â€
and for the HTML pages to be updated accordingly.
*/
@@ -341,6 +341,8 @@ svg, :root {
max-width: 100%;
overflow-x: auto;
font-family: var( --Q-font-family-sans );
+ /*letter-spacing: 0.03em;*/
+ word-spacing: 0.2em;
}
dd .maths {
@@ -393,22 +395,23 @@ dd .maths {
vertical-align: middle;
position: relative;
align: middle;
- margin: 1em;
+ margin: 1em 0.5em;
padding: 1em;
- font-family: var( --Q-font-family-mono );
+ /*font-family: var( --Q-font-family-mono );*/
font-weight: 300;
line-height: 1em;
- text-align: right;
+ /*text-align: right;*/
+ text-align: center;
}
.matrix td {
- padding: 5px 10px;
+ padding: 0.25em 0.5em;
}
.matrix-bracket-left, .matrix-bracket-right {
position: absolute;
top: 0;
- width: 5px;
+ width: 0.5em;
height: 100%;
border: 1px solid #CCC;
}
@@ -437,7 +440,7 @@ dd .maths {
.Q-state-vector.bra::before,
.complex-vector.bra::before {
- content: '⟨';
+ content: '⟨';
color: #BBB;
}
.Q-state-vector.bra::after,
@@ -455,7 +458,7 @@ dd .maths {
.Q-state-vector.ket::after,
.complex-vector.ket::after {
- content: '⟩';
+ content: '⟩';
color: #BBB;
}
.Q-state-vector.bra + .Q-state-vector.ket::before,
@@ -472,7 +475,7 @@ dd .maths {
/*
- Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
*/
@charset "utf-8";
@@ -484,7 +487,6 @@ dd .maths {
-
/*
Z indices:
@@ -567,11 +569,12 @@ dd .maths {
+
.Q-circuit,
.Q-circuit-palette {
position: relative;
- width: 100%;
+ width: 50%;
}
.Q-circuit-palette {
@@ -595,6 +598,7 @@ dd .maths {
margin: 1rem 0 2rem 0;
/*border-top: 2px solid hsl( 0, 0%, 50% );*/
}
+.Q-parameters-box,
.Q-circuit-board-foreground {
line-height: 3.85rem;
@@ -747,6 +751,19 @@ dd .maths {
grid-auto-columns: 4rem;
grid-auto-flow: column;
}
+
+.Q-parameters-box {
+
+ position: absolute;
+ display: none;
+ z-index: 100;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: whitesmoke;
+}
+
/*.Q-circuit-palette,*/
.Q-circuit-board-foreground,
.Q-circuit-board-background {
@@ -836,8 +853,17 @@ dd .maths {
);
}
+.Q-parameter-box-exit {
+ position: relative;
+ right: 0;
+ left: 0;
+ width: 5rem;
+ height: 2.5rem;
+ background-color: whitesmoke;
+ border-radius: 0;
+}
-
+.Q-parameters-box > div,
.Q-circuit-palette > div,
.Q-circuit-clipboard > div,
.Q-circuit-board-foreground > div {
@@ -1079,7 +1105,7 @@ dd .maths {
rgba( 0, 0, 0, 0.05 )
)*/;
}
-.Q-circuit-palette .Q-circuit-operation:hover {
+.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover {
/*background-color: rgba( 255, 255, 255, 0.6 );*/
background-color: white;
@@ -1192,6 +1218,25 @@ dd .maths {
}
+.Q-parameter-box-input-container {
+ position: relative;
+ text-align: center;
+ grid-auto-columns: 4rem;
+ grid-auto-flow: column;
+}
+
+.Q-parameter-box-input {
+ position: relative;
+ border-radius: .2rem;
+ margin-left: 10px;
+ font-family: var( --Q-font-family-mono );
+}
+
+.Q-parameter-input-label {
+ position: relative;
+ color: var( --Q-color-blue );
+ font-family: var( --Q-font-family-mono );
+}
@@ -1329,4 +1374,3 @@ dd .maths {
}
-
diff --git a/build/q.js b/build/q.js
index 52a0ab0..ddcf90e 100644
--- a/build/q.js
+++ b/build/q.js
@@ -1,15 +1,10 @@
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-
-
-
-
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
const Q = function(){
// Did we send arguments of the form
// ( bandwidth, timewidth )?
-
if( arguments.length === 2 &&
Array.from( arguments ).every( function( argument ){
@@ -68,7 +63,7 @@ Object.assign( Q, {
function countPrefixTabs( text ){
- // Is counting tabs “manually”
+ // Is counting tabs “manuallyâ€
// actually more performant than regex?
let count = index = 0
@@ -79,7 +74,7 @@ Object.assign( Q, {
//------------------- TO DO!
// we should check that there is ONLY whitespace between the function opening and the tick mark!
- // otherwise it’s not documentation.
+ // otherwise it’s not documentation.
let
tabs = Number.MAX_SAFE_INTEGER
@@ -284,11 +279,11 @@ Q.createConstants(
'REVISION', 19,
- // Yeah... F’ing floating point numbers, Man!
- // Here’s the issue:
+ // Yeah... F’ing floating point numbers, Man!
+ // Here’s the issue:
// var a = new Q.ComplexNumber( 1, 2 )
// a.multiply(a).isEqualTo( a.power( new Q.ComplexNumber( 2, 0 )))
- // That’s only true if Q.EPSILON >= Number.EPSILON * 6
+ // That’s only true if Q.EPSILON >= Number.EPSILON * 6
'EPSILON', Number.EPSILON * 6,
@@ -506,7 +501,7 @@ Q.createConstants(
'Turkey',
'Turtle',
// U
- 'Vicuña',
+ 'Vicuña',
'Viper',
'Vulture',
'Wallaby',
@@ -622,7 +617,7 @@ https://quantumjavascript.app
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
@@ -630,28 +625,28 @@ https://quantumjavascript.app
Q.ComplexNumber = function( real, imaginary ){
`
- The set of “real numbers” (ℝ) contains any number that can be expressed
+ The set of “real numbers†(â„) contains any number that can be expressed
along an infinite timeline. https://en.wikipedia.org/wiki/Real_number
- … -3 -2 -1 0 +1 +2 +3 …
- ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄
- √2 𝒆 π
+ … -3 -2 -1 0 +1 +2 +3 …
+ ┄───┴───┴───┴───┴───┴─┬─┴──┬┴┬──┄
+ √2 𒆠π
- Meanwhile, “imaginary numbers” (𝕀) consist of a real (ℝ) multiplier and
- the symbol 𝒊, which is the impossible solution to the equation 𝒙² = −1.
+ Meanwhile, “imaginary numbers†(ð•€) consist of a real (â„) multiplier and
+ the symbol ð’Š, which is the impossible solution to the equation ð’™Â² = −1.
Note that no number when multiplied by itself can ever result in a
- negative product, but the concept of 𝒊 gives us a way to reason around
+ negative product, but the concept of ð’Š gives us a way to reason around
this imaginary scenario nonetheless.
https://en.wikipedia.org/wiki/Imaginary_number
- … -3𝒊 -2𝒊 -1𝒊 0𝒊 +1𝒊 +2𝒊 +3𝒊 …
- ┄───┴───┴───┴───┴───┴───┴───┴───┄
+ … -3𒊠-2𒊠-1𒊠0𒊠+1𒊠+2𒊠+3𒊠…
+ ┄───┴───┴───┴───┴───┴───┴───┴───┄
- A “complex number“ (ℂ) is a number that can be expressed in the form
- 𝒂 + 𝒃𝒊, where 𝒂 is the real component (ℝ) and 𝒃𝒊 is the imaginary
- component (𝕀). https://en.wikipedia.org/wiki/Complex_number
+ A “complex number“ (ℂ) is a number that can be expressed in the form
+ ð’‚ + ð’ƒð’Š, where ð’‚ is the real component (â„) and ð’ƒð’Š is the imaginary
+ component (ð•€). https://en.wikipedia.org/wiki/Complex_number
Operation functions on Q.ComplexNumber instances generally accept as
@@ -664,7 +659,7 @@ Q.ComplexNumber = function( real, imaginary ){
imaginary = real.imaginary
real = real.real
- Q.warn( 'Q.ComplexNumber tried to create a new instance with an argument that is already a Q.ComplexNumber — and that’s weird!' )
+ Q.warn( 'Q.ComplexNumber tried to create a new instance with an argument that is already a Q.ComplexNumber — and that’s weird!' )
}
else if( real === undefined ) real = 0
if( imaginary === undefined ) imaginary = 0
@@ -687,6 +682,82 @@ Object.assign( Q.ComplexNumber, {
constants: {},
createConstant: Q.createConstant,
createConstants: Q.createConstants,
+
+
+
+
+ toText: function( rNumber, iNumber, roundToDecimal, padPositive ){
+
+ // Should we round these numbers?
+ // Our default is yes: to 3 digits.
+ // Otherwise round to specified decimal.
+
+ if( typeof roundToDecimal !== 'number' ) roundToDecimal = 3
+ const factor = Math.pow( 10, roundToDecimal )
+ rNumber = Math.round( rNumber * factor ) / factor
+ iNumber = Math.round( iNumber * factor ) / factor
+
+
+ // Convert padPositive
+ // from a potential Boolean
+ // to a String.
+ // If we don’t receive a FALSE
+ // then we’ll pad the positive numbers.
+
+ padPositive = padPositive === false ? '' : ' '
+
+
+ // We need the absolute values of each.
+
+ let
+ rAbsolute = Math.abs( rNumber ),
+ iAbsolute = Math.abs( iNumber )
+
+
+ // And an absolute value string.
+
+ let
+ rText = rAbsolute.toString(),
+ iText = iAbsolute.toString()
+
+
+ // Is this an IMAGINARY-ONLY number?
+ // Don’t worry: -0 === 0.
+
+ if( rNumber === 0 ){
+
+ if( iNumber === Infinity ) return padPositive +'∞i'
+ if( iNumber === -Infinity ) return '-∞i'
+ if( iNumber === 0 ) return padPositive +'0'
+ if( iNumber === -1 ) return '-i'
+ if( iNumber === 1 ) return padPositive +'i'
+ if( iNumber >= 0 ) return padPositive + iText +'i'
+ if( iNumber < 0 ) return '-'+ iText +'i'
+ return iText +'i'// NaN
+ }
+
+
+ // This number contains a real component
+ // and may also contain an imaginary one as well.
+
+ if( rNumber === Infinity ) rText = padPositive +'∞'
+ else if( rNumber === -Infinity ) rText = '-∞'
+ else if( rNumber >= 0 ) rText = padPositive + rText
+ else if( rNumber < 0 ) rText = '-'+ rText
+
+ if( iNumber === Infinity ) return rText +' + ∞i'
+ if( iNumber === -Infinity ) return rText +' - ∞i'
+ if( iNumber === 0 ) return rText
+ if( iNumber === -1 ) return rText +' - i'
+ if( iNumber === 1 ) return rText +' + i'
+ if( iNumber > 0 ) return rText +' + '+ iText +'i'
+ if( iNumber < 0 ) return rText +' - '+ iText +'i'
+ return rText +' + '+ iText +'i'// NaN
+ },
+
+
+
+
isNumberLike: function( n ){
return isNaN( n ) === false && ( typeof n === 'number' || n instanceof Number )
@@ -746,6 +817,7 @@ Object.assign( Q.ComplexNumber, {
+
absolute: function( n ){
return Q.hypotenuse( n.real, n.imaginary )
@@ -875,7 +947,7 @@ Object.assign( Q.ComplexNumber, {
// If our exponent is Real (has no Imaginary component)
- // then we’re really just raising to a power.
+ // then we’re really just raising to a power.
if( b.imaginary === 0 ){
@@ -987,7 +1059,7 @@ Object.assign( Q.ComplexNumber, {
firsts = a.real * b.real,
outers = a.real * b.imaginary,
inners = a.imaginary * b.real,
- lasts = a.imaginary * b.imaginary * -1// Because i² = -1.
+ lasts = a.imaginary * b.imaginary * -1// Because i² = -1.
return new Q.ComplexNumber(
@@ -1021,7 +1093,7 @@ Object.assign( Q.ComplexNumber, {
function( a, b ){
- // Ermergerd I had to look this up because it’s been so long.
+ // Ermergerd I had to look this up because it’s been so long.
// https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/complex-conjugates-and-dividing-complex-numbers/a/dividing-complex-numbers-review
const
@@ -1145,6 +1217,13 @@ Object.assign( Q.ComplexNumber.prototype, {
if( this.imaginary === 0 ) return this.real
return this
},
+ toText: function( roundToDecimal, padPositive ){
+
+
+ // Note: this will kill function chaining.
+
+ return Q.ComplexNumber.toText( this.real, this.imaginary, roundToDecimal, padPositive )
+ },
isNaN: function( n ){
@@ -1209,84 +1288,6 @@ Object.assign( Q.ComplexNumber.prototype, {
},
- toText: function( roundToDecimal, padPositive ){
-
-
- // Convert padPositive
- // from a potential Boolean
- // to a String.
- // If we don’t receive a FALSE
- // then we’ll pad the positive numbers.
-
- padPositive = padPositive === false ? '' : ' '
-
-
- // Right. Let’s copy over the actual numbers.
-
- let
- rNumber = this.real,
- iNumber = this.imaginary
-
-
- // Should we round these numbers?
- // Our default is yes: to 3 digits.
- // Otherwise round to specified decimal.
-
- if( typeof roundToDecimal !== 'number' ) roundToDecimal = 3
- const factor = Math.pow( 10, roundToDecimal )
- rNumber = Math.round( rNumber * factor ) / factor
- iNumber = Math.round( iNumber * factor ) / factor
-
-
- // We need the absolute values of each.
-
- let
- rAbsolute = Math.abs( rNumber ),
- iAbsolute = Math.abs( iNumber )
-
-
- // And an absolute value string.
-
- let
- rText = rAbsolute.toString(),
- iText = iAbsolute.toString()
-
-
- // Is this an IMAGINARY-ONLY number?
- // Don’t worry: -0 === 0.
-
- if( rNumber === 0 ){
-
- if( iNumber === Infinity ) return padPositive +'∞i'
- if( iNumber === -Infinity ) return '-∞i'
- if( iNumber === 0 ) return padPositive +'0'
- if( iNumber === -1 ) return '-i'
- if( iNumber === 1 ) return padPositive +'i'
- if( iNumber >= 0 ) return padPositive + iText +'i'
- if( iNumber < 0 ) return '-'+ iText +'i'
- return iText +'i'// NaN
- }
-
-
- // This number contains a real component
- // and may also contain an imaginary one as well.
-
- if( rNumber === Infinity ) rText = padPositive +'∞'
- else if( rNumber === -Infinity ) rText = '-∞'
- else if( rNumber >= 0 ) rText = padPositive + rText
- else if( rNumber < 0 ) rText = '-'+ rText
-
- if( iNumber === Infinity ) return rText +' + ∞i'
- if( iNumber === -Infinity ) return rText +' - ∞i'
- if( iNumber === 0 ) return rText
- if( iNumber === -1 ) return rText +' - i'
- if( iNumber === 1 ) return rText +' + i'
- if( iNumber > 0 ) return rText +' + '+ iText +'i'
- if( iNumber < 0 ) return rText +' - '+ iText +'i'
- return rText +' + '+ iText +'i'// NaN
- },
-
-
// DESTRUCTIVE operations.
@@ -1337,7 +1338,7 @@ Object.assign( Q.ComplexNumber.prototype, {
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
@@ -1345,7 +1346,7 @@ Object.assign( Q.ComplexNumber.prototype, {
Q.Matrix = function(){
- // We’re keeping track of how many matrices are
+ // We’re keeping track of how many matrices are
// actually being generated. Just curiosity.
this.index = Q.Matrix.index ++
@@ -1379,10 +1380,10 @@ Q.Matrix = function(){
}
else {
- // Matrices’ primary organization is by rows,
+ // Matrices’ primary organization is by rows,
// which is more congruent with our written langauge;
// primarily organizated by horizontally juxtaposed glyphs.
- // That means it’s easier to write an instance invocation in code
+ // That means it’s easier to write an instance invocation in code
// and easier to read when inspecting properties in the console.
let matrixWidthIsBroken = false
@@ -1413,7 +1414,7 @@ Q.Matrix = function(){
for( let y = 0; y < this.rows.length; y ++ ){
- // Since we’re combing through here
+ // Since we’re combing through here
// this is a good time to convert Number to ComplexNumber!
const value = matrix.rows[ y ][ x ]
@@ -1547,7 +1548,7 @@ Object.assign( Q.Matrix, {
const f = Q.Matrix[ 'from'+ format ]
format = format.toLowerCase()
if( typeof f !== 'function' )
- return Q.error( `Q.Matrix could not find an importer for “${format}” data.` )
+ return Q.error( `Q.Matrix could not find an importer for “${format}†data.` )
return f
},
fromArray: function( array ){
@@ -1999,16 +2000,16 @@ Q.Matrix.createConstants(
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-Q.Qubit = function( a, b, label, name ){
+Q.Qubit = function( a, b, symbol, name ){
- // If we’ve received an instance of Q.Matrix as our first argument
- // then we’ll assume there are no further arguments
+ // If we’ve received an instance of Q.Matrix as our first argument
+ // then we’ll assume there are no further arguments
// and just use that matrix as our new Q.Qubit instance.
if( Q.Matrix.isMatrixLike( a ) && b === undefined ){
@@ -2021,14 +2022,14 @@ Q.Qubit = function( a, b, label, name ){
// All of our internal math now uses complex numbers
// rather than Number literals
- // so we’d better convert!
+ // so we’d better convert!
if( typeof a === 'number' ) a = new Q.ComplexNumber( a, 0 )
if( typeof b === 'number' ) b = new Q.ComplexNumber( b, 0 )
// If we receive undefined (or garbage inputs)
- // let’s try to make it useable.
+ // let’s try to make it useable.
// This way we can always call Q.Qubit with no arguments
// to make a new qubit available for computing with.
@@ -2036,10 +2037,10 @@ Q.Qubit = function( a, b, label, name ){
if( b instanceof Q.ComplexNumber !== true ){
- // 1 - |𝒂|² = |𝒃|²
- // So this does NOT account for if 𝒃 ought to be imaginary or not.
+ // 1 - |ð’‚|² = |ð’ƒ|²
+ // So this does NOT account for if ð’ƒ ought to be imaginary or not.
// Perhaps for completeness we could randomly decide
- // to flip the real and imaginary components of 𝒃 after this line?
+ // to flip the real and imaginary components of ð’ƒ after this line?
b = Q.ComplexNumber.ONE.subtract( Math.pow( a.absolute(), 2 )).squareRoot()
}
@@ -2047,7 +2048,7 @@ Q.Qubit = function( a, b, label, name ){
// Sanity check!
- // Does this constraint hold true? |𝒂|² + |𝒃|² = 1
+ // Does this constraint hold true? |ð’‚|² + |ð’ƒ|² = 1
if( Math.pow( a.absolute(), 2 ) + Math.pow( b.absolute(), 2 ) - 1 > Q.EPSILON )
return Q.error( `Q.Qubit could not accept the initialization values of a=${a} and b=${b} because their squares do not add up to 1.` )
@@ -2056,7 +2057,7 @@ Q.Qubit = function( a, b, label, name ){
this.index = Q.Qubit.index ++
- // Convenience getters and setters for this qubit’s
+ // Convenience getters and setters for this qubit’s
// controll bit and target bit.
Object.defineProperty( this, 'alpha', {
@@ -2071,11 +2072,11 @@ Q.Qubit = function( a, b, label, name ){
})
- // Used for Dirac notation: |?⟩
+ // Used for Dirac notation: |?⟩
- if( typeof label === 'string' ) this.label = label
+ if( typeof symbol === 'string' ) this.symbol = symbol
if( typeof name === 'string' ) this.name = name
- if( this.label === undefined || this.name === undefined ){
+ if( this.symbol === undefined || this.name === undefined ){
const found = Object.values( Q.Qubit.constants ).find( function( qubit ){
@@ -2087,12 +2088,12 @@ Q.Qubit = function( a, b, label, name ){
})
if( found === undefined ){
- this.label = '?'
+ this.symbol = '?'
this.name = 'Unnamed'
}
else {
- if( this.label === undefined ) this.label = found.label
+ if( this.symbol === undefined ) this.symbol = found.symbol
if( this.name === undefined ) this.name = found.name
}
}
@@ -2182,7 +2183,7 @@ Object.assign( Q.Qubit, {
This is means of inverting what comes first:
the Gate or the Qubit?
If the Gate only operates on a single qubit,
- then it doesn’t matter and we can do this:
+ then it doesn’t matter and we can do this:
`
if( gate instanceof Q.Gate === false ) return Q.error( `Q.Qubit attempted to apply something that was not a gate to this qubit #${ qubit.index }.` )
@@ -2190,12 +2191,12 @@ Object.assign( Q.Qubit, {
},
toText: function( qubit ){
- //return `|${qubit.beta.toText()}⟩`
+ //return `|${qubit.beta.toText()}⟩`
return qubit.alpha.toText() +'\n'+ qubit.beta.toText()
},
toStateVectorText: function( qubit ){
- return `|${ qubit.beta.toText() }⟩`
+ return `|${ qubit.beta.toText() }⟩`
},
toStateVectorHtml: function( qubit ){
@@ -2205,7 +2206,7 @@ Object.assign( Q.Qubit, {
// This code was a pain in the ass to figure out.
- // I’m not fluent in trigonometry
+ // I’m not fluent in trigonometry
// and none of the quantum primers actually lay out
// how to convert arbitrary qubit states
// to Bloch Sphere representation.
@@ -2217,9 +2218,9 @@ Object.assign( Q.Qubit, {
toBlochSphere: function( qubit ){
`
- Based on this qubit’s state return the
- Polar angle θ (theta),
- azimuth angle ϕ (phi),
+ Based on this qubit’s state return the
+ Polar angle θ (theta),
+ azimuth angle Ï• (phi),
Bloch vector,
corrected surface coordinate.
@@ -2227,14 +2228,14 @@ Object.assign( Q.Qubit, {
`
- // Polar angle θ (theta).
+ // Polar angle θ (theta).
const theta = Q.ComplexNumber.arcCosine( qubit.alpha ).multiply( 2 )
if( isNaN( theta.real )) theta.real = 0
if( isNaN( theta.imaginary )) theta.imaginary = 0
- // Azimuth angle ϕ (phi).
+ // Azimuth angle Ï• (phi).
const phi = Q.ComplexNumber.log(
@@ -2255,8 +2256,8 @@ Object.assign( Q.Qubit, {
}
- // Bloch vector’s axes are wonked.
- // Let’s “correct” them for use with Three.js, etc.
+ // Bloch vector’s axes are wonked.
+ // Let’s “correct†them for use with Three.js, etc.
const position = {
@@ -2268,10 +2269,18 @@ Object.assign( Q.Qubit, {
return {
- // Ummm... I’m only returnig the REAL portions. Please forgive me!
+ // Wow does this make tweening easier down the road.
+
+ alphaReal: qubit.alpha.real,
+ alphaImaginary: qubit.alpha.imaginary,
+ betaReal: qubit.beta.real,
+ betaImaginary: qubit.beta.imaginary,
+
+
+ // Ummm... I’m only returnig the REAL portions. Please forgive me!
theta: theta.real,
- phi: phi.real,
+ phi: phi.real,
vector, // Wonked YZX vector for maths because maths.
position// Un-wonked XYZ for use by actual 3D engines.
}
@@ -2291,9 +2300,9 @@ Q.Qubit.createConstants(
// Opposing pairs:
- // |H⟩ and |V⟩
- // |D⟩ and |A⟩
- // |R⟩ and |L⟩
+ // |H⟩ and |V⟩
+ // |D⟩ and |A⟩
+ // |R⟩ and |L⟩
'HORIZONTAL', new Q.Qubit( 1, 0, 'H', 'Horizontal' ),// ZERO.
'VERTICAL', new Q.Qubit( 0, 1, 'V', 'Vertical' ),// ONE.
@@ -2384,7 +2393,8 @@ Q.Gate = function( params ){
if( typeof this.symbol !== 'string' ) this.symbol = '?'
if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase()
-
+ const parameters = Object.assign( {}, params.parameters )
+ this.parameters = parameters
// We use symbols as unique identifiers
// among gate CONSTANTS
@@ -2401,10 +2411,12 @@ Q.Gate = function( params ){
return gate.symbol === scope.symbol
})
- if( foundConstant ){
+ //Muting this warning in order to have parameterized gates (that don't totally mess with the constants), we need
+ //to make clones of the constants...a lot if you're using a lot of parameterized gates. Warning gets annoying :/.
+ // if( foundConstant ){
- Q.warn( `Q.Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant )
- }
+ // Q.warn( `Q.Gate is creating a new instance, #${ this.index }, that uses the same symbol as a pre-existing Gate constant:`, foundConstant )
+ // }
if( typeof this.name !== 'string' ) this.name = 'Unknown'
if( typeof this.nameCss !== 'string' ) this.nameCss = 'unknown'
@@ -2553,8 +2565,13 @@ Q.Gate.createConstants(
nameCss: 'pauli-x',
matrix: new Q.Matrix(
[ 0, 1 ],
- [ 1, 0 ])
- }),
+ [ 1, 0 ]),
+ //ltnln: NOTE! can_be_controlled refers to whether or not the Braket SDK supports a controlled
+ //application of this gate; if we want Q to be able to support controlled gated regardless of whether
+ //or not Braket can, this must be changed..
+ can_be_controlled: true
+ },
+ ),
'PAULI_Y', new Q.Gate({
symbol: 'Y',
@@ -2564,8 +2581,10 @@ Q.Gate.createConstants(
nameCss: 'pauli-y',
matrix: new Q.Matrix(
[ 0, new Q.ComplexNumber( 0, -1 )],
- [ new Q.ComplexNumber( 0, 1 ), 0 ])
- }),
+ [ new Q.ComplexNumber( 0, 1 ), 0 ]),
+ can_be_controlled: true
+ },
+ ),
'PAULI_Z', new Q.Gate({
symbol: 'Z',
@@ -2575,32 +2594,35 @@ Q.Gate.createConstants(
nameCss: 'pauli-z',
matrix: new Q.Matrix(
[ 1, 0 ],
- [ 0, -1 ])
- }),
+ [ 0, -1 ]),
+ can_be_controlled: true
+ },
+ ),
'PHASE', new Q.Gate({
symbol: 'P',
- symbolAmazonBraket: 'p',// !!! Double check this !!!
+ symbolAmazonBraket: 'phaseshift',// ltnln edit: change from 'p' to 'phaseshift'
symbolSvg: '',
name: 'Phase',
nameCss: 'phase',
- phi: 1,
+ parameters: { "phi" : 1 },
updateMatrix$: function( phi ){
-
- if( Q.isUsefulNumber( phi ) === true ) this.phi = phi
+ if( Q.isUsefulNumber( phi ) === true ) this.parameters[ "phi" ] = phi
this.matrix = new Q.Matrix(
[ 1, 0 ],
- [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.phi ))])
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters["phi"] ))])
return this
},
applyToQubit: function( qubit, phi ){
- if( Q.isUsefulNumber( phi ) !== true ) phi = this.phi
+ if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ]
const matrix = new Q.Matrix(
[ 1, 0 ],
[ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi ))])
return new Q.Qubit( matrix.multiply( qubit ))
- }
+ },
+ can_be_controlled: true,
+ has_parameters: true
}),
'PI_8', new Q.Gate({
@@ -2625,22 +2647,224 @@ Q.Gate.createConstants(
// Create Bloch sphere visualizer instance.
}
}),
+ 'RX', new Q.Gate({
+
+ symbol: 'Rx',
+ symbolAmazonBraket: 'rx',
+ symbolSvg: '',
+ name: 'X Rotation',
+ nameCss: 'x-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 )])
+ return this
+ },
+ applyToQubit: function( qubit, phi ){
+ if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ],
+ [ new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 )])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ has_parameters: true
+ }),
+ 'RY', new Q.Gate({
+
+ symbol: 'Ry',
+ symbolAmazonBraket: 'ry',
+ symbolSvg: '',
+ name: 'Y Rotation',
+ nameCss: 'y-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
- // Operate on 2 qubits.
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), -Math.sin( phi / 2 ) ],
+ [ Math.sin( this.parameters[ "phi" ] / 2 ), Math.cos( this.parameters[ "phi" ] / 2 )])
+ return this
+ },
+ applyToQubit: function( qubit, phi ){
+
+ if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Math.cos( phi / 2 ), -Math.sin( phi / 2 ) ],
+ [ Math.sin( phi / 2 ), Math.cos( phi / 2 )])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ has_parameters: true
+ }),
+ 'RZ', new Q.Gate({
+
+ symbol: 'Rz',
+ symbolAmazonBraket: 'rz',
+ symbolSvg: '',
+ name: 'Z Rotation',
+ nameCss: 'z-rotation',
+ parameters: { "phi" : Math.PI / 2 },
+ updateMatrix$: function( phi ){
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 ))])
+ return this
+ },
+ applyToQubit: function( qubit, phi ){
+ if( Q.isUsefulNumber( phi ) !== true ) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -phi / 2 )), 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 ))])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ has_parameters: true
+ }),
+ 'UNITARY', new Q.Gate({
+
+ symbol: 'U',
+ symbolAmazonBraket: 'unitary',
+ symbolSvg: '',
+ name: 'Unitary',
+ nameCss: 'unitary',
+ //toAmazonBraket will have to use the following matrix as an argument for unitary()
+ parameters: { "phi" : Math.PI / 2,
+ "theta" : Math.PI / 2,
+ "lambda" : Math.PI / 2 },
+ updateMatrix$: function( phi, theta, lambda ){
+ //if all are valid, update; otherwise, update none.
+ if( (Q.isUsefulNumber( +phi ) === true) && (Q.isUsefulNumber( +theta ) === true) && (Q.isUsefulNumber( +lambda ) === true) ) {
+ this.parameters[ "phi" ] = +phi;
+ this.parameters[ "theta" ] = +theta;
+ this.parameters[ "lambda" ] = +lambda;
+ }
+ const a = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 ))
+ const b = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), -Math.sin( this.parameters[ "theta" ] / 2 ))
+ const c = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( this.parameters[ "phi" ] - this.parameters[ "lambda" ] ) / 2 )), Math.sin( this.parameters[ "theta" ] / 2 ))
+ const d = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( this.parameters[ "phi" ] + this.parameters[ "lambda" ] ) / 2 )), Math.cos( this.parameters[ "theta" ] / 2 ))
+ this.matrix = new Q.Matrix(
+ [ a, b ],
+ [ c, d ])
+ return this
+ },
+ applyToQubit: function( qubit, phi, theta, lambda ){
+ if( Q.isUsefulNumber( phi ) === true ) phi = this.parameters[ "phi" ]
+ if( Q.isUsefulNumber( theta ) === true ) theta = this.parameters[ "theta" ]
+ if( Q.isUsefulNumber( lambda ) === true ) lambda = this.parameters[ "lambda" ]
+ const a = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( phi + lambda ) / 2 )), Math.cos( theta / 2 ));
+ const b = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -( phi - lambda ) / 2 )), -Math.sin( theta / 2 ));
+ const c = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( phi - lambda ) / 2 )), Math.sin( theta / 2 ));
+ const d = Q.ComplexNumber.multiply(
+ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, ( phi + lambda ) / 2 )), Math.cos( theta / 2 ));
+ const matrix = new Q.Matrix(
+ [ a, b ],
+ [ c, d ])
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ has_parameters: true
+ }),
+ 'NOT1_2', new Q.Gate({
+
+ symbol: 'V',
+ symbolAmazonBraket: 'v',
+ symbolSvg: '',
+ name: '√Not',
+ nameCss: 'not1-2',
+ matrix: new Q.Matrix(
+ [ new Q.ComplexNumber( 1, 1 ) / 2, new Q.ComplexNumber( 1, -1 ) / 2 ],
+ [ new Q.ComplexNumber( 1, -1 ) / 2, new Q.ComplexNumber( 1, 1 ) / 2 ])
+ }),
+ 'PI_8_Dagger', new Q.Gate({
+
+ symbol: 'T†',
+ symbolAmazonBraket: 'ti',
+ symbolSvg: '',
+ name: 'PI_8_Dagger',
+ nameCss: 'pi8-dagger',
+ matrix: new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -Math.PI / 4 )) ])
+ }),
+ 'NOT1_2_Dagger', new Q.Gate({
+
+ symbol: 'V†',
+ symbolAmazonBraket: 'vi',
+ symbolSvg: '',
+ name: '√Not_Dagger',
+ nameCss: 'not1-2-dagger',
+ matrix: new Q.Matrix(
+ [ new Q.ComplexNumber( 1, -1 ) / 2, new Q.ComplexNumber( 1, 1 ) / 2 ],
+ [ new Q.ComplexNumber( 1, 1 ) / 2, new Q.ComplexNumber( 1, -1 ) / 2 ])
+ }),
+ //Note that S, S_Dagger, PI_8, and PI_8_Dagger can all be implemented by applying the PHASE gate
+ //using certain values of phi.
+ //These gates are included for completeness.
+ 'S', new Q.Gate({
+ symbol: 'S*', //Gotta think of a better symbol name...
+ symbolAmazonBraket: 's',
+ symbolSvg: '',
+ name: 'π ÷ 4',
+ nameCss: 'pi4',
+ matrix: new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, new Q.ComplexNumber( 0, 1 ) ])
+ }),
+ 'S_Dagger', new Q.Gate({
+
+ symbol: 'S†',
+ symbolAmazonBraket: 'si',
+ symbolSvg: '',
+ name: 'π ÷ 4 Dagger',
+ nameCss: 'pi4-dagger',
+ matrix: new Q.Matrix(
+ [ 1, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -1 )) ])
+ }),
+ // Operate on 2 qubits.
'SWAP', new Q.Gate({
- symbol: 'S',
- symbolAmazonBraket: 's',// !!! Double check this !!!
+ symbol: 'S',
+ symbolAmazonBraket: 'swap',
symbolSvg: '',
name: 'Swap',
nameCss: 'swap',
- matrix: new Q.Matrix(
- [ 1, 0, 0, 0 ],
- [ 0, 0, 1, 0 ],
- [ 0, 1, 0, 0 ],
- [ 0, 0, 0, 1 ])
+ parameters: { "phi" : 0.0 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ],
+ [ 0, Q.ComplexNumber.E.power(new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, 1 ), 0, 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ can_be_controlled: true,
+ has_parameters: true,
+ is_multi_qubit: true
}),
'SWAP1_2', new Q.Gate({
@@ -2653,8 +2877,258 @@ Q.Gate.createConstants(
[ 1, 0, 0, 0 ],
[ 0, new Q.ComplexNumber( 0.5, 0.5 ), new Q.ComplexNumber( 0.5, -0.5 ), 0 ],
[ 0, new Q.ComplexNumber( 0.5, -0.5 ), new Q.ComplexNumber( 0.5, 0.5 ), 0 ],
- [ 0, 0, 0, 1 ])
- })
+ [ 0, 0, 0, 1 ]),
+ is_multi_qubit: true
+ }),
+ 'ISWAP', new Q.Gate({
+
+ symbol: 'iS',
+ symbolAmazonBraket: 'iswap',
+ symbolSvg: '',
+ name: 'Imaginary Swap',
+ nameCss: 'iswap',
+ matrix: new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 0, new Q.ComplexNumber( 0, 1 ), 0 ],
+ [ 0, new Q.ComplexNumber( 0, 1 ), 0, 0 ],
+ [ 0, 0, 0, 1 ]),
+ is_multi_qubit: true
+ }),
+ 'ISING-XX', new Q.Gate({
+
+ symbol: 'XX',
+ symbolAmazonBraket: 'xx',
+ symbolSvg: '',
+ name: 'Ising XX Coupling',
+ nameCss: 'ising-xx-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Math.cos( phi / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ],
+ [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ],
+ [ new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-XY', new Q.Gate({
+
+ symbol: 'XY',
+ symbolAmazonBraket: 'xy',
+ symbolSvg: '',
+ name: 'Ising XY Coupling',
+ nameCss: 'ising-xy-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, Math.sin( phi / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-YY', new Q.Gate({
+
+ symbol: 'YY',
+ symbolAmazonBraket: 'yy',
+ symbolSvg: '',
+ name: 'Ising YY Coupling',
+ nameCss: 'ising-yy-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Math.cos( this.parameters[ "phi" ] / 2 ), 0, 0, new Q.ComplexNumber( 0, Math.sin( this.parameters[ "phi" ] / 2 )) ],
+ [ 0, Math.cos( this.parameters[ "phi" ] / 2 ), new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), Math.cos( this.parameters[ "phi" ] / 2 ), 0 ],
+ [ new Q.ComplexNumber( 0, -Math.sin( this.parameters[ "phi" ] / 2 )), 0, 0, Math.cos( this.parameters[ "phi" ] / 2 ) ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Math.cos( phi / 2 ), 0, 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )) ],
+ [ 0, Math.cos( phi / 2 ), new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), 0 ],
+ [ 0, new Q.ComplexNumber( 0, -Math.sin( phi / 2 )), Math.cos( phi / 2 ), 0 ],
+ [ new Q.ComplexNumber( 0, Math.sin( phi / 2 )), 0, 0, Math.cos( phi / 2 ) ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'ISING-ZZ', new Q.Gate({
+
+ symbol: 'ZZ',
+ symbolAmazonBraket: 'zz',
+ symbolSvg: '',
+ name: 'Ising ZZ Coupling',
+ nameCss: 'ising-zz-coupling',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] / 2 )), 0],
+ [ 0, 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -this.parameters[ "phi" ] / 2 )) ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0, 0, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi / 2 )), 0],
+ [ 0, 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, -phi / 2 )) ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase00', new Q.Gate({
+
+ symbol: '00', //placeholder
+ symbolAmazonBraket: 'cphaseshift00',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 00',
+ nameCss: 'cphase00',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi )), 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase01', new Q.Gate({
+
+ symbol: '01', //placeholder
+ symbolAmazonBraket: 'cphaseshift01',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 01',
+ nameCss: 'cphase01',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0, 0 ],
+ [ 0, 0, 1, 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CPhase10', new Q.Gate({
+
+ symbol: '10', //placeholder
+ symbolAmazonBraket: 'cphaseshift10',
+ symbolSvg: '',
+ name: 'Controlled Phase Shift 10',
+ nameCss: 'cphase01',
+ parameters: { "phi" : 1 },
+ updateMatrix$: function( phi ) {
+
+ if( Q.isUsefulNumber( +phi ) === true ) this.parameters[ "phi" ] = +phi
+ this.matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, this.parameters[ "phi" ] )), 0 ],
+ [ 0, 0, 0, 1 ])
+ return this
+ },
+ applyToQubit: function( qubit, phi ) {
+ if( Q.isUsefulNumber( phi ) !== true) phi = this.parameters[ "phi" ]
+ const matrix = new Q.Matrix(
+ [ 1, 0, 0, 0 ],
+ [ 0, 1, 0, 0 ],
+ [ 0, 0, Q.ComplexNumber.E.power( new Q.ComplexNumber( 0, phi)), 0 ],
+ [ 0, 0, 0, 1 ]
+ )
+ return new Q.Qubit( matrix.multiply( qubit ))
+ },
+ is_multi_qubit: true,
+ has_parameters: true
+ }),
+ 'CSWAP', new Q.Gate({
+
+ symbol: 'CSWAP',
+ symbolAmazonBraket: 'cswap',
+ symbolSvg: '',
+ name: 'Controlled Swap',
+ nameCss: 'controlled-swap',
+ matrix: new Q.Matrix(
+ [1, 0, 0, 0, 0, 0, 0, 0],
+ [0, 1, 0, 0, 0, 0, 0, 0],
+ [0, 0, 1, 0, 0, 0, 0, 0],
+ [0, 0, 0, 1, 0, 0, 0, 0],
+ [0, 0, 0, 0, 1, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 0, 1, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 1]
+ )
+ })
/*
@@ -2667,12 +3141,14 @@ Q.Gate.createConstants(
*/
-)
+)
+
-// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
@@ -2738,7 +3214,7 @@ Object.assign( Q.History.prototype, {
// Are we recording this history?
// Usually, yes.
- // But if our history state is “playback”
+ // But if our history state is “playbackâ€
// then we will NOT record this.
if( this.isRecording ){
@@ -2815,7 +3291,7 @@ Object.assign( Q.History.prototype, {
if( direction < 0 ) this.index --
- // It’s now safe to turn recording back on.
+ // It’s now safe to turn recording back on.
this.isRecording = true
@@ -2838,18 +3314,18 @@ Object.assign( Q.History.prototype, {
}
return this.entries.reduce( function( output, entry, i ){
- output += '\n\n'+ i + ' ════════════════════════════════════════'+
+ output += '\n\n'+ i + ' â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•'+
entry.reduce( function( output, entry, i ){
- output += '\n\n '+ i +' ────────────────────────────────────────\n'
+ output += '\n\n '+ i +' ────────────────────────────────────────\n'
if( entry.redo ){
- output += '\n ⟳ Redo ── '+ entry.redo.name +' '
+ output += '\n ⟳ Redo ── '+ entry.redo.name +' '
if( entry.redo.args ) output += entry.redo.args.reduce( argsParse, '' )
}
output += entry.undo.reduce( function( output, entry, i ){
- output += '\n ⟲ Undo '+ i +' ── '+ entry.name +' '
+ output += '\n ⟲ Undo '+ i +' ── '+ entry.name +' '
if( entry.args ) output += entry.args.reduce( argsParse, '' )
return output
@@ -2944,7 +3420,6 @@ Object.assign( Q.Circuit, {
if( text === undefined ) return new Q.Circuit()
-
// Is this a String Template -- as opposed to a regular String?
// If so, let’s convert it to a regular String.
// Yes, this maintains the line breaks.
@@ -3064,7 +3539,6 @@ Object.assign( Q.Circuit, {
fromTableTransposed: function( table ){
-
const
bandwidth = table.length,
timewidth = table.reduce( function( max, moments ){
@@ -3084,7 +3558,6 @@ Object.assign( Q.Circuit, {
const
momentIndex = m + 1,
operation = table[ r ][ m ]
-
let siblingHasBeenFound = false
for( let s = 0; s < r; s ++ ){
@@ -3097,7 +3570,6 @@ Object.assign( Q.Circuit, {
// We’ve found a sibling !
-
const operationsIndex = circuit.operations.findIndex( function( operation ){
return (
@@ -3245,7 +3717,6 @@ Object.assign( Q.Circuit, {
if( bitsEqual ){
// console.log( 'bits ARE equal' )
-
let
istar = 0,
jstar = 0,
@@ -3257,8 +3728,6 @@ Object.assign( Q.Circuit, {
istar |= (( i & ( 1 << q )) >> q ) << k
jstar |= (( j & ( 1 << q )) >> q ) << k
}
-
-
//console.log( 'U.read( istar, jstar )', U.read( istar, jstar ).toText() )
// console.log( 'before write$', result.toTsv())
@@ -3373,11 +3842,15 @@ Object.assign( Q.Circuit, {
// Yikes. May need to separate registerIndices in to controls[] and targets[] ??
// Works for now tho.....
-
- for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){
-
- U = Q.Circuit.controlled( U )
- // console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() )
+ // Houston we have a problem. Turns out, not every gate with registerIndices.length > 1 is
+ // controlled.
+ // This is a nasty fix, leads to a lot of edge cases. (For instance: hard-coding cswaps...) But just experimenting.
+ if(!operation.gate.is_multi_qubit || (operation.gate.symbol == 'S' && operation.registerIndices.length > 2) && operation.gate.can_be_controlled) {
+ for( let j = 0; j < operation.registerIndices.length - 1; j ++ ){
+
+ U = Q.Circuit.controlled( U )
+ //console.log( 'qubitIndex #', j, 'U = Q.Circuit.controlled( U )', U.toTsv() )
+ }
}
@@ -3387,8 +3860,6 @@ Object.assign( Q.Circuit, {
// and wow -- tracking down that bug was painful!
const registerIndices = operation.registerIndices.slice()
-
-
state = Q.Circuit.expandMatrix(
circuit.bandwidth,
@@ -3399,7 +3870,6 @@ Object.assign( Q.Circuit, {
-
operationsCompleted ++
const progress = operationsCompleted / operationsTotal
@@ -3465,7 +3935,6 @@ Object.assign( Q.Circuit, {
-
return matrix
}
@@ -3982,46 +4451,102 @@ https://cirq.readthedocs.io/en/stable/tutorial.html
return headers
},
toAmazonBraket: function(){
-
+ let is_valid_braket_circuit = true
const header = `import boto3
-from braket.aws import AwsQuantumSimulator, AwsQuantumSimulatorArns
+from braket.aws import AwsDevice
from braket.circuits import Circuit
-aws_account_id = boto3.client("sts").get_caller_identity()["Account"]
-device = AwsQuantumSimulator(AwsQuantumSimulatorArns.QS1)
-s3_folder = (f"braket-output-{aws_account_id}", "folder-name")
-
-`
+my_bucket = f"amazon-braket-Your-Bucket-Name" # the name of the bucket
+my_prefix = "Your-Folder-Name" # the name of the folder in the bucket
+s3_folder = (my_bucket, my_prefix)\n
+device = LocalSimulator()\n\n`
+//TODO (ltnln): Syntax is different for simulators and actual quantum computers. Should there be a default? Should there be a way to change?
+//vs an actual quantum computer? May not be necessary.
+ let variables = ''
+ let num_unitaries = 0
//`qjs_circuit = Circuit().h(0).cnot(0,1)`
+ //ltnln change: from gate.AmazonBraketName -> gate.symbolAmazonBraket
let circuit = this.operations.reduce( function( string, operation ){
-
- let awsGate = operation.gate.AmazonBraketName !== undefined ?
- operation.gate.AmazonBraketName :
+ let awsGate = operation.gate.symbolAmazonBraket !== undefined ?
+ operation.gate.symbolAmazonBraket :
operation.gate.symbol.substr( 0, 1 ).toLowerCase()
+ if( operation.gate.symbolAmazonBraket === undefined ) is_valid_braket_circuit = false
+ if( operation.gate.symbol === 'X' ) {
+ if( operation.registerIndices.length === 1 ) awsGate = operation.gate.symbolAmazonBraket
+ else if( operation.registerIndices.length === 2 ) awsGate = 'cnot'
+ else if( operation.registerIndices.length === 3) awsGate = 'ccnot'
+ else is_valid_braket_circuit = false
+ }
+
+ else if( operation.gate.symbol === 'S' ) {
+ if( operation.gate.parameters["phi"] === 0 ) {
+ awsGate = operation.registerIndices.length == 2 ? awsGate : "cswap"
+ return string +'.'+ awsGate +'(' +
+ operation.registerIndices.reduce( function( string, registerIndex, r ){
- if( operation.gate.symbol === 'X' &&
- operation.registerIndices.length > 1 ){
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
- awsGate = 'cnot'
+ }, '' ) + ')'
+ }
+ awsGate = 'pswap'
}
- if( operation.gate.symbol === '*' ){
+ //ltnln note: removed the if( operation.gate.symbol == '*') branch as it should be covered by
+ //the inclusion of the CURSOR gate.
+ else if( operation.gate.symbol === 'Y' || operation.gate.symbol === 'Z' || operation.gate.symbol === 'P' ) {
+ if( operation.registerIndices.length === 1) awsGate = operation.gate.symbolAmazonBraket
+ else if( operation.registerIndices.length === 2 ) awsGate = (operation.gate.symbol === 'Y') ? 'cy' : (operation.gate.symbol === 'Z') ? 'cz' : 'cphaseshift'
+ else is_valid_braket_circuit = false
+ }
+ //for all unitary gates, there must be a line of code to initialize the matrix for use
+ //in Braket's .u(matrix=my_unitary, targets[0]) function
+ else if( operation.gate.symbol === 'U') {
+ //check that this truly works as a unique id
+ is_valid_braket_circuit &= operation.registerIndices.length === 1
+ const new_matrix = `unitary_` + num_unitaries
+ num_unitaries++
+ const a = Q.ComplexNumber.toText(Math.cos(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2),
+ Math.sin(-(operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2))
+ .replace('i', 'j')
+ const b = Q.ComplexNumber.toText(-Math.cos(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2),
+ -Math.sin(-(operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ const c = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2) / 2),
+ -Math.sin((operation.gate.parameters[ "phi" ] - operation.gate.parameters[ "lambda" ])*Math.sin(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ const d = Q.ComplexNumber.toText(Math.cos((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2) / 2),
+ Math.sin((operation.gate.parameters[ "phi" ] + operation.gate.parameters[ "lambda" ])*Math.cos(operation.gate.parameters[ "theta" ] / 2)) / 2)
+ .replace('i', 'j')
+ variables += new_matrix + ` = np.array(` +
+ `[[` + a + ', ' + b + `],`+
+ `[` + c + ', ' + d + `]])\n`
+ return string +'.'+ awsGate +'(' + new_matrix +','+
+ operation.registerIndices.reduce( function( string, registerIndex, r ){
- awsGate = 'i'
+ return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
+
+ }, '' ) + ')'
}
-
+ // I believe this line should ensure that we don't include any controlled single-qubit gates that aren't allowed in Braket.
+ // The registerIndices.length > 1 technically shouldn't be necessary, but if changes are made later, it's just for safety.
+ else is_valid_braket_circuit &= (operation.registerIndices.length === 1) || ( operation.registerIndices.length > 1 && operation.gate.is_multi_qubit )
return string +'.'+ awsGate +'(' +
operation.registerIndices.reduce( function( string, registerIndex, r ){
return string + (( r > 0 ) ? ',' : '' ) + ( registerIndex - 1 )
- }, '' ) + ')'
+ }, '' ) + ((operation.gate.has_parameters) ?
+ Object.values( operation.gate.parameters ).reduce( function( string, parameter ) {
+ return string + "," + parameter
+ }, '')
+ : '') + ')'
}, 'qjs_circuit = Circuit()' )
+ variables += '\n'
if( this.operations.length === 0 ) circuit += '.i(0)'// Quick fix to avoid an error here!
const footer = `\n\ntask = device.run(qjs_circuit, s3_folder, shots=100)
print(task.result().measurement_counts)`
- return header + circuit + footer
+ return is_valid_braket_circuit ? header + variables + circuit + footer : `###This circuit is not representable as a Braket circuit!###`
},
toLatex: function(){
@@ -4228,15 +4753,13 @@ print(task.result().measurement_counts)`
},
- set$: function( gate, momentIndex, registerIndices ){
+ set$: function( gate, momentIndex, registerIndices, parameters = {} ){
const circuit = this
-
-
// Is this a valid gate?
-
- if( typeof gate === 'string' ) gate = Q.Gate.findBySymbol( gate )
- if( gate instanceof Q.Gate !== true ) return Q.error( `Q.Circuit attempted to add a gate to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate )
+ // We clone the gate rather than using the constant; this way, if we change it's parameters, we don't change the constant.
+ if( typeof gate === 'string' ) gate = Q.Gate.prototype.clone( Q.Gate.findBySymbol( gate ) )
+ if( gate instanceof Q.Gate !== true ) return Q.error( `Q.Circuit attempted to add a gate (${ gate }) to circuit #${ this.index } at moment #${ momentIndex } that is not a gate:`, gate )
// Is this a valid moment index?
@@ -4307,7 +4830,11 @@ print(task.result().measurement_counts)`
// Aren’t you glad we handle all this for you?
const
- isControlled = registerIndices.length > 1 && gate !== Q.Gate.SWAP,
+ //TODO: For ltnln (have to fix)
+ // a) allow users to control whatever they want! Just because it's not allowed in Braket
+ // doesn't mean they shouldn't be allowed to do it in Q! (Probably fixable by adjusting toAmazonBraket)
+ // b) Controlling a multi_qubit gate will not treat the control icon like a control gate!
+ isControlled = registerIndices.length > 1 && gate !== Q.Gate.SWAP && gate.can_be_controlled !== undefined
operation = {
gate,
@@ -4315,6 +4842,9 @@ print(task.result().measurement_counts)`
registerIndices,
isControlled
}
+ //perform parameter update here!!!
+ Object.keys(parameters).forEach( element => { parameters[element] = +parameters[element] })
+ if(gate.has_parameters) gate.updateMatrix$.apply( gate, Object.values(parameters) )
this.operations.push( operation )
@@ -4327,14 +4857,15 @@ print(task.result().measurement_counts)`
// Let’s make history.
-
+ const redo_args = Array.from( arguments )
+ Object.assign( redo_args[ redo_args.length - 1 ], parameters )
this.history.record$({
redo: {
name: 'set$',
func: circuit.set$,
- args: Array.from( arguments )
+ args: redo_args
},
undo: [{
@@ -4903,6 +5434,40 @@ Q.Circuit.createConstants(
+
+
+
+
+
+
+
+
+
+
+
+/*
+
+
+%%HTML
+
+
+
+
+
+%%javascript
+Q.braket( element )
+
+
+
+
+*/
+
+
+
+//%%javascript
+
+
+
// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
@@ -5088,7 +5653,6 @@ Q.Circuit.Editor = function( circuit, targetEl ){
boardContainerEl.classList.add( 'Q-circuit-board-container' )
//boardContainerEl.addEventListener( 'touchstart', Q.Circuit.Editor.onPointerPress )
boardContainerEl.addEventListener( 'mouseleave', function(){
-
Q.Circuit.Editor.unhighlightAll( circuitEl )
})
@@ -5100,7 +5664,9 @@ Q.Circuit.Editor = function( circuit, targetEl ){
boardEl.appendChild( backgroundEl )
backgroundEl.classList.add( 'Q-circuit-board-background' )
-
+ const parameterEl = createDiv()
+ boardEl.appendChild( parameterEl )
+ parameterEl.classList.add( 'Q-parameters-box' )
// Create background highlight bars
// for each row.
@@ -5170,7 +5736,7 @@ Q.Circuit.Editor = function( circuit, targetEl ){
}
- // Add “Add register” button.
+ // Add “Add register” button.q
const addRegisterEl = createDiv()
foregroundEl.appendChild( addRegisterEl )
@@ -5227,7 +5793,6 @@ Q.Circuit.Editor = function( circuit, targetEl ){
// Add operations.
circuit.operations.forEach( function( operation ){
-
Q.Circuit.Editor.set( circuitEl, operation )
})
@@ -5349,11 +5914,11 @@ Object.assign( Q.Circuit.Editor, {
const r = min + Math.random() * ( max - min )
return Math.floor( Math.random() * 2 ) ? r : -r
}
-
+
+ //ltnln: added missing Braket operations.
paletteEl.classList.add( 'Q-circuit-palette' )
-
- 'HXYZPT*'
- .split( '' )
+ 'H,X,Y,Z,P,Rx,Ry,Rz,U,V,V†,S*,S†,T,T†,00,01,10,√S,iS,XX,XY,YY,ZZ,*'
+ .split( ',' )
.forEach( function( symbol ){
const gate = Q.Gate.findBySymbol( symbol )
@@ -5446,7 +6011,6 @@ Q.Circuit.Editor.prototype.onExternalSet = function( event ){
}
}
Q.Circuit.Editor.set = function( circuitEl, operation ){
-
const
backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ),
foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
@@ -5454,7 +6018,6 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){
operationIndex = circuitEl.circuit.operations.indexOf( operation )
operation.registerIndices.forEach( function( registerIndex, i ){
-
const operationEl = document.createElement( 'div' )
foregroundEl.appendChild( operationEl )
operationEl.classList.add( 'Q-circuit-operation', 'Q-circuit-operation-'+ operation.gate.nameCss )
@@ -5468,7 +6031,9 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){
operationEl.setAttribute( 'title', operation.gate.name )
operationEl.style.gridColumnStart = Q.Circuit.Editor.momentIndexToGridColumn( operation.momentIndex )
operationEl.style.gridRowStart = Q.Circuit.Editor.registerIndexToGridRow( registerIndex )
-
+ if( operation.gate.has_parameters ) Object.keys(operation.gate.parameters).forEach( element => {
+ operationEl.setAttribute( element, operation.gate.parameters[element] ) //adds a parameter attribute to the operation!
+ })
const tileEl = document.createElement( 'div' )
operationEl.appendChild( tileEl )
tileEl.classList.add( 'Q-circuit-operation-tile' )
@@ -5517,7 +6082,6 @@ Q.Circuit.Editor.set = function( circuitEl, operation ){
}
})
if( operation.isControlled && i === 0 ){
-
operationEl.classList.add( 'Q-circuit-operation-control' )
operationEl.setAttribute( 'title', 'Control' )
tileEl.innerText = ''
@@ -5622,12 +6186,10 @@ Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
if( allSiblingsPresent !== true ) return false
-
// Note the different gate types present
// among the selected operations.
const gates = selectedOperations.reduce( function( gates, operationEl ){
-
const gateSymbol = operationEl.getAttribute( 'gate-symbol' )
if( !Q.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1
else gates[ gateSymbol ] ++
@@ -5657,7 +6219,6 @@ Q.Circuit.Editor.isValidControlCandidate = function( circuitEl ){
totalNotControlled: 0
})
-
// This could be ONE “identity cursor”
// and one or more of a regular single gate
// that is NOT already controlled.
@@ -5741,7 +6302,6 @@ Q.Circuit.Editor.createControl = function( circuitEl ){
)
})
circuit.set$(
-
targets[ 0 ].getAttribute( 'gate-symbol' ),
+control.getAttribute( 'moment-index' ),
[ +control.getAttribute( 'register-index' )].concat(
@@ -5776,7 +6336,6 @@ Q.Circuit.Editor.isValidSwapCandidate = function( circuitEl ){
// We can only swap between two registers.
// No crazy rotation-swap bullshit. (Yet.)
-
if( selectedOperations.length !== 2 ) return false
@@ -5974,7 +6533,6 @@ Q.Circuit.Editor.onPointerMove = function( event ){
// Let’s prioritize any element that is “sticky”
// which means it can appear OVER another grid cell.
-
const
cellEl = foundEls.find( function( el ){
@@ -6074,9 +6632,6 @@ Q.Circuit.Editor.onPointerMove = function( event ){
-
-
-
///////////////////////
// //
// Pointer PRESS //
@@ -6085,25 +6640,21 @@ Q.Circuit.Editor.onPointerMove = function( event ){
Q.Circuit.Editor.onPointerPress = function( event ){
-
-
// This is just a safety net
// in case something terrible has ocurred.
// (ex. Did the user click and then their mouse ran
// outside the window but browser didn’t catch it?)
-
+ console.log("event target: ", event.target);
if( Q.Circuit.Editor.dragEl !== null ){
Q.Circuit.Editor.onPointerRelease( event )
return
}
-
-
const
targetEl = event.target,
circuitEl = targetEl.closest( '.Q-circuit' ),
paletteEl = targetEl.closest( '.Q-circuit-palette' )
-
+ parameterEl = targetEl.closest( '.Q-parameters-box' )
// If we can’t find a circuit that’s a really bad sign
// considering this event should be fired when a circuit
@@ -6111,7 +6662,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){
if( !circuitEl && !paletteEl ) return
-
// This is a bit of a gamble.
// There’s a possibility we’re not going to drag anything,
// but we’ll prep these variables here anyway
@@ -6126,9 +6676,8 @@ Q.Circuit.Editor.onPointerPress = function( event ){
// Are we dealing with a circuit interface?
// ie. NOT a palette interface.
- if( circuitEl ){
+ if( circuitEl && !parameterEl ){
-
// Shall we toggle the circuit lock?
const
@@ -6233,7 +6782,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){
if( !cellEl ) return
-
// Once we know what cell we’ve pressed on
// we can get the momentIndex and registerIndex
// from its pre-defined attributes.
@@ -6262,7 +6810,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){
inputEl = targetEl.closest( '.Q-circuit-input' ),
operationEl = targetEl.closest( '.Q-circuit-operation' )
-
// +++++++++++++++
// We’ll have to add some input editing capability later...
// Of course you can already do this in code!
@@ -6296,7 +6843,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){
if( operationsSelectedLength === operations.length ){
operations.forEach( function( el ){
-
el.classList.remove( 'Q-circuit-cell-selected' )
})
}
@@ -6341,14 +6887,21 @@ Q.Circuit.Editor.onPointerPress = function( event ){
// then GO HOME.
if( !operationEl ) return
-
+ // If we've doubleclicked on an operation and the operation has parameters, we should be able
+ // to edit those parameters regardless of whether or not the circuit is locked.
+ if( event.detail == 2) {
+ const operation = Q.Gate.findBySymbol(operationEl.getAttribute( 'gate-symbol' ))
+ if( operation.has_parameters ) {
+ Q.Circuit.Editor.onDoubleclick( event, operationEl )
+ return
+ }
+ }
// Ok now we know we are dealing with an operation.
// This preserved selection state information
// will be useful for when onPointerRelease is fired.
if( operationEl.classList.contains( 'Q-circuit-cell-selected' )){
-
operationEl.wasSelected = true
}
else operationEl.wasSelected = false
@@ -6454,7 +7007,6 @@ Q.Circuit.Editor.onPointerPress = function( event ){
dragEl.registerIndex = registerIndex
}
else if( paletteEl ){
-
const operationEl = targetEl.closest( '.Q-circuit-operation' )
if( !operationEl ) return
@@ -6468,6 +7020,29 @@ Q.Circuit.Editor.onPointerPress = function( event ){
dragEl.offsetX = bounds.left - x
dragEl.offsetY = bounds.top - y
}
+ else if( parameterEl ){
+ const exitEl = targetEl.closest( '.Q-parameter-box-exit' )
+ if( !exitEl ) return
+ parameterEl.style.display = 'none'
+ const foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
+ operationEl = foregroundEl.querySelector( `[moment-index="${ parameterEl.getAttribute( 'operation-moment-index' )}"]` +
+ `[register-index="${ parameterEl.getAttribute( 'operation-register-index' )}"]` )
+ parameters = {}
+ operationSkeleton = Q.Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' ))
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = operationEl.getAttribute( key ) ? operationEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ //on exiting the parameter-input-box, we should update the circuit!!
+ circuitEl.circuit.set$(
+ operationEl.getAttribute( 'gate-symbol' ),
+ +operationEl.getAttribute( 'moment-index' ),
+ operationEl.getAttribute( 'register-indices' ) ? operationEl.getAttribute( 'register-indices' ).split(',').map( i => +i ) :
+ [ +operationEl.getAttribute( 'register-index' )],
+ parameters
+ )
+ parameterEl.innerHTML = ""
+ return
+ }
dragEl.timestamp = Date.now()
@@ -6496,10 +7071,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// If there’s no dragEl then bail immediately.
-
if( Q.Circuit.Editor.dragEl === null ) return
-
-
// Looks like we’re moving forward with this plan,
// so we’ll take control of the input now.
@@ -6514,13 +7086,18 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// under the mouse / finger, skipping element [0]
// because that will be the clipboard.
- const
- { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event ),
- boardContainerEl = document.elementsFromPoint( x, y )
- .find( function( el ){
-
- return el.classList.contains( 'Q-circuit-board-container' )
- }),
+ // doing this because elementsFromPoint() doesnt work well with JSDOM for testing purposes
+ const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
+ const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container")
+ if( boardContainerAll.length === 0 ) return
+ let boardContainerEl = Array.from(boardContainerAll).find((element) => {
+ let rect = element.getBoundingClientRect()
+ let clientX = rect.left
+ let clientY = rect.top
+ let height = element.offsetHeight
+ let width = element.offsetWidth
+ return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height )
+ })
returnToOrigin = function(){
@@ -6665,7 +7242,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
droppedAtRegisterIndex < 1 ||
droppedAtRegisterIndex > circuit.bandwidth
){
-
returnToOrigin()
return
}
@@ -6716,7 +7292,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
const registerIndicesString = childEl.getAttribute( 'register-indices' )
if( registerIndicesString ){
-
// What are ALL of the registerIndices
// associated with this multi-register operation?
// (We may use them later as a checklist.)
@@ -6800,6 +7375,13 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
if( registerIndices.length === foundComponents.length ){
+ const operationSkeleton = Q.Gate.findBySymbol( gatesymbol )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
//circuit.set$(
setCommands.push([
@@ -6820,7 +7402,8 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
registerIndexTarget += childEl.origin.registerIndex - Q.Circuit.Editor.dragEl.registerIndex + siblingDelta
}
return registerIndexTarget
- })
+ }),
+ parameters
// )
])
}
@@ -6841,7 +7424,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// can sit at each register index.
// This copies registerIndices,
// but inverts the key : property relationship.
-
const registerMap = registerIndices
.reduce( function( registerMap, registerIndex, r ){
@@ -6869,7 +7451,6 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// Now we can seat it at its new position.
// Note: This may OVERWRITE one of its siblings!
// And that’s ok.
-
foundComponents.forEach( function( component ){
const
@@ -6879,7 +7460,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// Now put it where it wants to go,
// possibly overwriting a sibling component!
-
+ //ltnln: if a multiqubit operation component that requires a sibling, overwrites its sibling, both/all components should be destroyed
registerMap[
componentRegisterIndex + draggedRegisterDelta
@@ -6904,34 +7485,47 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// ie. if a dragged sibling overwrote a seated one.
.filter( function( entry ){
-
return Q.isUsefulInteger( entry )
})
-
+ const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
// Finally, we’re ready to set.
-
// circuit.set$(
setCommands.push([
-
+ //ltnln: if a component of an operation that requires a sibling pair overwrites its sibling, we want it removed entirely.
+ fixedRegistersIndices.length < 2 && Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ Q.Gate.NOOP :
childEl.getAttribute( 'gate-symbol' ),
momentIndexTarget,
- fixedRegistersIndices
+ fixedRegistersIndices,
+ parameters
// )
])
}
else {
-
remainingComponents.forEach( function( componentEl, i ){
-
//circuit.set$(
+ const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
setCommands.push([
- +componentEl.getAttribute( 'register-indices-index' ) ?
+ +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
gatesymbol :
Q.Gate.NOOP,
+componentEl.getAttribute( 'moment-index' ),
- +componentEl.getAttribute( 'register-index' )
+ +componentEl.getAttribute( 'register-index' ),
+ parameters
// )
])
})
@@ -6941,15 +7535,22 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// all the components that were part of the drag.
foundComponents.forEach( function( componentEl ){
-
- // circuit.set$(
+ const operationSkeleton = Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
setCommands.push([
-
- +componentEl.getAttribute( 'register-indices-index' ) ?
- gatesymbol :
+ //ltnln: temporary fix: certain multiqubit operations should only be represented in pairs of registers. If one is removed (i.e. a single component of the pair)
+ //then the entire operation should be removed.
+ +componentEl.getAttribute( 'register-indices-index' ) && !Q.Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ componentEl.getAttribute( 'gate-symbol' ) :
Q.Gate.NOOP,
+componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta,
+componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta,
+ parameters
// )
])
})
@@ -6983,7 +7584,7 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
else {
-
+
// First, if this operation comes from a circuit
// (and not a circuit palette)
// make sure the old positions are cleared away.
@@ -7005,11 +7606,24 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
// in its new home.
// circuit.set$(
+ let registerIndices = [ registerIndexTarget ]
+ //ltnln: By default, multiqubit gates appear in pairs on the circuit rather than
+ // requiring the user to have to pair them like with Swap/CNot.
+ const operationSkeleton = Q.Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ))
+ if(operationSkeleton.is_multi_qubit ) {
+ registerIndices.push( registerIndexTarget == circuit.bandwidth ? registerIndexTarget - 1 : registerIndexTarget + 1)
+ }
+ let parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
setCommands.push([
-
childEl.getAttribute( 'gate-symbol' ),
momentIndexTarget,
- [ registerIndexTarget ]
+ registerIndices,
+ parameters
// )
])
}
@@ -7053,8 +7667,83 @@ Q.Circuit.Editor.onPointerRelease = function( event ){
}
+ /////////////////////////
+ // //
+ // Pointer DOUBLECLICK //
+ // //
+/////////////////////////
+//ltnln: my trying out an idea for parameterized gates...
+Q.Circuit.Editor.onDoubleclick = function( event, operationEl ) {
+ // assumption for the following 3 lines is that we've already decided that we are on-top of a valid gate operation in
+ // the circuit
+ const operation = Q.Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' ))
+ const { x, y } = Q.Circuit.Editor.getInteractionCoordinates( event )
+
+ const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container")
+ if( boardContainerAll.length === 0 ) return
+ let boardContainerEl = Array.from(boardContainerAll).find((element) => {
+ let rect = element.getBoundingClientRect()
+ let clientX = rect.left
+ let clientY = rect.top
+ let height = element.offsetHeight
+ let width = element.offsetWidth
+ return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height )
+ })
+ if( !boardContainerEl ) return;
+ const parameterEl = boardContainerEl.querySelector('.Q-parameters-box')
+ const exit = document.createElement( 'button' )
+ parameterEl.appendChild( exit )
+ exit.classList.add( 'Q-parameter-box-exit' )
+ exit.appendChild(document.createTextNode( '⬅' ))
+ parameterEl.setAttribute( "operation-moment-index", operationEl.getAttribute( 'moment-index' ))
+ parameterEl.setAttribute( "operation-register-index", operationEl.getAttribute( 'register-index' ))
+
+
+ //here we generate queries for each parameter that the gate operation takes!
+ const parameters = Object.keys(operation.parameters)
+ parameters.forEach( element => {
+ if( operation.parameters && operation.parameters[element] !== null ) {
+ const input_fields = document.createElement( 'div' )
+ parameterEl.appendChild( input_fields )
+ input_fields.classList.add( 'Q-parameter-box-input-container' )
+ const label = document.createElement( "span" )
+ input_fields.appendChild( label )
+ label.classList.add( 'Q-parameter-input-label' )
+ label.appendChild(document.createTextNode( element ))
+
+ const textbox = document.createElement( "input" )
+ input_fields.appendChild( textbox )
+ textbox.classList.add( 'Q-parameter-box-input' )
+ textbox.setAttribute( 'type', 'text' )
+ textbox.setAttribute( 'placeholder', element )
+ textbox.setAttribute( 'value', operationEl.getAttribute(element) ? operationEl.getAttribute(element) : operation.parameters[element] )
+ //set textbox to update the operation instance (cellEl)'s parameters on value change
+ textbox.addEventListener( "change", () => {
+ let parameterValue
+ let oldValue = operationEl.getAttribute( element )
+ if( !oldValue ) oldValue = operation.parameters[ element ]
+ try {
+ //TODO: figure out how to properly import the mathjs library...
+ parameterValue = +(textbox.value.toLowerCase());
+ }
+ catch( err ) {
+ parameterValue = oldValue
+ }
+
+ if( !parameterValue || parameterValue === Infinity ) textbox.value = oldValue.toString()
+ else {
+ operationEl.setAttribute( element, parameterValue )
+ textbox.value = parameterValue.toString()
+ }
+ })
+
+ }
+ })
+ parameterEl.classList.toggle('overlay')
+ parameterEl.style.display = 'block'
+}
///////////////////
@@ -7078,3 +7767,108 @@ window.addEventListener( 'touchend', Q.Circuit.Editor.onPointerRelease )
+/*
+
+
+%%HTML
+
+
+
+
+
+%%javascript
+Q.braket( element )
+
+
+
+
+*/
+
+
+
+//%%javascript
+
+
+
+Q.braket = function(){
+
+
+ // Create the HTML bits we need,
+ // contain them all together,
+ // and output them to Jupyter.
+ if( arguments.length === 0 || arguments.length > 3 ) return;
+ const element = arguments[0];
+ const args = (Array.from(arguments)).slice(1);
+ let circuit
+ if(args.length === 0) {
+ circuit = new Q( 4, 8 )
+ }
+ else if(args.length === 1) {
+ circuit = new Q( args[0] )
+ }
+ else {
+ circuit = new Q( args[0], args[1] )
+ }
+ container = document.createElement( 'div' )
+ container.appendChild( Q.Circuit.Editor.createPalette() )
+ container.appendChild( circuit.toDom() )
+ element.html( container )
+
+
+ // We’re going to take this SLOOOOOOOOWLY
+ // because there are many potential things to debug.
+
+ const thisCell = Jupyter.notebook.get_selected_cell()
+ // console.log( 'thisCell', thisCell )
+
+ const thisCellIndex = Jupyter.notebook.get_cells().indexOf( thisCell )
+ // console.log( 'thisCellIndex', thisCellIndex )
+
+ const nextCell = Jupyter.notebook.insert_cell_below( 'code', thisCellIndex - 1 )
+ const nextNextCell = Jupyter.notebook.insert_cell_below( 'markdown', Jupyter.notebook.get_cells().indexOf( thisCell ) - 1 )
+ // console.log( 'nextCell', nextCell )
+
+ nextCell.set_text( circuit.toAmazonBraket() )
+ nextNextCell.set_text( circuit.report$() )
+
+
+
+
+
+
+ window.addEventListener( 'Q gui altered circuit', function( event ){
+
+ // updatePlaygroundFromDom( event.detail.circuit )
+ if( event.detail.circuit === circuit ){
+
+ console.log( 'Updating circuit from GUI', circuit )
+ circuit.evaluate$()
+ nextCell.set_text( circuit.toAmazonBraket() )
+
+ }
+ })
+
+ window.addEventListener( 'Q.Circuit.evaluate completed', function( event ) {
+ if( event.detail.circuit === circuit ) {
+ nextNextCell.set_text( circuit.report$() )
+ }
+ })
+
+
+ // nextCell.render()
+
+ // console.log( 'thisCell', thisCell )
+ // console.log( 'nextCell', nextCell )
+ // console.log( 'thisCellIndex', thisCellIndex )
+
+ // code = Jupyter.notebook.insert_cell_{0}('code');
+ // code.set_text(atob("{1}"))
+
+ // var t_cell = Jupyter.notebook.get_selected_cell()
+ // t_cell.set_text(' \\n{}')
+ // var t_index = Jupyter.notebook.get_cells().indexOf(t_cell)
+ // Jupyter.notebook.to_markdown(t_index)
+ // Jupyter.notebook.get_cell(t_index).render()
+}
+
+module.exports = Q
\ No newline at end of file
diff --git a/contributing.html b/contributing.html
index c8a3316..a516c2c 100644
--- a/contributing.html
+++ b/contributing.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
diff --git a/index.html b/index.html
index 1aa2898..c513938 100644
--- a/index.html
+++ b/index.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
@@ -585,7 +578,7 @@ Import and export
// console.log( 'state width', state.getWidth(), 'state height', state.getHeight() )
// console.log( 'state', state.toTsv() )
})
-window.addEventListener( 'Q.Circuit.evaluate completed', function( event ){
+window.addEventListener( 'Circuit.evaluate completed', function( event ){
const circuit = event.detail.circuit
console.log(
@@ -624,7 +617,7 @@ Import and export
// Demonstrate Q’s random naming feature.
-const circuitNameRandom = Q.getRandomName$()
+const circuitNameRandom = misc.getRandomName$()
Array
.from( document.querySelectorAll( '.circuit-name-random' ))
@@ -639,8 +632,8 @@ Import and export
// so wee can begin playing with them straight away.
var
-cat = new Q.ComplexNumber( 1, 2 ),
-dog = new Q.ComplexNumber( 3, -4 )
+cat = new ComplexNumber( 1, 2 ),
+dog = new ComplexNumber( 3, -4 )
@@ -661,7 +654,7 @@ Import and export
.from( document.querySelectorAll( '.Q-circuit-palette' ))
.forEach( function( el ){
- Q.Circuit.Editor.createPalette( el )
+ Editor.createPalette( el )
})
diff --git a/index.js b/index.js
index fc40a83..840985e 100644
--- a/index.js
+++ b/index.js
@@ -1,13 +1,6 @@
-const {Q} = require('./Q/Q');
-const {Circuit} = require('./Q/Q-Circuit');
-const {Qubit} = require('./Q/Q-Qubit');
-const {Gate} = require('./Q/Q-Gate');
-const {Matrix} = require('./Q/Q-Matrix');
-const {ComplexNumber} = require('./Q/Q-ComplexNumber');
-const mathf = require('./Q/Math-Functions');
-const misc = require('./Q/Misc');
-const logger = require('./Q/Logging');
+let {logger, mathf, misc, ComplexNumber, Matrix, Gate, Qubit, Circuit, History, Q} = require('quantum-js-util');
+let {Editor, BlochSphere, braket} = require('quantum-js-vis');
+global.misc = misc;
+global.logger = logger;
+global.mathf = mathf;
-console.log("Howdy! Welcome to Q.js!");
-
-module.exports = {Q, Circuit, Qubit, Gate, Matrix, ComplexNumber, mathf, misc, logger};
\ No newline at end of file
diff --git a/package.json b/package.json
index 0573d1b..82cdd1c 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,11 @@
"description": "\")",
"main": "Q/Q.js",
"scripts": {
- "test": "npm run test -ws && exit 0",
- "lint": "eslint",
- "prettier": "prettier --write"
+ "test": "npm run test -ws",
+ "lint": "npm run lint -ws",
+ "prettier": "npm run prettier -ws",
+ "dev": "vite",
+ "build": "npx browserify index.js > build/bundle.js && npx concat -o build/bundle.css packages/quantum-js-vis/Q.css packages/quantum-js-vis/Q-Circuit-Editor.css"
},
"repository": {
"type": "git",
@@ -16,6 +18,8 @@
"author": "",
"license": "ISC",
"devDependencies": {
+ "browserify": "^17.0.0",
+ "concat": "^1.0.3",
"eslint": "^7.31.0",
"jest": "^27.0.6",
"jsdom": "^16.6.0",
@@ -23,7 +27,8 @@
"n": "^7.3.1",
"prettier": "2.3.2"
},
- "dependencies": {},
+ "dependencies": {
+ },
"workspaces": [
"./packages/*"
]
diff --git a/packages/quantum-js-cli/__test__/runner.test.js b/packages/quantum-js-cli/__test__/runner.test.js
new file mode 100644
index 0000000..1ae42fb
--- /dev/null
+++ b/packages/quantum-js-cli/__test__/runner.test.js
@@ -0,0 +1,257 @@
+const runner = require('../runner');
+const quantumjs = require('quantum-js-util');
+
+
+describe("Checking evaluateInput calls the correct functions and/or logs the correct information given a certain input", () => {
+ //create empty circuit.
+ let circuit = quantumjs.Q();
+ console.log = jest.fn();
+ test("Testing evaluateInput() with input 'help' and an empty circuit.", () => {
+ let expectedText = runner.printMenu();
+ runner.evaluateInput("help", circuit);
+ expect(console.log).toHaveBeenCalled();
+ expect(console.log).toHaveBeenCalledWith(expectedText);
+ })
+ test("Testing evaluateInput() with input '-h' and an empty circuit.", () => {
+ let expectedText = runner.printMenu();
+ runner.evaluateInput("-h", circuit);
+ expect(console.log).toHaveBeenCalled();
+ expect(console.log).toHaveBeenCalledWith(expectedText);
+ })
+ test("Testing evaluateInput() with input 'toAmazonBraket' and an empty circuit", () => {
+ runner.evaluateInput("toAmazonBraket", circuit);
+ expectedText = circuit.toAmazonBraket();
+ expect(console.log).toHaveBeenCalled();
+ expect(console.log).toHaveBeenCalledWith(expectedText);
+ })
+ test("Testing evaluateInput() with input 'toDiagram' and an empty circuit", () => {
+ runner.evaluateInput("toDiagram", circuit);
+ expectedText = circuit.toDiagram();
+ expect(console.log).toHaveBeenCalled();
+ expect(console.log).toHaveBeenCalledWith(expectedText);
+ })
+ test("Testing evaluateInput() with input 'toLaTeX' and an empty circuit", () => {
+ runner.evaluateInput("toLaTeX", circuit);
+ expectedText = circuit.toLatex();
+ expect(console.log).toHaveBeenCalled();
+ expect(console.log).toHaveBeenCalledWith(expectedText);
+ })
+ test("Testing evaluateInput() with input 'report' and an empty circuit", () => {
+ runner.evaluateInput("report", circuit);
+ expectedText = circuit.report$();
+ expect(console.log).toHaveBeenCalled();
+ expect(console.log).toHaveBeenCalledWith(expectedText);
+ })
+ test("Testing evaluateInput() with input 'toText' and an empty circuit", () => {
+ runner.evaluateInput("toText", circuit);
+ expectedText = circuit.toAmazonBraket();
+ expect(console.log).toHaveBeenCalled();
+ expect(console.log).toHaveBeenCalledWith(expectedText);
+ })
+ test("Testing evaluateInput() with input 'clear' and an empty circuit", () => {
+ console.clear = jest.fn();
+ runner.evaluateInput("clear", circuit);
+ expectedText = circuit.toAmazonBraket();
+ expect(console.clear).toHaveBeenCalled();
+ })
+})
+
+//Gate operation syntax is of the regex form /(\w+\(\s*\d+\s*,\s*\[(\s*\d+\s*,{0,1}\s*)+\](,\s*\d+\.{0,1}\d+\s*)*\))/g
+//or more easily described:
+//'gate-symbol(moment-index, [registerIndex0, registerIndex1,...], parameter0, parameter1,...)' with white space allowed liberally
+describe("Testing various forms of gate-operation call expression and see that evaluateOperation is called", ()=> {
+ //create empty circuit.
+ test("Check that evaluateOperation is called once for input 'h(1, [1])'", () => {
+ let circuit = quantumjs.Q();
+ runner.evaluateInput("h(1, [1])", circuit);
+ expect(circuit.get(1, 1).gate.symbol).toBe('H');
+ })
+ //messing with the white space
+ test("Check that evaluateOperation is called once for input 'h( 1 , [ 1 ])'", () => {
+ let circuit = quantumjs.Q();
+ runner.evaluateInput("h( 1 , [ 1 ])", circuit)
+ expect(circuit.get(1, 1).gate.symbol).toBe('H');
+ })
+ //while this doesn't update the circuit, it should still call evaluateOperation which detects the flaw later.
+ test("Check that no operation is added for 'h( 1 , [ 1 ], 3)' as the Hadamard operation takes no parameters", () => {
+ let circuit = quantumjs.Q();
+ runner.evaluateInput("h( 1 , [ 1 ], 3)", circuit)
+ expect(circuit.get(1, 1)).toBeUndefined();
+ })
+ test("Check that evaluateOperation is called for input 'x(1, [1, 2])'", () => {
+ let circuit = quantumjs.Q();
+ runner.evaluateInput("x(1, [1, 2])", circuit)
+ expect(circuit.get(1, 1).gate.symbol).toBe("X");
+ expect(circuit.get(1, 2).gate.symbol).toBe("X");
+ })
+ //messing with the white space
+ test("Check that evaluateOperation is called for input 'x( 1, [ 1, 2 ] )'", () => {
+ let circuit = quantumjs.Q();
+ runner.evaluateInput("x(1, [1, 2])", circuit)
+ expect(circuit.get(1, 1).gate.symbol).toBe("X");
+ expect(circuit.get(1, 2).gate.symbol).toBe("X");
+ })
+ describe("Check that gateSymbol(...,[...]) is valid for all gate constants in the Gate module using their nameCss value", ()=> {
+ Object.entries(quantumjs.Gate.constants).forEach(function(entry) {
+ let gate = entry[1];
+ let input = gate.nameCss + (gate.is_multi_qubit ? "(1, [1, 2])" : "(1, [1])");
+ test("Checking that evaluate operation is called once for the input '" + input + "'", () => {
+ let circuit = quantumjs.Q();
+ runner.evaluateInput(input, circuit);
+ expect(circuit.get(1, 1).gate.nameCss).toBe(gate.nameCss);
+ if(gate.is_multi_qubit) {
+ expect(circuit.get(1, 2).gate.nameCss).toBe(gate.nameCss);
+ }
+ })
+ })
+ })
+})
+
+
+describe("Testing removal operations (of the same regex form as above) and that removeOperation() is called", () => {
+ test("Check that removeOperation is called once for input remove(1, [1])", () => {
+ let circuit = quantumjs.Q();
+ //Set a hadamard operation on the circuit.
+ circuit.set$('H', 1, [1]);
+ runner.evaluateInput('remove(1, [1])', circuit);
+ expect(circuit.get(1, 1)).toBeUndefined();
+ })
+ //messing with the whitepsace
+ test("Check that removeOperation is called once for input remove( 1 , [ 1 ] )", () => {
+ let circuit = quantumjs.Q();
+ //Set a hadamard operation on the circuit.
+ circuit.set$('H', 1, [1]);
+ runner.evaluateInput('remove( 1 , [ 1 ] )', circuit);
+ expect(circuit.get(1, 1)).toBeUndefined();
+ })
+ test("Check that removeOperation removes all sibling indices of the operation x(1, [1, 2]) when remove(1, [1]) is called", ()=> {
+ let circuit = quantumjs.Q();
+ circuit.set$('X', 1, [1, 2]);
+ runner.evaluateInput('remove(1, [1])', circuit);
+ expect(circuit.get(1, 1)).toBeUndefined();
+ expect(circuit.get(1, 2)).toBeUndefined();
+ })
+ test("Check that the removeOperation does NOT remove any operation given a set of indices that are not siblings", ()=> {
+ let circuit = quantumjs.Q();
+ //Set a hadamard operation on the circuit.
+ circuit.set$('H', 1, [1]);
+ runner.evaluateInput('remove(1, [1, 2])', circuit);
+ expect(circuit.get(1, 1)).toBeDefined();
+ expect(circuit.get(1, 1).gate.symbol).toBe('H');
+ })
+})
+
+describe("Check that parameters parameterized gates can be input and set properly", ()=> {
+ test("Check that the input 'u(1, [1], 3, 2, 5)' results in the creation of a unitary gate with phi=3,theta=2,lambda=5", ()=>{
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("u(1, [1], 3, 2, 5)", circuit);
+ let result = circuit.get(1, 1).gate;
+ expect(result.symbol).toBe('U');
+ expect(result.parameters['phi']).toBe(3);
+ expect(result.parameters['theta']).toBe(2);
+ expect(result.parameters['lambda']).toBe(5);
+ })
+ //messing with whitespace
+ test("Check that the input 'u(1, [1], 3 , 2 , 5 )' results in the creation of a unitary gate with phi=3,theta=2,lambda=5", ()=>{
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("u(1, [1], 3 , 2 , 5 )", circuit);
+ let result = circuit.get(1, 1).gate;
+ expect(result.symbol).toBe('U');
+ expect(result.parameters['phi']).toBe(3);
+ expect(result.parameters['theta']).toBe(2);
+ expect(result.parameters['lambda']).toBe(5);
+ })
+ //checking regex accepts decimal values
+ test("Check that the input 'u(1, [1], 3.93, 2.24, 5.12)' results in the creation of a unitary gate with phi=3,theta=2,lambda=5", ()=>{
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("u(1, [1], 3.93, 2.24, 5.12)", circuit);
+ let result = circuit.get(1, 1).gate;
+ expect(result.symbol).toBe('U');
+ expect(result.parameters['phi']).toBe(3.93);
+ expect(result.parameters['theta']).toBe(2.24);
+ expect(result.parameters['lambda']).toBe(5.12);
+ })
+ //check that too many parameters results in a failed set operation
+ test("Check that the input 'u(1, [1], 1, 2, 3, 4)' does NOT result creation of a unitary gate", ()=>{
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("u(1, [1], 1, 2, 3, 4)", circuit);
+ expect(circuit.get(1, [1])).toBeUndefined();
+ })
+ test("Check that the input 'u(1, [1], 1, 2)' does result creation of a unitary gate with phi=1,theta=2,lambda=[default]", ()=>{
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("u(1, [1], 1, 2)", circuit);
+ let result = circuit.get(1, 1).gate;
+ let defaultUnitary = quantumjs.Gate.findBySymbol('U');
+ expect(result.symbol).toBe('U');
+ expect(result.parameters['phi']).toBe(1);
+ expect(result.parameters['theta']).toBe(2);
+ expect(result.parameters['lambda']).toBe(defaultUnitary.parameters['lambda']);
+ })
+})
+
+
+describe("Test various combinations of set and remove operations chained together", ()=> {
+ test("Check that the input 'h(1, [1]).x(2, [1])' results in valid gate set operations", ()=> {
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("h(1, [1]).x(2, [1])", circuit);
+ expect(circuit.get(1, 1).gate.symbol).toBe('H');
+ expect(circuit.get(2, 1).gate.symbol).toBe('X');
+ })
+ //Messing with the whitespace
+ test("Check that the input 'h(1, [ 1 ]).x( 2 , [ 1 ] )' results in valid gate set operations", ()=> {
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("h(1, [ 1 ]).x( 2 , [ 1 ] )", circuit);
+ expect(circuit.get(1, 1).gate.symbol).toBe('H');
+ expect(circuit.get(2, 1).gate.symbol).toBe('X');
+ })
+ test("Check that the input 'h(1, [1]).x(2, [1, 2]).p(1, [3], 3.14159)' results in valid gate set operations", ()=> {
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("h(1, [1]).x(2, [1, 2]).p(1, [3], 3.14159)", circuit);
+ expect(circuit.get(1, 1).gate.symbol).toBe('H');
+ expect(circuit.get(2, 1).gate.symbol).toBe('X');
+ expect(circuit.get(2, 1).registerIndices).toEqual([1, 2]);
+ expect(circuit.get(1, 3).gate.symbol).toBe('P');
+ expect(circuit.get(1, 3).gate.parameters['phi']).toBe(3.14159);
+ })
+ test("Check that the input 'h(1, [1]).x(2, [1, 2]).remove(1, [1]).p(1, [3], 3.14159)' results in valid gate set operations", ()=> {
+ let circuit = quantumjs.Q(4, 4);
+ runner.evaluateInput("h(1, [1]).x(2, [1, 2]).remove(1, [1]).p(1, [3], 3.14159)", circuit);
+ expect(circuit.get(1, 1)).toBeUndefined();
+ expect(circuit.get(2, 1).gate.symbol).toBe('X');
+ expect(circuit.get(2, 1).registerIndices).toEqual([1, 2]);
+ expect(circuit.get(1, 3).gate.symbol).toBe('P');
+ expect(circuit.get(1, 3).gate.parameters['phi']).toBe(3.14159);
+ })
+})
+
+
+describe.only("Check that the prepareCircuit() method correctly creates circuits based on user input", ()=> {
+ let prompt = jest.fn();
+ test("Test prepareCircuit() with inputs '1'...'4' at prompts to see it creates an empty circuit with bandwidth = 4", ()=> {
+ prompt.mockReturnValueOnce("1").mockReturnValueOnce("4");
+ let circuit = runner.prepareCircuit(prompt);
+ expect(circuit instanceof quantumjs.Circuit).toBeTruthy();
+ expect(circuit.bandwidth).toBe(4);
+ expect(circuit.timewidth).toBe(8);
+ //check that there are no operations on the circuit
+ for(let i = 0; i < circuit.bandwidth; i++) {
+ for(let j = 0; j < circuit.timewidth; j++) {
+ expect(circuit.get(i, j)).toBeUndefined();
+ }
+ }
+ })
+ test("Test prepareCircuit() with inputs '1'...'-1'...'4' at prompts to see it creates an empty circuit with bandwidth = 4", ()=> {
+ //the -1 input will not trigger a circuit creation as -1 is not a valid number of registers. The user will be prompted again, to which '4' will be a valid response.
+ prompt.mockReturnValueOnce("1").mockReturnValueOnce('-1').mockReturnValueOnce("4");
+ let circuit = runner.prepareCircuit(prompt);
+ expect(circuit instanceof quantumjs.Circuit).toBeTruthy();
+ expect(circuit.bandwidth).toBe(4);
+ expect(circuit.timewidth).toBe(8);
+ //check that there are no operations on the circuit
+ for(let i = 0; i < circuit.bandwidth; i++) {
+ for(let j = 0; j < circuit.timewidth; j++) {
+ expect(circuit.get(i, j)).toBeUndefined();
+ }
+ }
+ })
+})
\ No newline at end of file
diff --git a/packages/quantum-js-cli/index.js b/packages/quantum-js-cli/index.js
new file mode 100644
index 0000000..d9a69ae
--- /dev/null
+++ b/packages/quantum-js-cli/index.js
@@ -0,0 +1,5 @@
+const {run} = require('./runner');
+
+run();
+
+module.exports = {run};
\ No newline at end of file
diff --git a/packages/quantum-js-cli/package.json b/packages/quantum-js-cli/package.json
new file mode 100644
index 0000000..1f15a31
--- /dev/null
+++ b/packages/quantum-js-cli/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "quantum-js-cli",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "jest",
+ "pretter": "npx prettier --write",
+ "lint": "npx eslint **/*.js"
+ },
+ "dependencies": {
+ "prompt-sync": "^4.2.0",
+ "readline-sync": "^1.4.10"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/packages/quantum-js-cli/runner.js b/packages/quantum-js-cli/runner.js
new file mode 100644
index 0000000..7e46e3a
--- /dev/null
+++ b/packages/quantum-js-cli/runner.js
@@ -0,0 +1,228 @@
+const {Q, Circuit, Gate, logger} = require('quantum-js-util');
+const prompt_sync = require('prompt-sync')({sigint: true});
+var readlineSync = require('readline-sync');
+const mark = "> ";
+
+
+function prepareCircuit(prompt = prompt_sync) {
+ let selection = NaN;
+ console.clear();
+ let circuit;
+ while(!selection) {
+ //the following prompt requires the user to select between a number of options to create a circuit. they will enter the NUMBER that corresponds with the action they'd like.
+ console.log("Please select a method (number value) to begin circuit creation: ");
+ console.log("1. From Scratch\n" +
+ "2. From Text Diagram\n");
+ selection = Number(prompt(mark));
+ switch(selection) {
+ case 1:
+ let num_registers = NaN;
+ while(!num_registers || num_registers <= 0) {
+ console.log("Enter the number of qubits you would like to start out with.\n");
+ num_registers = Number(prompt(mark));
+ }
+ circuit = Q(num_registers, 8);
+ break;
+ case 2:
+ circuit = prepareCircuitFromTable();
+ break;
+ default:
+ selection = NaN;
+ }
+ }
+ if(!(circuit instanceof Circuit)) {
+ logger.error("Failed to create circuit");
+ process.exit();
+ }
+ console.log(circuit.toDiagram());
+ return circuit;
+}
+
+function prepareCircuitFromTable() {
+ let resultingCircuit;
+ let tableString;
+ let lines = [];
+ console.log('Input (or paste) your table below and press [Enter] key twice to submit.');
+ readlineSync.promptLoop(line => {
+ lines.push(line);
+ return !line;
+ }, {prompt: ''});
+ tableString = lines.join('\n');
+ try {
+ resultingCircuit = Q(tableString.trim());
+ }
+ catch(e) {
+ return logger.error("Failed to create circuit from table.");
+ }
+ if(!(resultingCircuit instanceof Circuit) || resultingCircuit.bandwidth <= 0 || resultingCircuit.timewidth <= 0) return logger.error("Failed to create circuit from table.");
+ return resultingCircuit;
+}
+
+
+
+function printMenu() {
+ let menu =
+`-h, help Print Q.js command line options (currently set)
+ toDiagram Print the current circuit as a text diagram
+ toAmazonBraket Export the current circuit as Python code using the Amazon Braket SDK.
+ toLaTeX Print the current circuit as a LaTeX diagram
+ toText Print as a table using only common characters (can be used to import later).
+ report Evaluate and current circuit's probability report
+ clear Clear the console
+ newCircuit Discard the current circuit and create a new circuit.
+q, quit Exit the command line
+ `;
+ console.log(menu);
+ return menu;
+}
+
+function evaluateOperation(input, circuit) {
+ let functionName = (/[^\s,\[\]()]+/g).exec(input)[0];
+ let gate = Gate.findBySymbol(functionName);
+ if(!gate) gate = Gate.findByNameCss(functionName)
+ //checks that the function call is gate set$ operation and not another circuit operation.
+ //Syntax: GateSymbol(momentIndex, [registerIndex0, registerIndex1,...])
+ //Regex explanation: looks for the first INTEGER of the from "(digit0digit1digit2..." and removes the "(" at the beginning.
+ let momentIndex = +(/\(\s*\d+/).exec(input)[0].substring(1);
+ if(momentIndex > circuit.timewidth || momentIndex < 0) return logger.error("Moment index out of bounds");
+ if(momentIndex === undefined) {
+ return logger.error("Invalid gate set operation syntax");
+ }
+
+ let registerIndices;
+ //This is a regex that selects an array of integers from a string, i.e. any substring of the form "[integer1, integer2, integer3...]"
+ let arrayRegex = /\[(\s*\d+\s*,{0,1}\s*)+\]/g;
+ try {
+ registerIndices = (arrayRegex)
+ .exec(input)[0]
+ .slice(1, -1)
+ .split(',')
+ .map(index => +index);
+ }
+ catch(e) {
+ return logger.error("Invalid gate set operation syntax");
+ }
+ if(!registerIndices.every(index => {
+ return index > 0 && index < circuit.bandwidth;
+ })) return logger.error("Register index out of bounds");
+ let newParameters = {};
+ input = input.substring(arrayRegex.lastIndex);
+ let commaSeparatedDecimalRegex = /\d+\.{0,1}\d*/g
+ let input_params = [];
+ while(value = commaSeparatedDecimalRegex.exec(input)) {
+ input_params.push(Number(value[0]));
+ }
+ input_params.reverse();
+ if(gate.has_parameters) {
+ if(input_params.length > Object.keys(gate.parameters).length) return logger.error("b Invalid gate set operation syntax");
+ Object.keys(gate.parameters).forEach(key => {
+ newParameters[key] = input_params.pop();
+ if(!newParameters[key]) {
+ newParameters[key] = gate.parameters[key];
+ }
+ });
+ }
+ else if(input_params.length !== 0) return logger.error("Invalid gate set operation syntax");
+ return circuit[functionName](momentIndex, registerIndices, newParameters);
+}
+
+
+function removeOperation(input, circuit) {
+ let momentIndex = +(/\(\s*\d+/).exec(input)[0].substring(1);
+ if(momentIndex === undefined) {
+ return logger.error("Invalid gate set operation syntax");
+ }
+ //
+ let registerIndices;
+ let arrayRegex = /\[(\s*\d+\s*,{0,1}\s*)+\]/g;
+ try {
+ registerIndices = (arrayRegex)
+ .exec(input)[0]
+ .slice(1, -1)
+ .split(',')
+ .map(index => Number(index));
+ }
+ catch(e) {
+ return logger.error("Invalid gate set operation syntax");
+ }
+ if(input.substring(arrayRegex.lastIndex).trim() != ")") {
+ return logger.error("Invalid gate set operation syntax");
+ }
+ let operationToRemove = circuit.get(momentIndex, registerIndices[0]);
+ if(!operationToRemove) {
+ return logger.log("No operation to remove");
+ }
+ if(registerIndices.every(index => {
+ return operationToRemove.registerIndices.includes(index);
+ })) return circuit.clear$(momentIndex, operationToRemove.registerIndices);
+}
+
+function evaluateInput(input, circuit, prompt=prompt_sync) {
+ switch(input) {
+ case "-h":
+ case "help":
+ printMenu();
+ break;
+ case "toDiagram":
+ console.log(circuit.toDiagram());
+ break;
+ case "toAmazonBraket":
+ console.log(circuit.toAmazonBraket());
+ break;
+ case "toLaTeX":
+ console.log(circuit.toLatex());
+ break;
+ case "report":
+ circuit.evaluate$();
+ console.log(circuit.report$());
+ break;
+ case "clear":
+ console.clear();
+ break;
+ case "toText":
+ console.log(circuit.toText(true));
+ break;
+ case "newCircuit":
+ let response = prompt("Creating a new circuit will discard the current circuit. Enter yes to continue: ").toLowerCase();
+ if(response !== "yes") console.log("Did not create new circuit.");
+ else circuit = prepareCircuit();
+ break;
+ default:
+ let circuitBackup = circuit.toText();
+ let functionCallRegex = /((\w+-*)+\(\s*\d+\s*,\s*\[(\s*\d+\s*,{0,1}\s*)+\]\s*(,\s*\d+\.{0,1}\d*\s*)*\))/g;
+ while(functionCall = functionCallRegex.exec(input)) {
+ functionCall = functionCall[0];
+ let functionName = (/[^\s,\[\]()]+/g).exec(functionCall)[0];
+ //checks that the function call is gate set$ operation and not another circuit operation.
+ //Syntax: GateSymbol(momentIndex, [registerIndex0, registerIndex1,...])
+ if(circuit[functionName] instanceof Function && (Gate.findBySymbol(functionName) instanceof Gate || Gate.findByNameCss(functionName) instanceof Gate)) {
+ if(evaluateOperation(functionCall, circuit) === "(error)") {
+ circuit = Q(circuitBackup);
+ break;
+ }
+ }
+ //Syntax: clear(momentIndex, registerIndex)
+ //If the registerIndex is the index of a multiqubit operation, we clear all indices associated with the operation under registerIndex
+ else if(functionName == "remove") {
+ if(removeOperation(functionCall, circuit) === "(error)") {
+ circuit = circuitBackup;
+ break;
+ }
+ }
+ }
+ }
+ return circuit;
+}
+
+
+function run(prompt = prompt_sync) {
+ let circuit = prepareCircuit(prompt);
+ let input = prompt(mark);
+ while(input !== "quit" && input !== "q" && circuit !== '(error)') {
+ circuit = evaluateInput(input, circuit, prompt);
+ input = prompt(mark);
+ }
+
+}
+
+module.exports = {run, evaluateInput, removeOperation, evaluateOperation, printMenu, prepareCircuit};
\ No newline at end of file
diff --git a/packages/quantum-js-util/Misc.js b/packages/quantum-js-util/Misc.js
index 4eb824d..6c7974b 100644
--- a/packages/quantum-js-util/Misc.js
+++ b/packages/quantum-js-util/Misc.js
@@ -1,18 +1,23 @@
const logger = require('./Logging');
-const COLORS = [];
-const ANIMALS = [];
const constants = {};
-
-function dispatchEventToGlobal(event) {
- if(typeof window != undefined) {
- window.dispatchEvent(event);
- }
- else {
- //if window does exist, global == window is true. So maybe we can just do global.dispatchEvent instead of this wrapper?
- global.dispatchEvent(event);
- console.log(event);
+function dispatchCustomEventToGlobal(event_name, detail, terminate_on_error=false, silent=true) {
+ try {
+ const event = new CustomEvent(event_name, detail);
+ if(typeof window != undefined) {
+ window.dispatchEvent(event);
+ }
+ else {
+ //if window does exist, global == window is true. So maybe we can just do global.dispatchEvent instead of this wrapper?
+ global.dispatchEvent(event);
+ if(!silent) console.log(event);
+ }
+ } catch(e) {
+ //When running in node, CustomEvent and documents don't exist. We can emulate using a JSDOM package
+ if(!silent) logger.error("Could not dispatch custom event.");
+ if(terminate_on_error) process.exit();
}
+
}
function createConstant(key, value) {
@@ -60,7 +65,6 @@ function shuffleNames$() {
function getRandomName$() {
if (shuffledNames.length === 0) shuffleNames$();
-
const pair = shuffledNames[namesIndex],
name = COLORS[pair[0]] + " " + ANIMALS[pair[1]];
@@ -397,4 +401,4 @@ createConstants(
]
);
-module.exports = { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchEventToGlobal, constants };
+module.exports = { createConstant, createConstants, getRandomName$, hueToColorName, colorIndexToHue, dispatchCustomEventToGlobal, constants };
diff --git a/packages/quantum-js-util/Q-Circuit.js b/packages/quantum-js-util/Q-Circuit.js
index 9cba24c..3a63015 100644
--- a/packages/quantum-js-util/Q-Circuit.js
+++ b/packages/quantum-js-util/Q-Circuit.js
@@ -425,13 +425,13 @@ Object.assign( Circuit, {
// console.log( circuit.toDiagram() )
- misc.dispatchEventToGlobal(new CustomEvent(
+ misc.dispatchCustomEventToGlobal(
'Circuit.evaluate began', {
detail: { circuit }
}
- ))
+ );
// Our circuit’s operations must be in the correct order
@@ -541,7 +541,7 @@ Object.assign( Circuit, {
const progress = operationsCompleted / operationsTotal
- misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate progressed', { detail: {
+ misc.dispatchCustomEventToGlobal('Circuit.evaluate progressed', { detail: {
circuit,
progress,
@@ -552,7 +552,7 @@ Object.assign( Circuit, {
gate: operation.gate.name,
state
- }}))
+ }})
// console.log( `\n\nProgress ... ${ Math.round( operationsCompleted / operationsTotal * 100 )}%`)
@@ -591,13 +591,13 @@ Object.assign( Circuit, {
- misc.dispatchEventToGlobal(new CustomEvent( 'Circuit.evaluate completed', { detail: {
+ misc.dispatchCustomEventToGlobal('Circuit.evaluate completed', { detail: {
// circuit.dispatchEvent( new CustomEvent( 'evaluation complete', { detail: {
circuit,
results: outcomes
- }}))
+ }})
@@ -1121,6 +1121,7 @@ https://cirq.readthedocs.io/en/stable/tutorial.html
const header = `import boto3
from braket.aws import AwsDevice
from braket.circuits import Circuit
+from braket.devices import LocalSimulator
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
@@ -1388,7 +1389,7 @@ print(task.result().measurement_counts)`
foundOperations.forEach( function( operation ){
- misc.dispatchEventToGlobal(new CustomEvent(
+ misc.dispatchCustomEventToGlobal(
'Circuit.clear$', { detail: {
@@ -1396,7 +1397,7 @@ print(task.result().measurement_counts)`
momentIndex,
registerIndices: operation.registerIndices
}}
- ))
+ )
})
}
@@ -1545,14 +1546,14 @@ print(task.result().measurement_counts)`
// Emit an event that we have set an operation
// on this circuit.
- misc.dispatchEventToGlobal(new CustomEvent(
+ misc.dispatchCustomEventToGlobal(
'Circuit.set$', { detail: {
circuit,
operation
}}
- ))
+ )
}
return circuit
},
@@ -1615,16 +1616,15 @@ print(task.result().measurement_counts)`
const original = this
let {
- registerFirstIndex,
- registerRange,
- registerLastIndex,
+ qubitFirstIndex,
+ qubitRange,
+ qubitLastIndex,
momentFirstIndex,
momentRange,
momentLastIndex
} = this.determineRanges( options )
-
- const copy = new Circuit( registerRange, momentRange )
+ const copy = new Circuit( qubitRange, momentRange )
original.operations
.filter( function( operation ){
@@ -1635,8 +1635,8 @@ print(task.result().measurement_counts)`
operation.momentIndex >= momentFirstIndex &&
operation.momentIndex < momentLastIndex &&
- operation.registerIndex >= registerFirstIndex &&
- operation.registerIndex < registerLastIndex
+ operation.registerIndex >= qubitFirstIndex &&
+ operation.registerIndex < qubitLastIndex
)
}))
})
@@ -1943,12 +1943,15 @@ Object.entries( Gate.constants ).forEach( function( entry ){
const
gateConstantName = entry[ 0 ],
gate = entry[ 1 ],
- set$ = function( momentIndex, registerIndexOrIndices ){
+ set$ = function( momentIndex, registerIndexOrIndices, parameters ){
- this.set$( gate, momentIndex, registerIndexOrIndices )
+ this.set$( gate, momentIndex, registerIndexOrIndices, parameters )
return this
}
- Circuit.prototype[ gateConstantName ] = set$
+ Circuit.prototype[ gate.name ] = set$,
+ Circuit.prototype[ gate.name.toLowerCase() ] = set$,
+ Circuit.prototype[ gate.nameCss ] = set$,
+ Circuit.prototype[ gate.nameCss.toLowerCase() ] = set$,
Circuit.prototype[ gate.symbol ] = set$
Circuit.prototype[ gate.symbol.toLowerCase() ] = set$
})
diff --git a/packages/quantum-js-util/Q-Gate.js b/packages/quantum-js-util/Q-Gate.js
index 472dde1..488a666 100644
--- a/packages/quantum-js-util/Q-Gate.js
+++ b/packages/quantum-js-util/Q-Gate.js
@@ -11,7 +11,6 @@ Gate = function( params ){
this.index = Gate.index ++
if( typeof this.symbol !== 'string' ) this.symbol = '?'
- if( typeof this.symbolAmazonBraket !== 'string' ) this.symbolAmazonBraket = this.symbol.toLowerCase()
const parameters = Object.assign( {}, params.parameters )
this.parameters = parameters
@@ -99,6 +98,9 @@ Object.assign( Gate, {
findByName: function( name ){
return Gate.findBy( 'name', name )
+ },
+ findByNameCss: function( nameCss ) {
+ return Gate.findBy( 'nameCss', nameCss )
}
})
diff --git a/packages/quantum-js-util/Q-History.js b/packages/quantum-js-util/Q-History.js
index 1d38945..a41cfaf 100644
--- a/packages/quantum-js-util/Q-History.js
+++ b/packages/quantum-js-util/Q-History.js
@@ -1,7 +1,7 @@
// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-const {dispatchEventToGlobal} = require('./Misc');
+const {dispatchCustomEventToGlobal} = require('./Misc');
History = function( instance ){
@@ -26,31 +26,27 @@ Object.assign( History.prototype, {
const instance = this.instance
if( this.index > 0 ){
- dispatchEventToGlobal(new CustomEvent(
-
+ dispatchCustomEventToGlobal(
'History undo is capable', { detail: { instance }}
- ));
+ );
}
else {
- dispatchEventToGlobal(new CustomEvent(
-
+ dispatchCustomEventToGlobal(
'History undo is depleted', { detail: { instance }}
- ))
+ )
}
if( this.index + 1 < this.entries.length ){
- dispatchEventToGlobal(new CustomEvent(
-
+ dispatchCustomEventToGlobal(
'History redo is capable', { detail: { instance }}
- ))
+ )
}
else {
- dispatchEventToGlobal(new CustomEvent(
-
+ dispatchCustomEventToGlobal(
'History redo is depleted', { detail: { instance }}
- ))
+ )
}
return this
},
diff --git a/packages/quantum-js-util/Q-Matrix.js b/packages/quantum-js-util/Q-Matrix.js
index 6f837c5..544b4d4 100644
--- a/packages/quantum-js-util/Q-Matrix.js
+++ b/packages/quantum-js-util/Q-Matrix.js
@@ -96,7 +96,7 @@ Matrix = function () {
Object.assign(Matrix, {
index: 0,
help: function () {
- return help(this);
+ return logger.help(this);
},
constants: {}, // Only holds references; an easy way to look up what constants exist.
createConstant: function (key, value) {
diff --git a/packages/quantum-js-util/Q-Qubit.js b/packages/quantum-js-util/Q-Qubit.js
index 48c3f1e..99fc7c4 100644
--- a/packages/quantum-js-util/Q-Qubit.js
+++ b/packages/quantum-js-util/Q-Qubit.js
@@ -95,7 +95,7 @@ Qubit.prototype.constructor = Qubit;
Object.assign(Qubit, {
index: 0,
help: function () {
- return help(this);
+ return logger.help(this);
},
constants: {},
createConstant: function (key, value) {
diff --git a/packages/quantum-js-util/Q.js b/packages/quantum-js-util/Q.js
index a4ca1d5..4749fb2 100644
--- a/packages/quantum-js-util/Q.js
+++ b/packages/quantum-js-util/Q.js
@@ -1,25 +1,19 @@
// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
-const logger = require('./Logging');
const misc = require('./Misc');
const mathf = require('./Math-Functions');
-const {ComplexNumber} = require('./Q-ComplexNumber');
-const {Gate} = require('./Q-Gate');
-const {Qubit} = require('./Q-Qubit');
-const {Matrix} = require('./Q-Matrix');
-const {History} = require('./Q-History');
const {Circuit} = require('./Q-Circuit');
-const Q = function () {
+Q = function () {
// Did we send arguments of the form
// ( bandwidth, timewidth )?
if (
arguments.length === 2 &&
Array.from(arguments).every(function (argument) {
- return isUsefulInteger(argument);
+ return mathf.isUsefulInteger(argument);
})
) {
return new Circuit(arguments[0], arguments[1]);
@@ -51,5 +45,5 @@ https://quantumjavascript.app
`);
-module.exports = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q};
+module.exports = {Q};
diff --git a/packages/quantum-js-util/index.js b/packages/quantum-js-util/index.js
index c1cc69f..997c0c9 100644
--- a/packages/quantum-js-util/index.js
+++ b/packages/quantum-js-util/index.js
@@ -1 +1,12 @@
-const {Q} = require('./Q');
+const logger = require('./Logging');
+const misc = require('./Misc');
+const mathf = require('./Math-Functions');
+const {ComplexNumber} = require('./Q-ComplexNumber');
+const {Gate} = require('./Q-Gate');
+const {Qubit} = require('./Q-Qubit');
+const {Matrix} = require('./Q-Matrix');
+const {History} = require('./Q-History');
+const {Circuit} = require('./Q-Circuit');
+const {Q} = require('./Q.js');
+
+module.exports = {logger, misc, mathf, ComplexNumber, Matrix, Gate, Qubit, History, Circuit, Q};
diff --git a/packages/quantum-js-util/package-lock.json b/packages/quantum-js-util/package-lock.json
index 9a5f4af..8be5282 100644
--- a/packages/quantum-js-util/package-lock.json
+++ b/packages/quantum-js-util/package-lock.json
@@ -1,8 +1,4696 @@
{
"name": "quantum-js-util",
"version": "1.0.0",
- "lockfileVersion": 1,
+ "lockfileVersion": 2,
"requires": true,
+ "packages": {
+ "": {
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "custom-event": "^1.0.1"
+ },
+ "devDependencies": {
+ "cross-env": "^7.0.3",
+ "eslint": "^7.31.0",
+ "jest": "^27.0.6",
+ "jsdom": "^16.6.0",
+ "prettier": "2.3.2"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz",
+ "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.14.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz",
+ "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz",
+ "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.14.5",
+ "@babel/generator": "^7.14.8",
+ "@babel/helper-compilation-targets": "^7.14.5",
+ "@babel/helper-module-transforms": "^7.14.8",
+ "@babel/helpers": "^7.14.8",
+ "@babel/parser": "^7.14.8",
+ "@babel/template": "^7.14.5",
+ "@babel/traverse": "^7.14.8",
+ "@babel/types": "^7.14.8",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.1.2",
+ "semver": "^6.3.0",
+ "source-map": "^0.5.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz",
+ "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.14.8",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz",
+ "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.14.5",
+ "@babel/helper-validator-option": "^7.14.5",
+ "browserslist": "^4.16.6",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz",
+ "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-get-function-arity": "^7.14.5",
+ "@babel/template": "^7.14.5",
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-get-function-arity": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz",
+ "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz",
+ "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.14.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz",
+ "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz",
+ "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz",
+ "integrity": "sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.14.5",
+ "@babel/helper-replace-supers": "^7.14.5",
+ "@babel/helper-simple-access": "^7.14.8",
+ "@babel/helper-split-export-declaration": "^7.14.5",
+ "@babel/helper-validator-identifier": "^7.14.8",
+ "@babel/template": "^7.14.5",
+ "@babel/traverse": "^7.14.8",
+ "@babel/types": "^7.14.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz",
+ "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz",
+ "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz",
+ "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-member-expression-to-functions": "^7.14.5",
+ "@babel/helper-optimise-call-expression": "^7.14.5",
+ "@babel/traverse": "^7.14.5",
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz",
+ "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.14.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz",
+ "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz",
+ "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz",
+ "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.8.tgz",
+ "integrity": "sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.14.5",
+ "@babel/traverse": "^7.14.8",
+ "@babel/types": "^7.14.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+ "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.14.5",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "node_modules/@babel/highlight/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz",
+ "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-bigint": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+ "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+ "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-meta": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+ "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz",
+ "integrity": "sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz",
+ "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.14.5",
+ "@babel/parser": "^7.14.5",
+ "@babel/types": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.8.tgz",
+ "integrity": "sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.14.5",
+ "@babel/generator": "^7.14.8",
+ "@babel/helper-function-name": "^7.14.5",
+ "@babel/helper-hoist-variables": "^7.14.5",
+ "@babel/helper-split-export-declaration": "^7.14.5",
+ "@babel/parser": "^7.14.8",
+ "@babel/types": "^7.14.8",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.14.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz",
+ "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.14.8",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bcoe/v8-coverage": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
+ "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.1.1",
+ "espree": "^7.3.0",
+ "globals": "^13.9.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.10.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz",
+ "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
+ "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.0",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz",
+ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==",
+ "dev": true
+ },
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+ "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jest/console": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.0.6.tgz",
+ "integrity": "sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "jest-message-util": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/core": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.0.6.tgz",
+ "integrity": "sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^27.0.6",
+ "@jest/reporters": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "emittery": "^0.8.1",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.4",
+ "jest-changed-files": "^27.0.6",
+ "jest-config": "^27.0.6",
+ "jest-haste-map": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-resolve-dependencies": "^27.0.6",
+ "jest-runner": "^27.0.6",
+ "jest-runtime": "^27.0.6",
+ "jest-snapshot": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "jest-watcher": "^27.0.6",
+ "micromatch": "^4.0.4",
+ "p-each-series": "^2.1.0",
+ "rimraf": "^3.0.0",
+ "slash": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/environment": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.0.6.tgz",
+ "integrity": "sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/fake-timers": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "jest-mock": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/fake-timers": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.0.6.tgz",
+ "integrity": "sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "@sinonjs/fake-timers": "^7.0.2",
+ "@types/node": "*",
+ "jest-message-util": "^27.0.6",
+ "jest-mock": "^27.0.6",
+ "jest-util": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/globals": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.0.6.tgz",
+ "integrity": "sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "expect": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/reporters": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.0.6.tgz",
+ "integrity": "sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA==",
+ "dev": true,
+ "dependencies": {
+ "@bcoe/v8-coverage": "^0.2.3",
+ "@jest/console": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "chalk": "^4.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "exit": "^0.1.2",
+ "glob": "^7.1.2",
+ "graceful-fs": "^4.2.4",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-instrument": "^4.0.3",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.0.2",
+ "jest-haste-map": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-worker": "^27.0.6",
+ "slash": "^3.0.0",
+ "source-map": "^0.6.0",
+ "string-length": "^4.0.1",
+ "terminal-link": "^2.0.0",
+ "v8-to-istanbul": "^8.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@jest/source-map": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.0.6.tgz",
+ "integrity": "sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0",
+ "graceful-fs": "^4.2.4",
+ "source-map": "^0.6.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/test-result": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.0.6.tgz",
+ "integrity": "sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "collect-v8-coverage": "^1.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/test-sequencer": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.0.6.tgz",
+ "integrity": "sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/test-result": "^27.0.6",
+ "graceful-fs": "^4.2.4",
+ "jest-haste-map": "^27.0.6",
+ "jest-runtime": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/transform": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.0.6.tgz",
+ "integrity": "sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.1.0",
+ "@jest/types": "^27.0.6",
+ "babel-plugin-istanbul": "^6.0.0",
+ "chalk": "^4.0.0",
+ "convert-source-map": "^1.4.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "graceful-fs": "^4.2.4",
+ "jest-haste-map": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "micromatch": "^4.0.4",
+ "pirates": "^4.0.1",
+ "slash": "^3.0.0",
+ "source-map": "^0.6.1",
+ "write-file-atomic": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@jest/types": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.6.tgz",
+ "integrity": "sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "^2.0.0",
+ "@types/istanbul-reports": "^3.0.0",
+ "@types/node": "*",
+ "@types/yargs": "^16.0.0",
+ "chalk": "^4.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@sinonjs/commons": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
+ "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/fake-timers": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz",
+ "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==",
+ "dev": true,
+ "dependencies": {
+ "@sinonjs/commons": "^1.7.0"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.1.15",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.15.tgz",
+ "integrity": "sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz",
+ "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
+ "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.14.2",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz",
+ "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.3.0"
+ }
+ },
+ "node_modules/@types/graceful-fs": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
+ "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/istanbul-lib-coverage": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
+ "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==",
+ "dev": true
+ },
+ "node_modules/@types/istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "*"
+ }
+ },
+ "node_modules/@types/istanbul-reports": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
+ "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-report": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.5.tgz",
+ "integrity": "sha512-+0GPv/hIFNoy8r5MFf7vRpBjnqNYNrlHdetoy23E7TYc7JB2ctwyi3GMKpviozaHQ/Sy2kBNUTvG9ywN66zV1g==",
+ "dev": true
+ },
+ "node_modules/@types/prettier": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.3.2.tgz",
+ "integrity": "sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==",
+ "dev": true
+ },
+ "node_modules/@types/stack-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
+ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
+ "dev": true
+ },
+ "node_modules/@types/yargs": {
+ "version": "16.0.4",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
+ "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==",
+ "dev": true,
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz",
+ "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==",
+ "dev": true
+ },
+ "node_modules/abab": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "8.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
+ "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-globals": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
+ "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^7.1.1",
+ "acorn-walk": "^7.1.1"
+ }
+ },
+ "node_modules/acorn-globals/node_modules/acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
+ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "node_modules/babel-jest": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.0.6.tgz",
+ "integrity": "sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/babel__core": "^7.1.14",
+ "babel-plugin-istanbul": "^6.0.0",
+ "babel-preset-jest": "^27.0.6",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.4",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.8.0"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz",
+ "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^4.0.0",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-jest-hoist": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.6.tgz",
+ "integrity": "sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.3.3",
+ "@babel/types": "^7.3.3",
+ "@types/babel__core": "^7.0.0",
+ "@types/babel__traverse": "^7.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/babel-preset-current-node-syntax": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
+ "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-bigint": "^7.8.3",
+ "@babel/plugin-syntax-class-properties": "^7.8.3",
+ "@babel/plugin-syntax-import-meta": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-top-level-await": "^7.8.3"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/babel-preset-jest": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.0.6.tgz",
+ "integrity": "sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw==",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-jest-hoist": "^27.0.6",
+ "babel-preset-current-node-syntax": "^1.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browser-process-hrtime": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+ "dev": true
+ },
+ "node_modules/browserslist": {
+ "version": "4.16.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
+ "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
+ "dev": true,
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001219",
+ "colorette": "^1.2.2",
+ "electron-to-chromium": "^1.3.723",
+ "escalade": "^3.1.1",
+ "node-releases": "^1.1.71"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ }
+ },
+ "node_modules/bser": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
+ "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+ "dev": true,
+ "dependencies": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001248",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz",
+ "integrity": "sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/char-regex": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz",
+ "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==",
+ "dev": true
+ },
+ "node_modules/cjs-module-lexer": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
+ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
+ "dev": true
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true,
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/collect-v8-coverage": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
+ "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
+ "dev": true
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/colorette": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
+ "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
+ "dev": true
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
+ "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cssom": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
+ "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==",
+ "dev": true
+ },
+ "node_modules/cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dev": true,
+ "dependencies": {
+ "cssom": "~0.3.6"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cssstyle/node_modules/cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ },
+ "node_modules/custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
+ },
+ "node_modules/data-urls": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
+ "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.3",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+ "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz",
+ "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==",
+ "dev": true
+ },
+ "node_modules/dedent": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
+ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
+ "dev": true
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "node_modules/deepmerge": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-newline": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
+ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/diff-sequences": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz",
+ "integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==",
+ "dev": true,
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/domexception": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
+ "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==",
+ "dev": true,
+ "dependencies": {
+ "webidl-conversions": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/domexception/node_modules/webidl-conversions": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
+ "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.3.789",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.789.tgz",
+ "integrity": "sha512-lK4xn6C6ZF1kgLaC/EhOtC1MSKENExj3rMwGVnBTfHW81Z/Hb1Rge5YaWawN/YOXy3xCaESuE4KWSD50kOZ9rQ==",
+ "dev": true
+ },
+ "node_modules/emittery": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
+ "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-colors": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
+ "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "7.32.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz",
+ "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "7.12.11",
+ "@eslint/eslintrc": "^0.4.3",
+ "@humanwhocodes/config-array": "^0.5.0",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.1.2",
+ "globals": "^13.6.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^6.0.9",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-scope/node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint/node_modules/@babel/code-frame": {
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+ "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.10.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz",
+ "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint/node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/eslint/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/espree": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.3.1",
+ "eslint-visitor-keys": "^1.3.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/espree/node_modules/acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/expect": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz",
+ "integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "ansi-styles": "^5.0.0",
+ "jest-get-type": "^27.0.6",
+ "jest-matcher-utils": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-regex-util": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/expect/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "node_modules/fb-watchman": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
+ "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==",
+ "dev": true,
+ "dependencies": {
+ "bser": "2.1.1"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz",
+ "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==",
+ "dev": true
+ },
+ "node_modules/form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
+ "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-encoding": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/import-local": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz",
+ "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==",
+ "dev": true,
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-ci": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz",
+ "integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==",
+ "dev": true,
+ "dependencies": {
+ "ci-info": "^3.1.1"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz",
+ "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
+ "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+ "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.7.5",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.0.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "dev": true,
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz",
+ "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
+ "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
+ "dev": true,
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-27.0.6.tgz",
+ "integrity": "sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/core": "^27.0.6",
+ "import-local": "^3.0.2",
+ "jest-cli": "^27.0.6"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-changed-files": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.0.6.tgz",
+ "integrity": "sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "execa": "^5.0.0",
+ "throat": "^6.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-circus": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.0.6.tgz",
+ "integrity": "sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "dedent": "^0.7.0",
+ "expect": "^27.0.6",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^27.0.6",
+ "jest-matcher-utils": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-runtime": "^27.0.6",
+ "jest-snapshot": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "pretty-format": "^27.0.6",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3",
+ "throat": "^6.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-config": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.0.6.tgz",
+ "integrity": "sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.1.0",
+ "@jest/test-sequencer": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "babel-jest": "^27.0.6",
+ "chalk": "^4.0.0",
+ "deepmerge": "^4.2.2",
+ "glob": "^7.1.1",
+ "graceful-fs": "^4.2.4",
+ "is-ci": "^3.0.0",
+ "jest-circus": "^27.0.6",
+ "jest-environment-jsdom": "^27.0.6",
+ "jest-environment-node": "^27.0.6",
+ "jest-get-type": "^27.0.6",
+ "jest-jasmine2": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-runner": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-diff": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz",
+ "integrity": "sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "diff-sequences": "^27.0.6",
+ "jest-get-type": "^27.0.6",
+ "pretty-format": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-docblock": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.0.6.tgz",
+ "integrity": "sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA==",
+ "dev": true,
+ "dependencies": {
+ "detect-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-each": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.0.6.tgz",
+ "integrity": "sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "pretty-format": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-environment-jsdom": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.0.6.tgz",
+ "integrity": "sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.0.6",
+ "@jest/fake-timers": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "jest-mock": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jsdom": "^16.6.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-environment-node": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.0.6.tgz",
+ "integrity": "sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w==",
+ "dev": true,
+ "dependencies": {
+ "@jest/environment": "^27.0.6",
+ "@jest/fake-timers": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "jest-mock": "^27.0.6",
+ "jest-util": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-get-type": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz",
+ "integrity": "sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==",
+ "dev": true,
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-haste-map": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.0.6.tgz",
+ "integrity": "sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "@types/graceful-fs": "^4.1.2",
+ "@types/node": "*",
+ "anymatch": "^3.0.3",
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.2.4",
+ "jest-regex-util": "^27.0.6",
+ "jest-serializer": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-worker": "^27.0.6",
+ "micromatch": "^4.0.4",
+ "walker": "^1.0.7"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "^2.3.2"
+ }
+ },
+ "node_modules/jest-jasmine2": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.0.6.tgz",
+ "integrity": "sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.1.0",
+ "@jest/environment": "^27.0.6",
+ "@jest/source-map": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "co": "^4.6.0",
+ "expect": "^27.0.6",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^27.0.6",
+ "jest-matcher-utils": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-runtime": "^27.0.6",
+ "jest-snapshot": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "pretty-format": "^27.0.6",
+ "throat": "^6.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-leak-detector": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.0.6.tgz",
+ "integrity": "sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ==",
+ "dev": true,
+ "dependencies": {
+ "jest-get-type": "^27.0.6",
+ "pretty-format": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-matcher-utils": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz",
+ "integrity": "sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "jest-diff": "^27.0.6",
+ "jest-get-type": "^27.0.6",
+ "pretty-format": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-message-util": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.6.tgz",
+ "integrity": "sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.12.13",
+ "@jest/types": "^27.0.6",
+ "@types/stack-utils": "^2.0.0",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.4",
+ "micromatch": "^4.0.4",
+ "pretty-format": "^27.0.6",
+ "slash": "^3.0.0",
+ "stack-utils": "^2.0.3"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-mock": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.0.6.tgz",
+ "integrity": "sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "@types/node": "*"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-pnp-resolver": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
+ "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "peerDependencies": {
+ "jest-resolve": "*"
+ },
+ "peerDependenciesMeta": {
+ "jest-resolve": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jest-regex-util": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.0.6.tgz",
+ "integrity": "sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ==",
+ "dev": true,
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-resolve": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.0.6.tgz",
+ "integrity": "sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "chalk": "^4.0.0",
+ "escalade": "^3.1.1",
+ "graceful-fs": "^4.2.4",
+ "jest-pnp-resolver": "^1.2.2",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "resolve": "^1.20.0",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-resolve-dependencies": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.6.tgz",
+ "integrity": "sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-snapshot": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-runner": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.0.6.tgz",
+ "integrity": "sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^27.0.6",
+ "@jest/environment": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "emittery": "^0.8.1",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.4",
+ "jest-docblock": "^27.0.6",
+ "jest-environment-jsdom": "^27.0.6",
+ "jest-environment-node": "^27.0.6",
+ "jest-haste-map": "^27.0.6",
+ "jest-leak-detector": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-runtime": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-worker": "^27.0.6",
+ "source-map-support": "^0.5.6",
+ "throat": "^6.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-runtime": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.0.6.tgz",
+ "integrity": "sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q==",
+ "dev": true,
+ "dependencies": {
+ "@jest/console": "^27.0.6",
+ "@jest/environment": "^27.0.6",
+ "@jest/fake-timers": "^27.0.6",
+ "@jest/globals": "^27.0.6",
+ "@jest/source-map": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/yargs": "^16.0.0",
+ "chalk": "^4.0.0",
+ "cjs-module-lexer": "^1.0.0",
+ "collect-v8-coverage": "^1.0.0",
+ "exit": "^0.1.2",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.2.4",
+ "jest-haste-map": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-mock": "^27.0.6",
+ "jest-regex-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-snapshot": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "slash": "^3.0.0",
+ "strip-bom": "^4.0.0",
+ "yargs": "^16.0.3"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-serializer": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.0.6.tgz",
+ "integrity": "sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "graceful-fs": "^4.2.4"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-snapshot": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.0.6.tgz",
+ "integrity": "sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.7.2",
+ "@babel/generator": "^7.7.2",
+ "@babel/parser": "^7.7.2",
+ "@babel/plugin-syntax-typescript": "^7.7.2",
+ "@babel/traverse": "^7.7.2",
+ "@babel/types": "^7.0.0",
+ "@jest/transform": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/babel__traverse": "^7.0.4",
+ "@types/prettier": "^2.1.5",
+ "babel-preset-current-node-syntax": "^1.0.0",
+ "chalk": "^4.0.0",
+ "expect": "^27.0.6",
+ "graceful-fs": "^4.2.4",
+ "jest-diff": "^27.0.6",
+ "jest-get-type": "^27.0.6",
+ "jest-haste-map": "^27.0.6",
+ "jest-matcher-utils": "^27.0.6",
+ "jest-message-util": "^27.0.6",
+ "jest-resolve": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "natural-compare": "^1.4.0",
+ "pretty-format": "^27.0.6",
+ "semver": "^7.3.2"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-snapshot/node_modules/semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jest-util": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.0.6.tgz",
+ "integrity": "sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "chalk": "^4.0.0",
+ "graceful-fs": "^4.2.4",
+ "is-ci": "^3.0.0",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-validate": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.0.6.tgz",
+ "integrity": "sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.0.0",
+ "jest-get-type": "^27.0.6",
+ "leven": "^3.1.0",
+ "pretty-format": "^27.0.6"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-validate/node_modules/camelcase": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
+ "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jest-watcher": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.0.6.tgz",
+ "integrity": "sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/test-result": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "@types/node": "*",
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.0.0",
+ "jest-util": "^27.0.6",
+ "string-length": "^4.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.6.tgz",
+ "integrity": "sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/jest/node_modules/jest-cli": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.0.6.tgz",
+ "integrity": "sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg==",
+ "dev": true,
+ "dependencies": {
+ "@jest/core": "^27.0.6",
+ "@jest/test-result": "^27.0.6",
+ "@jest/types": "^27.0.6",
+ "chalk": "^4.0.0",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.2.4",
+ "import-local": "^3.0.2",
+ "jest-config": "^27.0.6",
+ "jest-util": "^27.0.6",
+ "jest-validate": "^27.0.6",
+ "prompts": "^2.0.1",
+ "yargs": "^16.0.3"
+ },
+ "bin": {
+ "jest": "bin/jest.js"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ },
+ "peerDependencies": {
+ "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "node-notifier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsdom": {
+ "version": "16.6.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz",
+ "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.5",
+ "acorn": "^8.2.4",
+ "acorn-globals": "^6.0.0",
+ "cssom": "^0.4.4",
+ "cssstyle": "^2.3.0",
+ "data-urls": "^2.0.0",
+ "decimal.js": "^10.2.1",
+ "domexception": "^2.0.1",
+ "escodegen": "^2.0.0",
+ "form-data": "^3.0.0",
+ "html-encoding-sniffer": "^2.0.1",
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.0",
+ "parse5": "6.0.1",
+ "saxes": "^5.0.1",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.0.0",
+ "w3c-hr-time": "^1.0.2",
+ "w3c-xmlserializer": "^2.0.0",
+ "webidl-conversions": "^6.1.0",
+ "whatwg-encoding": "^1.0.5",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.5.0",
+ "ws": "^7.4.5",
+ "xml-name-validator": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
+ "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.5"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
+ "dev": true
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/makeerror": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
+ "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=",
+ "dev": true,
+ "dependencies": {
+ "tmpl": "1.0.x"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
+ "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.1",
+ "picomatch": "^2.2.3"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
+ "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.32",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
+ "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.49.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "node_modules/node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
+ "dev": true
+ },
+ "node_modules/node-modules-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
+ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "1.1.73",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
+ "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+ "dev": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-each-series": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz",
+ "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
+ "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==",
+ "dev": true,
+ "dependencies": {
+ "node-modules-regexp": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
+ "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "27.0.6",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz",
+ "integrity": "sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==",
+ "dev": true,
+ "dependencies": {
+ "@jest/types": "^27.0.6",
+ "ansi-regex": "^5.0.0",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^17.0.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+ }
+ },
+ "node_modules/pretty-format/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/prompts": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz",
+ "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==",
+ "dev": true,
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/psl": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true
+ },
+ "node_modules/regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+ "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.2.0",
+ "path-parse": "^1.0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/saxes": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
+ "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
+ "dev": true,
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
+ "dev": true
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "node_modules/stack-utils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz",
+ "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/stack-utils/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-length": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
+ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+ "dev": true,
+ "dependencies": {
+ "char-regex": "^1.0.2",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+ "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
+ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-hyperlinks": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz",
+ "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
+ },
+ "node_modules/table": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
+ "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^8.0.1",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/table/node_modules/ajv": {
+ "version": "8.6.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz",
+ "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/table/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/terminal-link": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
+ "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^4.2.1",
+ "supports-hyperlinks": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/test-exclude": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+ "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+ "dev": true,
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "node_modules/throat": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
+ "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==",
+ "dev": true
+ },
+ "node_modules/tmpl": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
+ "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
+ "dev": true
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
+ "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
+ "dev": true,
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
+ "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "dev": true,
+ "dependencies": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/v8-compile-cache": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+ "dev": true
+ },
+ "node_modules/v8-to-istanbul": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz",
+ "integrity": "sha512-LkmXi8UUNxnCC+JlH7/fsfsKr5AU110l+SYGJimWNkWhxbN5EyeOtm1MJ0hhvqMMOhGwBj1Fp70Yv9i+hX0QAg==",
+ "dev": true,
+ "dependencies": {
+ "@types/istanbul-lib-coverage": "^2.0.1",
+ "convert-source-map": "^1.6.0",
+ "source-map": "^0.7.3"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/v8-to-istanbul/node_modules/source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/w3c-hr-time": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+ "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+ "dev": true,
+ "dependencies": {
+ "browser-process-hrtime": "^1.0.0"
+ }
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz",
+ "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==",
+ "dev": true,
+ "dependencies": {
+ "xml-name-validator": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/walker": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",
+ "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=",
+ "dev": true,
+ "dependencies": {
+ "makeerror": "1.0.x"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
+ "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.4"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+ "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "dev": true,
+ "dependencies": {
+ "iconv-lite": "0.4.24"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+ "dev": true
+ },
+ "node_modules/whatwg-url": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
+ "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.7.0",
+ "tr46": "^2.1.0",
+ "webidl-conversions": "^6.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "node_modules/write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "dev": true,
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "node_modules/ws": {
+ "version": "7.5.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
+ "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+ "dev": true
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ }
+ },
"dependencies": {
"@babel/code-frame": {
"version": "7.14.5",
@@ -442,6 +5130,57 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
+ "@eslint/eslintrc": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
+ "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.1.1",
+ "espree": "^7.3.0",
+ "globals": "^13.9.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "strip-json-comments": "^3.1.1"
+ },
+ "dependencies": {
+ "globals": {
+ "version": "13.10.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz",
+ "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ }
+ }
+ },
+ "@humanwhocodes/config-array": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
+ "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^1.2.0",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz",
+ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==",
+ "dev": true
+ },
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -813,6 +5552,13 @@
}
}
},
+ "acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "requires": {}
+ },
"acorn-walk": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
@@ -828,6 +5574,24 @@
"debug": "4"
}
},
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
"ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -871,6 +5635,12 @@
"sprintf-js": "~1.0.2"
}
},
+ "astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true
+ },
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -1164,6 +5934,11 @@
}
}
},
+ "custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
+ },
"data-urls": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
@@ -1226,6 +6001,15 @@
"integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==",
"dev": true
},
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
"domexception": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
@@ -1261,6 +6045,15 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
+ "enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1"
+ }
+ },
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -1286,12 +6079,224 @@
"source-map": "~0.6.1"
}
},
+ "eslint": {
+ "version": "7.32.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz",
+ "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "7.12.11",
+ "@eslint/eslintrc": "^0.4.3",
+ "@humanwhocodes/config-array": "^0.5.0",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.1.2",
+ "globals": "^13.6.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^6.0.9",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+ "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "globals": {
+ "version": "13.10.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz",
+ "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true
+ },
+ "espree": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.3.1",
+ "eslint-visitor-keys": "^1.3.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true
+ },
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
+ }
+ },
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
+ "esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ }
+ },
"estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
@@ -1349,6 +6354,12 @@
}
}
},
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -1370,6 +6381,15 @@
"bser": "2.1.1"
}
},
+ "file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -1389,6 +6409,22 @@
"path-exists": "^4.0.0"
}
},
+ "flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz",
+ "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==",
+ "dev": true
+ },
"form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
@@ -1419,6 +6455,12 @@
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
"gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -1457,6 +6499,15 @@
"path-is-absolute": "^1.0.0"
}
},
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -1535,6 +6586,30 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "dependencies": {
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ }
+ }
+ },
"import-local": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz",
@@ -1585,6 +6660,12 @@
"has": "^1.0.3"
}
},
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -1597,6 +6678,15 @@
"integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
"dev": true
},
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -1946,7 +7036,8 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
"integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"jest-regex-util": {
"version": "27.0.6",
@@ -2229,6 +7320,18 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
"json5": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
@@ -2275,6 +7378,24 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
+ "dev": true
+ },
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -2467,6 +7588,15 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
"parse5": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
@@ -2527,6 +7657,12 @@
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
"dev": true
},
+ "prettier": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
+ "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
+ "dev": true
+ },
"pretty-format": {
"version": "27.0.6",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz",
@@ -2547,6 +7683,12 @@
}
}
},
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
"prompts": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz",
@@ -2575,12 +7717,24 @@
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true
},
+ "regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true
+ },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true
+ },
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@@ -2675,6 +7829,17 @@
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true
},
+ "slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ }
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -2756,6 +7921,12 @@
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
"dev": true
},
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -2781,6 +7952,40 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true
},
+ "table": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
+ "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==",
+ "dev": true,
+ "requires": {
+ "ajv": "^8.0.1",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.6.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz",
+ "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ }
+ }
+ },
"terminal-link": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
@@ -2802,6 +8007,12 @@
"minimatch": "^3.0.4"
}
},
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
"throat": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
@@ -2885,6 +8096,21 @@
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true
},
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "v8-compile-cache": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+ "dev": true
+ },
"v8-to-istanbul": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.0.0.tgz",
@@ -3011,7 +8237,8 @@
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
"integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"xml-name-validator": {
"version": "3.0.0",
diff --git a/packages/quantum-js-util/package.json b/packages/quantum-js-util/package.json
index 55c7fe1..181cba9 100644
--- a/packages/quantum-js-util/package.json
+++ b/packages/quantum-js-util/package.json
@@ -2,10 +2,11 @@
"name": "quantum-js-util",
"version": "1.0.0",
"description": "",
- "main": "Q.js",
+ "main": "index.js",
"scripts": {
"test": "jest",
- "prettier": "echo 'I am a prettier util!' && exit 0"
+ "prettier": "npx prettier --write",
+ "lint": "npx eslint **/*.js"
},
"devDependencies": {
"cross-env": "^7.0.3",
@@ -14,7 +15,9 @@
"jsdom": "^16.6.0",
"prettier": "2.3.2"
},
- "dependencies": {},
+ "dependencies": {
+ "custom-event": "^1.0.1"
+ },
"keywords": [],
"author": "",
"license": "ISC"
diff --git a/packages/quantum-js-vis/Q-BlochSphere.js b/packages/quantum-js-vis/Q-BlochSphere.js
new file mode 100644
index 0000000..a1ff2e5
--- /dev/null
+++ b/packages/quantum-js-vis/Q-BlochSphere.js
@@ -0,0 +1,582 @@
+
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+
+
+const {Qubit} = require('quantum-js-util');
+BlochSphere = function( onValueChange ){
+
+ Object.assign( this, {
+
+ isRotating: false,
+ radius: 1,
+ radiusSafe: 1.01,
+ axesLineWidth: 0.01,
+ arcLineWidth: 0.015,
+ state: Qubit.LEFT_HAND_CIRCULAR_POLARIZED.toBlochSphere(),
+ target: Qubit.HORIZONTAL.toBlochSphere(),
+ group: new THREE.Group(),
+ onValueChange
+ })
+
+
+ // Create the surface of the Bloch sphere.
+
+ const surface = new THREE.Mesh(
+
+ new THREE.SphereGeometry( this.radius, 64, 64 ),
+ new THREE.MeshPhongMaterial({
+
+ side: THREE.FrontSide,
+ map: BlochSphere.makeSurface(),
+ transparent: true,
+ opacity: 0.97
+ })
+ )
+ surface.receiveShadow = true
+ this.group.add( surface )
+
+
+
+
+ // Create the X, Y, and Z axis lines.
+
+ const
+ xAxis = new THREE.Mesh(
+
+ new THREE.BoxGeometry( this.axesLineWidth, this.axesLineWidth, this.radius * 2.5 ),
+ new THREE.MeshBasicMaterial({ color: BlochSphere.xAxisColor })
+ ),
+ yAxis = new THREE.Mesh(
+
+ new THREE.BoxGeometry( this.radius * 2.5, this.axesLineWidth, this.axesLineWidth ),
+ new THREE.MeshBasicMaterial({ color: BlochSphere.yAxisColor })
+ ),
+ zAxis = new THREE.Mesh(
+
+ new THREE.BoxGeometry( this.axesLineWidth, this.radius * 2.5, this.axesLineWidth ),
+ new THREE.MeshBasicMaterial({ color: BlochSphere.zAxisColor })
+ )
+
+ this.group.add( xAxis, yAxis, zAxis )
+
+
+ // Create X, Y, and Z arrow heads,
+ // indicating positive directions for all three.
+
+ const
+ arrowLength = 0.101,// I know, weird, right?
+ arrowHeadLength = 0.1,
+ arrowHeadWidth = 0.1
+
+ this.group.add( new THREE.ArrowHelper(
+
+ new THREE.Vector3( 0, 0, 1.00 ),
+ new THREE.Vector3( 0, 0, 1.25 ),
+ arrowLength,
+ BlochSphere.xAxisColor,// Red
+ arrowHeadLength,
+ arrowHeadWidth
+ ))
+ this.group.add( new THREE.ArrowHelper(
+
+ new THREE.Vector3( 1.00, 0, 0 ),
+ new THREE.Vector3( 1.25, 0, 0 ),
+ arrowLength,
+ BlochSphere.yAxisColor,// Green
+ arrowHeadLength,
+ arrowHeadWidth
+ ))
+ this.group.add( new THREE.ArrowHelper(
+
+ new THREE.Vector3( 0, 1.00, 0 ),
+ new THREE.Vector3( 0, 1.25, 0 ),
+ arrowLength,
+ BlochSphere.zAxisColor,// Blue
+ arrowHeadLength,
+ arrowHeadWidth
+ ))
+
+
+ // Create the X, Y, and Z axis labels.
+
+ const
+ axesLabelStyle = {
+
+ width: 128,
+ height: 128,
+ fillStyle: BlochSphere.vectorColor,//'#505962',
+ font: 'bold italic 64px Georgia, "Times New Roman", serif'
+ },
+ xAxisLabel = new THREE.Sprite(
+
+ new THREE.SpriteMaterial({
+
+ map: Object.assign( SurfaceText( axesLabelStyle ))
+ })
+ ),
+ yAxisLabel = new THREE.Sprite(
+
+ new THREE.SpriteMaterial({
+
+ map: Object.assign( SurfaceText( axesLabelStyle ))
+ })
+ ),
+ zAxisLabel = new THREE.Sprite(
+
+ new THREE.SpriteMaterial({
+
+ map: Object.assign( SurfaceText( axesLabelStyle ))
+ })
+ )
+
+ xAxisLabel.material.map.print( 'x' )
+ xAxisLabel.position.set( 0, 0, 1.45 )
+ xAxisLabel.scale.set( 0.25, 0.25, 0.25 )
+ xAxis.add( xAxisLabel )
+
+ yAxisLabel.material.map.print( 'y' )
+ yAxisLabel.position.set( 1.45, 0, 0 )
+ yAxisLabel.scale.set( 0.25, 0.25, 0.25 )
+ yAxis.add( yAxisLabel )
+
+ zAxisLabel.material.map.print( 'z' )
+ zAxisLabel.position.set( 0, 1.45, 0 )
+ zAxisLabel.scale.set( 0.25, 0.25, 0.25 )
+ zAxis.add( zAxisLabel )
+
+
+ this.blochColor = new THREE.Color()
+
+
+ // Create the line from the sphere’s origin
+ // out to where the Bloch vector intersects
+ // with the sphere’s surface.
+
+ this.blochVector = new THREE.Mesh(
+
+ new THREE.BoxGeometry( 0.04, 0.04, this.radius ),
+ new THREE.MeshBasicMaterial({ color: BlochSphere.vectorColor })
+ )
+ this.blochVector.geometry.translate( 0, 0, 0.5 )
+ this.group.add( this.blochVector )
+
+
+ // Create the cone that indicates the Bloch vector
+ // and points to where that vectors
+ // intersects with the surface of the sphere.
+
+ this.blochPointer = new THREE.Mesh(
+
+ new THREE.CylinderBufferGeometry( 0, 0.5, 1, 32, 1 ),
+ new THREE.MeshPhongMaterial({ color: BlochSphere.vectorColor })
+ )
+ this.blochPointer.geometry.translate( 0, -0.5, 0 )
+ this.blochPointer.geometry.rotateX( Math.PI / 2 )
+ this.blochPointer.geometry.scale( 0.2, 0.2, 0.2 )
+ this.blochPointer.lookAt( new THREE.Vector3() )
+ this.blochPointer.receiveShadow = true
+ this.blochPointer.castShadow = true
+ this.group.add( this.blochPointer )
+
+
+ // Create the Theta ring that will belt the sphere.
+
+ const
+ arcR = this.radiusSafe * Math.sin( Math.PI / 2 ),
+ arcH = this.radiusSafe * Math.cos( Math.PI / 2 ),
+ thetaGeometry = BlochSphere.createLatitudeArc( arcR, 128, Math.PI / 2, Math.PI * 2 ),
+ thetaLine = new MeshLine(),
+ thetaPhiMaterial = new MeshLineMaterial({
+
+ color: BlochSphere.thetaPhiColor,//0x505962,
+ lineWidth: this.arcLineWidth * 3,
+ sizeAttenuation: true
+ })
+
+ thetaGeometry.rotateX( Math.PI / 2 )
+ thetaGeometry.rotateY( Math.PI / 2 )
+ thetaGeometry.translate( 0, arcH, 0 )
+ thetaLine.setGeometry( thetaGeometry )
+
+ this.thetaMesh = new THREE.Mesh(
+
+ thetaLine.geometry,
+ thetaPhiMaterial
+ )
+ this.group.add( this.thetaMesh )
+
+
+ // Create the Phi arc that will draw from the north pole
+ // down to wherever the Theta arc rests.
+
+ this.phiGeometry = BlochSphere.createLongitudeArc( this.radiusSafe, 64, 0, Math.PI * 2 ),
+ this.phiLine = new MeshLine()
+ this.phiLine.setGeometry( this.phiGeometry )
+ this.phiMesh = new THREE.Mesh(
+
+ this.phiLine.geometry,
+ thetaPhiMaterial
+ )
+ this.group.add( this.phiMesh )
+
+
+
+
+ // Time to put plans to action.
+
+ BlochSphere.prototype.setTargetState.call( this )
+}
+
+
+
+
+
+
+ ////////////////
+ // //
+ // Static //
+ // //
+////////////////
+
+
+Object.assign( BlochSphere, {
+
+ xAxisColor: 0x333333,// Was 0xCF1717 (red)
+ yAxisColor: 0x333333,// Was 0x59A112 (green)
+ zAxisColor: 0x333333,// Was 0x0F66BD (blue)
+ vectorColor: 0xFFFFFF,// Was 0xF2B90D (yellow)
+ thetaPhiColor: 0x333333,// Was 0xF2B90D (yellow)
+
+
+ // It’s important that we build the texture
+ // right here and now, rather than load an image.
+ // Why? Because if we load a pre-existing image
+ // we run into CORS problems using file:/// !
+
+ makeSurface: function(){
+
+ const
+ width = 2048,
+ height = width / 2
+
+ const canvas = document.createElement( 'canvas' )
+ canvas.width = width
+ canvas.height = height
+
+ const context = canvas.getContext( '2d' )
+ context.fillStyle = 'hsl( 210, 20%, 100% )'
+ context.fillRect( 0, 0, width, height )
+
+
+ // Create the base hue gradient for our texture.
+
+ const
+ hueGradient = context.createLinearGradient( 0, height / 2, width, height / 2 ),
+ hueSteps = 180,
+ huesPerStep = 360 / hueSteps
+
+ for( let i = 0; i <= hueSteps; i ++ ){
+
+ hueGradient.addColorStop( i / hueSteps, 'hsl( '+ ( i * huesPerStep - 90 ) +', 100%, 50% )' )
+ }
+ context.fillStyle = hueGradient
+ context.fillRect( 0, 0, width, height )
+
+
+ // For both the northern gradient (to white)
+ // and the southern gradient (to black)
+ // we’ll leave a thin band of full saturation
+ // near the equator.
+
+ const whiteGradient = context.createLinearGradient( width / 2, 0, width / 2, height / 2 )
+ whiteGradient.addColorStop( 0.000, 'hsla( 0, 0%, 100%, 1 )' )
+ whiteGradient.addColorStop( 0.125, 'hsla( 0, 0%, 100%, 1 )' )
+ whiteGradient.addColorStop( 0.875, 'hsla( 0, 0%, 100%, 0 )' )
+ context.fillStyle = whiteGradient
+ context.fillRect( 0, 0, width, height / 2 )
+
+ const blackGradient = context.createLinearGradient( width / 2, height / 2, width / 2, height )
+ blackGradient.addColorStop( 0.125, 'hsla( 0, 0%, 0%, 0 )' )
+ blackGradient.addColorStop( 0.875, 'hsla( 0, 0%, 0%, 1 )' )
+ blackGradient.addColorStop( 1.000, 'hsla( 0, 0%, 0%, 1 )' )
+ context.fillStyle = blackGradient
+ context.fillRect( 0, height / 2, width, height )
+
+
+ // Create lines of latitude and longitude.
+ // Note this is an inverse Mercatur projection ;)
+
+ context.fillStyle = 'hsla( 0, 0%, 0%, 0.2 )'
+ const yStep = height / 16
+ for( let y = 0; y <= height; y += yStep ){
+
+ context.fillRect( 0, y, width, 1 )
+ }
+ const xStep = width / 16
+ for( let x = 0; x <= width; x += xStep ){
+
+ context.fillRect( x, 0, 1, height )
+ }
+
+
+ // Prepare the THREE texture and return it
+ // so we can use it as a material map.
+
+ const texture = new THREE.CanvasTexture( canvas )
+ texture.needsUpdate = true
+ return texture
+ },
+
+
+
+
+ createLongitudeArc: function( radius, segments, thetaStart, thetaLength ){
+
+ const geometry = new THREE.CircleGeometry( radius, segments, thetaStart, thetaLength )
+ geometry.vertices.shift()
+
+
+ // This is NOT NORMALLY necessary
+ // because we expect this to only be
+ // between PI/2 and PI*2
+ // (so the length is only Math.PI instead of PI*2).
+
+ if( thetaLength >= Math.PI * 2 ){
+
+ geometry.vertices.push( geometry.vertices[ 0 ].clone() )
+ }
+ return geometry
+ },
+ createLatitudeArc: function( radius, segments, phiStart, phiLength ){
+
+ const geometry = new THREE.CircleGeometry( radius, segments, phiStart, phiLength )
+ geometry.vertices.shift()
+ if( phiLength >= 2 * Math.PI ){
+
+ geometry.vertices.push( geometry.vertices[ 0 ].clone() )
+ }
+ return geometry
+ },
+ createQuadSphere: function( options ){
+
+ let {
+
+ radius,
+ phiStart,
+ phiLength,
+ thetaStart,
+ thetaLength,
+ latitudeLinesTotal,
+ longitudeLinesTotal,
+ latitudeLineSegments,
+ longitudeLineSegments,
+ latitudeLinesAttributes,
+ longitudeLinesAttributes
+
+ } = options
+
+ if( typeof radius !== 'number' ) radius = 1
+ if( typeof phiStart !== 'number' ) phiStart = Math.PI / 2
+ if( typeof phiLength !== 'number' ) phiLength = Math.PI * 2
+ if( typeof thetaStart !== 'number' ) thetaStart = 0
+ if( typeof thetaLength !== 'number' ) thetaLength = Math.PI
+ if( typeof latitudeLinesTotal !== 'number' ) latitudeLinesTotal = 16
+ if( typeof longitudeLinesTotal !== 'number' ) longitudeLinesTotal = 16
+ if( typeof latitudeLineSegments !== 'number' ) latitudeLineSegments = 64
+ if( typeof longitudeLineSegments !== 'number' ) longitudeLineSegments = 64
+ if( typeof latitudeLinesAttributes === 'undefined' ) latitudeLinesAttributes = { color: 0xCCCCCC }
+ if( typeof longitudeLinesAttributes === 'undefined' ) longitudeLinesAttributes = { color: 0xCCCCCC }
+
+ const
+ sphere = new THREE.Group(),
+ latitudeLinesMaterial = new THREE.LineBasicMaterial( latitudeLinesAttributes ),
+ longitudeLinesMaterial = new THREE.LineBasicMaterial( longitudeLinesAttributes )
+
+
+ // Lines of longitude.
+ // https://en.wikipedia.org/wiki/Longitude
+
+ for(
+
+ let
+ phiDelta = phiLength / longitudeLinesTotal,
+ phi = phiStart,
+ arc = BlochSphere.createLongitudeArc( radius, longitudeLineSegments, thetaStart + Math.PI / 2, thetaLength );
+ phi < phiStart + phiLength + phiDelta;
+ phi += phiDelta ){
+
+ const geometry = arc.clone()
+ geometry.rotateY( phi )
+ sphere.add( new THREE.Line( geometry, longitudeLinesMaterial ))
+ }
+
+
+ // Lines of latitude.
+ // https://en.wikipedia.org/wiki/Latitude
+
+ for (
+
+ let
+ thetaDelta = thetaLength / latitudeLinesTotal,
+ theta = thetaStart;
+ theta < thetaStart + thetaLength;
+ theta += thetaDelta ){
+
+ if( theta === 0 ) continue
+
+ const
+ arcR = radius * Math.sin( theta ),
+ arcH = radius * Math.cos( theta ),
+ geometry = BlochSphere.createLatitudeArc( arcR, latitudeLineSegments, phiStart, phiLength )
+
+ geometry.rotateX( Math.PI / 2 )
+ geometry.rotateY( Math.PI / 2 )
+ geometry.translate( 0, arcH, 0 )
+ sphere.add( new THREE.Line( geometry, latitudeLinesMaterial ))
+ }
+
+
+ return sphere
+ }
+})
+
+
+
+
+
+
+ ///////////////
+ // //
+ // Proto //
+ // //
+///////////////
+
+
+Object.assign( BlochSphere.prototype, {
+
+ update: function(){
+
+ if( this.isRotating ) this.group.rotation.y += Math.PI / 4096
+ },
+ setTargetState: function( target ){
+
+ if( target === undefined ) target = Qubit.HORIZONTAL.toBlochSphere()
+
+
+ // Always take the shortest path around
+ // even if it crosses the 0˚ / 360˚ boundary,
+ // ie. between Anti-Diagonal (-90˚) and
+ // Right0-and circular polarized (180˚).
+
+ const
+ rangeHalf = Math.PI,
+ distance = this.state.phi - target.phi
+
+ if( Math.abs( distance ) > rangeHalf ){
+
+ this.state.phi += Math.sign( distance ) * rangeHalf * -2
+ }
+
+
+ // Cheap hack to test if we need to update values
+ // from within the updateBlochVector method.
+
+ Object.assign( this.target, target )
+
+
+ // Create the tween.
+
+ window.tween = new TWEEN.Tween( this.state )
+ .to( target, 1000 )
+ .easing( TWEEN.Easing.Quadratic.InOut )
+ .onUpdate( this.updateBlochVector.bind( this ))
+ .start()
+ },
+ updateBlochVector: function( state ){
+
+
+ // Move the big-ass surface pointer.
+
+ if( state.theta !== this.target.theta ||
+ state.phi !== this.target.phi ){
+
+ this.blochPointer.position.set(
+
+ Math.sin( state.theta ) * Math.sin( state.phi ),
+ Math.cos( state.theta ),
+ Math.sin( state.theta ) * Math.cos( state.phi )
+ )
+ this.blochPointer.lookAt( new THREE.Vector3() )
+ this.blochVector.lookAt( this.blochPointer.getWorldPosition( new THREE.Vector3() ))
+
+
+ // Determine the correct HSL color
+ // based on Phi and Theta.
+
+ let hue = state.phi * THREE.Math.RAD2DEG
+ if( hue < 0 ) hue = 360 + hue
+ this.blochColor.setHSL(
+
+ hue / 360,
+ 1,
+ 1 - ( state.theta / Math.PI )
+ )
+ this.blochPointer.material.color = this.blochColor
+ this.blochVector.material.color = this.blochColor
+
+ if( state.theta !== this.target.theta ){
+
+
+ // Slide the Theta ring from the north pole
+ // down as far south as it needs to go
+ // and scale its radius so it belts the sphere.
+
+ const thetaScaleSafe = Math.max( state.theta, 0.01 )
+ this.thetaMesh.scale.set(
+
+ Math.sin( thetaScaleSafe ),
+ 1,
+ Math.sin( thetaScaleSafe )
+ )
+ this.thetaMesh.position.y = Math.cos( state.theta )
+
+
+ // Redraw the Phi arc to extend from the north pole
+ // down to only as far as the Theta ring sits.
+ // Then rotate the whole Phi arc about the poles.
+
+ for(
+
+ let
+ i = 0,
+ limit = this.phiGeometry.vertices.length;
+
+ i < limit;
+ i ++ ){
+
+ const gain = i / ( limit - 1 )
+ this.phiGeometry.vertices[ i ].set(
+
+ Math.sin( state.theta * gain ) * this.radiusSafe,
+ Math.cos( state.theta * gain ) * this.radiusSafe,
+ 0
+ )
+ }
+ this.phiLine.setGeometry( this.phiGeometry )
+ }
+ if( state.phi !== this.target.phi ){
+
+ this.phiMesh.rotation.y = state.phi - Math.PI / 2
+ }
+ if( typeof this.onValueChange === 'function' ) this.onValueChange.call( this )
+ }
+ }
+})
+
+
+
+module.exports = {BlochSphere}
+
+
+
diff --git a/packages/quantum-js-vis/Q-Circuit-Editor.css b/packages/quantum-js-vis/Q-Circuit-Editor.css
new file mode 100644
index 0000000..6a6a261
--- /dev/null
+++ b/packages/quantum-js-vis/Q-Circuit-Editor.css
@@ -0,0 +1,900 @@
+/*
+
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+
+*/
+@charset "utf-8";
+
+
+
+
+
+
+
+
+
+/*
+
+ Z indices:
+
+ Clipboard =100
+ Selected op 10
+ Operation 0
+ Shadow -10
+ Background -20
+
+
+
+
+
+ Circuit
+
+ Menu Moments
+ ╭───────┬───┬───┬───┬───╮
+ │ ≡ ↘ │ 1 │ 2 │ 3 │ + │ Add moment
+ ├───┬───┼───┼───┼───┼───╯
+ R │ 0 │|0⟩│ H │ C0│ X │ -
+ e ├───┼───┼───┼───┼───┤
+ g │ 1 │|0⟩│ I │ C1│ X │ -
+ s ├───┼───┴───┴───┴───┘
+ │ + │ - - - -
+ ╰───╯
+ Add
+ register
+
+
+ Circuit Palette
+
+ ╭───────────────────┬───╮
+ │ H X Y Z S T π M … │ @ │
+ ╰───────────────────┴───╯
+
+
+ Circuit clipboard
+
+ ┌───────────────┐
+ ▟│ ┌───┬───────┐ │
+ █│ │ H │ X#0.0 │ │
+ █│ ├───┼───────┤ │
+ █│ │ I │ X#0.1 │ │
+ █│ └───┴───────┘ │
+ █└───────────────┘
+ ███████████████▛
+
+
+
+ ◢◣
+ ◢■■■■◣
+◢■■■■■■■■◣
+◥■■■■■■■■◤
+ ◥■■■■◤
+ ◥◤
+
+
+ ◢■■■■■■◤
+ ◢◤ ◢◤
+◢■■■■■■◤
+
+
+ ───────────
+ ╲ ╱ ╱ ╱
+ ╳ ╱ ╱
+ ╱ ╲╱ ╱
+ ───────
+
+
+ ─────⦢
+ ╱ ╱
+⦣─────
+
+
+*/
+
+
+
+
+
+.Q-circuit,
+.Q-circuit-palette {
+
+ position: relative;
+ width: 100%;
+}
+.Q-circuit-palette {
+
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ line-height: 0;
+}
+.Q-circuit-palette > div {
+
+ display: inline-block;
+ position: relative;
+ width: 4rem;
+ height: 4rem;
+}
+
+
+.Q-circuit {
+
+ margin: 1rem 0 2rem 0;
+ /*border-top: 2px solid hsl( 0, 0%, 50% );*/
+}
+.Q-parameters-box,
+.Q-circuit-board-foreground {
+ line-height: 3.85rem;
+ width: auto;
+}
+
+
+
+
+
+
+ /***************/
+ /* */
+ /* Toolbar */
+ /* */
+/***************/
+
+
+.Q-circuit-toolbar {
+
+ position: relative;
+ display: block;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ margin-bottom: 0.5rem;
+
+ box-sizing: border-box;
+ display: grid;
+ grid-auto-columns: 3.6rem;
+ grid-auto-rows: 3.0rem;
+ grid-auto-flow: column;
+
+}
+.Q-circuit-button {
+
+ position: relative;
+ display: inline-block;
+ /*margin: 0 0.5rem 0.5rem 0;*/
+ width: 3.6rem;
+ height: 3rem;
+/* box-shadow:
+ -0.1rem -0.1rem 0 rgba( 255, 255, 255, 0.8 ),
+ 0.1rem 0.1rem 0.1rem rgba( 0, 0, 0, 0.35 );*/
+
+ border-top: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 100%
+ );
+ border-right: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 90%
+ );
+ border-bottom: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 85%
+ );
+ border-left: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 97%
+ );
+ background: var( --Q-color-background );
+/* background:
+ var( --Q-color-background )
+ linear-gradient(
+
+ 0.4turn,
+
+ rgba( 0, 0, 0, 0.02 ),
+ rgba( 255, 255, 255, 0.1 )
+ );*/
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 30%
+ );
+ text-shadow: 1px 1px 0 rgba( 255, 255, 255, 1 );
+ /*border-radius: 0.5rem;*/
+ /*border-radius: 100%;*/
+ line-height: 2.9rem;
+ text-align: center;
+ cursor: pointer;
+ overflow: hidden;
+ font-weight: 900;
+}
+.Q-circuit-toolbar .Q-circuit-button:first-child {
+
+ border-top-left-radius: 0.5rem;
+ border-bottom-left-radius: 0.5rem;
+}
+.Q-circuit-toolbar .Q-circuit-button:last-child {
+
+ border-top-right-radius: 0.5rem;
+ border-bottom-right-radius: 0.5rem;
+}
+.Q-circuit-locked .Q-circuit-button,
+.Q-circuit-button[Q-disabled] {
+
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 85%
+ );
+ cursor: not-allowed;
+}
+.Q-circuit-locked .Q-circuit-toggle-lock {
+
+ color: inherit;
+ cursor: pointer;
+}
+
+
+
+
+.Q-circuit-board-container {
+
+ position: relative;
+ margin: 0 0 2rem 0;
+ margin: 0;
+ width: 100%;
+ max-height: 60vh;
+ overflow: scroll;
+}
+.Q-circuit-board {
+
+ position: relative;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+/*.Q-circuit-palette,*/
+.Q-circuit-board-foreground,
+.Q-circuit-board-background,
+.Q-circuit-clipboard {
+
+ box-sizing: border-box;
+ display: grid;
+ grid-auto-rows: 4rem;
+ grid-auto-columns: 4rem;
+ grid-auto-flow: column;
+}
+
+.Q-parameters-box {
+
+ position: absolute;
+ display: none;
+ z-index: 100;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: whitesmoke;
+}
+
+/*.Q-circuit-palette,*/
+.Q-circuit-board-foreground,
+.Q-circuit-board-background {
+
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.Q-circuit-clipboard {
+
+ position: absolute;
+ z-index: 100;
+ min-width: 4rem;
+ min-height: 4rem;
+ transform: scale( 1.05 );
+}
+.Q-circuit-clipboard, .Q-circuit-clipboard > div {
+
+ cursor: grabbing;
+}
+.Q-circuit-clipboard-danger .Q-circuit-operation {
+
+ background-color: var( --Q-color-yellow );
+}
+.Q-circuit-clipboard-destroy {
+
+ animation-name: Q-circuit-clipboard-poof;
+ animation-fill-mode: forwards;
+ animation-duration: 0.3s;
+ animation-iteration-count: 1;
+}
+@keyframes Q-circuit-clipboard-poof {
+
+ 100% {
+
+ transform: scale( 1.5 );
+ opacity: 0;
+ }
+}
+.Q-circuit-board-background {
+
+ /*
+
+ Clipboard: 100
+ Operation: 0
+ Shadow: -10
+ Background: -20
+
+ */
+ position: absolute;
+ z-index: -20;
+ color: rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-board-background > div {
+
+/* transition:
+ background-color 0.2s,
+ color 0.2s;*/
+}
+.Q-circuit-board-background .Q-circuit-cell-highlighted {
+
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+ /*transition: none;*/
+}
+
+
+
+
+.Q-circuit-register-wire {
+
+ position: absolute;
+ top: calc( 50% - 0.5px );
+ width: 100%;
+ height: 1px;
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 50%
+ );
+}
+
+.Q-parameter-box-exit {
+ position: relative;
+ right: 0;
+ left: 0;
+ width: 5rem;
+ height: 2.5rem;
+ background-color: whitesmoke;
+}
+
+.Q-parameters-box > div,
+.Q-circuit-palette > div,
+.Q-circuit-clipboard > div,
+.Q-circuit-board-foreground > div {
+
+ text-align: center;
+}
+
+
+
+
+
+
+ /***************/
+ /* */
+ /* Headers */
+ /* */
+/***************/
+
+
+.Q-circuit-header {
+
+ position: sticky;
+ z-index: 2;
+ margin: 0;
+ /*background-color: var( --Q-color-background );*/
+ background-color: white;
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 75%
+ );
+ font-family: var( --Q-font-family-mono );
+}
+.Q-circuit-input.Q-circuit-cell-highlighted,
+.Q-circuit-header.Q-circuit-cell-highlighted {
+
+ background-color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+ color: black;
+}
+.Q-circuit-selectall {
+
+ z-index: 3;
+ margin: 0;
+ top: 0;
+ /*left: 4rem;*/
+ /*grid-column: 2;*/
+ left: 0;
+ grid-column-start: 1;
+ grid-column-end: 3;
+ grid-row: 1;
+ cursor: se-resize;
+}
+.Q-circuit-moment-label,
+.Q-circuit-moment-add {
+
+ grid-row: 1;
+ top: 0;
+ cursor: s-resize;
+}
+.Q-circuit-register-label,
+.Q-circuit-register-add {
+
+ grid-column: 2;
+ left: 4rem;
+ cursor: e-resize;
+}
+.Q-circuit-moment-add,
+.Q-circuit-register-add {
+
+ cursor: pointer;
+}
+.Q-circuit-moment-add,
+.Q-circuit-register-add {
+
+ display: none;
+}
+.Q-circuit-selectall,
+.Q-circuit-moment-label,
+.Q-circuit-moment-add {
+
+ border-bottom: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+}
+.Q-circuit-selectall,
+.Q-circuit-register-label,
+.Q-circuit-register-add {
+
+ border-right: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 95%
+ );
+}
+.Q-circuit-input {
+
+ position: sticky;
+ z-index: 2;
+ grid-column: 1;
+ left: 0;
+ /*background-color: var( --Q-color-background );*/
+ background-color: white;
+ font-size: 1.5rem;
+ font-weight: 900;
+ font-family: var( --Q-font-family-mono );
+}
+
+
+
+
+
+
+.Q-circuit-operation-link-container {
+
+ --Q-link-stroke: 3px;
+ --Q-link-radius: 100%;
+
+ display: block;
+ position: relative;
+ left: calc( 50% - ( var( --Q-link-stroke ) / 2 ));
+ width: 50%;
+ height: 100%;
+ overflow: hidden;
+}
+.Q-circuit-operation-link-container.Q-circuit-cell-highlighted {
+
+ background-color: transparent;
+}
+.Q-circuit-operation-link {
+
+ display: block;
+ position: absolute;
+ width: calc( var( --Q-link-stroke ) * 2 );
+ height: calc( 100% - 4rem + var( --Q-link-stroke ));
+ /*border: var( --Q-link-stroke ) solid hsl( 0, 0%, 50% );*/
+ border: var( --Q-link-stroke ) solid hsl(
+
+ var( --Q-color-background-hue ),
+ 10%,
+ 30%
+ );
+
+ /*border: var( --Q-link-stroke ) solid var( --Q-color-orange );*/
+
+ transform: translate( -50%, calc( 2rem - ( var( --Q-link-stroke ) / 2 )));
+ transform-origin: center;
+}
+.Q-circuit-operation-link.Q-circuit-operation-link-curved {
+
+ width: calc( var( --Q-link-radius ) - var( --Q-link-stroke ));
+ width: 200%;
+ border-radius: 100%;
+}
+
+
+
+
+
+
+ /******************/
+ /* */
+ /* Operations */
+ /* */
+/******************/
+
+.Q-circuit-operation {
+
+ position: relative;
+ /*--Q-operation-color-hue: var( --Q-color-green-hue );
+ --Q-operation-color-main: var( --Q-color-green );*/
+
+ --Q-operation-color-hue: var( --Q-color-blue-hue );
+ --Q-operation-color-main: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 35%
+ );
+
+ --Q-operation-color-light: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 50%
+ );
+ --Q-operation-color-dark: hsl(
+
+ var( --Q-operation-color-hue ),
+ 10%,
+ 25%
+ );
+ color: white;
+ text-shadow: -0.05rem -0.05rem 0 rgba( 0, 0, 0, 0.1 );
+ font-size: 1.5rem;
+ line-height: 2.9rem;
+ font-weight: 900;
+ cursor: grab;
+}
+.Q-circuit-locked .Q-circuit-operation {
+
+ cursor: not-allowed;
+}
+.Q-circuit-operation-tile {
+
+ position: absolute;
+ top: 0.5rem;
+ left: 0.5rem;
+ right: 0.5rem;
+ bottom: 0.5rem;
+
+ /*margin: 0.5rem;*/
+ /*padding: 0.5rem;*/
+
+ /*box-shadow: 0.1rem 0.1rem 0.2rem rgba( 0, 0, 0, 0.2 );*/
+ border-radius: 0.2rem;
+ /*
+ border-top: 0.1rem solid var( --Q-operation-color-light );
+ border-left: 0.1rem solid var( --Q-operation-color-light );
+ border-right: 0.1rem solid var( --Q-operation-color-dark );
+ border-bottom: 0.1rem solid var( --Q-operation-color-dark );
+ */
+ background:
+ var( --Q-operation-color-main )
+ /*linear-gradient(
+
+ 0.45turn,
+ rgba( 255, 255, 255, 0.1 ),
+ rgba( 0, 0, 0, 0.05 )
+ )*/;
+}
+.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover {
+
+ /*background-color: rgba( 255, 255, 255, 0.6 );*/
+ background-color: white;
+}
+.Q-circuit-palette .Q-circuit-operation-tile {
+
+ --Q-before-rotation: 12deg;
+ --Q-before-x: 1px;
+ --Q-before-y: -2px;
+
+ --Q-after-rotation: -7deg;
+ --Q-after-x: -2px;
+ --Q-after-y: 3px;
+
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-palette .Q-circuit-operation-tile:before,
+.Q-circuit-palette .Q-circuit-operation-tile:after {
+
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ border-radius: 0.2rem;
+ /*background-color: hsl( 0, 0%, 60% );*/
+
+ background-color: var( --Q-operation-color-dark );
+ transform:
+ translate( var( --Q-before-x ), var( --Q-before-y ))
+ rotate( var( --Q-before-rotation ));
+ z-index: -10;
+ /*z-index: 10;*/
+ display: block;
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-palette .Q-circuit-operation-tile:after {
+
+ transform:
+ translate( var( --Q-after-x ), var( --Q-after-y ))
+ rotate( var( --Q-after-rotation ));
+ box-shadow: 0.2rem 0.2rem 0.2rem rgba( 0, 0, 0, 0.2 );
+}
+.Q-circuit-operation:hover .Q-circuit-operation-tile {
+
+ color: white;
+}
+
+
+
+
+.Q-circuit-operation-hadamard .Q-circuit-operation-tile {
+
+ /*--Q-operation-color-hue: var( --Q-color-red-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-red );*/
+
+ /*--Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 10% );*/
+
+
+/* background:
+ linear-gradient(
+
+ -33deg,
+ var( --Q-color-blue ) 20%,
+ #6f3c69 50%,
+ var( --Q-color-red ) 80%
+ );*/
+}
+.Q-circuit-operation-identity .Q-circuit-operation-tile,
+.Q-circuit-operation-control .Q-circuit-operation-tile,
+.Q-circuit-operation-target .Q-circuit-operation-tile {
+
+ /*--Q-operation-color-hue: var( --Q-color-orange-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-orange );*/
+ border-radius: 100%;
+}
+.Q-circuit-operation-identity .Q-circuit-operation-tile,
+.Q-circuit-operation-control .Q-circuit-operation-tile {
+
+ top: calc( 50% - 0.7rem );
+ left: calc( 50% - 0.7rem );
+ width: 1.4rem;
+ height: 1.4rem;
+ overflow: hidden;
+/* --Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 10% );*/
+}
+.Q-circuit-operation-pauli-x,
+.Q-circuit-operation-pauli-y,
+.Q-circuit-operation-pauli-z {
+
+ /*--Q-operation-color-hue: var( --Q-color-red-hue );*/
+ /*--Q-operation-color-main: var( --Q-color-red );*/
+
+/* --Q-operation-color-hue: 0;
+ --Q-operation-color-main: hsl( 0, 0%, 30% );*/
+}
+.Q-circuit-operation-swap .Q-circuit-operation-tile {
+
+ top: calc( 50% - 0.55rem );
+ left: calc( 50% - 0.55rem );
+ width: 1.2rem;
+ height: 1.2rem;
+ border-radius: 0;
+ transform-origin: center;
+ transform: rotate( 45deg );
+ font-size: 0;
+}
+
+.Q-parameter-box-input-container {
+ position: relative;
+ text-align: center;
+ grid-auto-columns: 4rem;
+ grid-auto-flow: column;
+}
+
+.Q-parameter-box-input {
+ position: relative;
+ border-radius: .2rem;
+ margin-left: 10px;
+ font-family: var( --Q-font-family-mono );
+}
+
+.Q-parameter-input-label {
+ position: relative;
+ color: var( --Q-color-blue );
+ font-family: var( --Q-font-family-mono );
+}
+
+
+
+
+ /********************/
+ /* */
+ /* Other states */
+ /* */
+/********************/
+
+
+.Q-circuit-palette > div:hover,
+.Q-circuit-board-foreground > div:hover {
+
+ outline: 2px solid var( --Q-hyperlink-internal-color );
+ outline-offset: -2px;
+}
+.Q-circuit-palette > div:hover .Q-circuit-operation-tile {
+
+ box-shadow: none;
+}
+/*.Q-circuit-palette > div:hover,*/
+.Q-circuit-board-foreground > div:hover {
+
+ background-color: white;
+ color: black;
+}
+
+
+
+
+
+
+.Q-circuit-clipboard > div,
+.Q-circuit-cell-selected {
+
+ background-color: white;
+}
+.Q-circuit-clipboard > div:before,
+.Q-circuit-cell-selected:before {
+
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ z-index: -10;
+ box-shadow:
+ 0 0 1rem rgba( 0, 0, 0, 0.2 ),
+ 0.4rem 0.4rem 0.2rem rgba( 0, 0, 0, 0.2 );
+ outline: 1px solid hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 50%
+ );
+ /*outline-offset: -1px;*/
+}
+
+
+
+
+.Q-circuit-clipboard > div {
+
+ background-color: white;
+}
+.Q-circuit-clipboard > div:before {
+
+ /*
+
+ This was very helpful!
+ https://blog.dudak.me/2014/css-shadows-under-adjacent-elements/
+
+ */
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: -10;
+ display: block;
+ box-shadow: 0.4rem 0.4rem 0.3rem rgba( 0, 0, 0, 0.2 );
+}
+
+
+
+
+
+
+
+ /***************/
+ /* */
+ /* Buttons */
+ /* */
+/***************/
+
+
+.Q-circuit-locked .Q-circuit-toggle-lock,
+.Q-circuit-locked .Q-circuit-toggle-lock:hover {
+
+ background-color: var( --Q-color-red );
+}
+.Q-circuit-toggle-lock {
+
+ z-index: 3;
+ left: 0;
+ top: 0;
+ grid-column: 1;
+ grid-row: 1;
+ cursor: pointer;
+ font-size: 1.1rem;
+ text-shadow: none;
+ font-weight: normal;
+}
+.Q-circuit-button-undo,
+.Q-circuit-button-redo {
+
+ font-size: 1.2rem;
+ line-height: 2.6rem;
+ font-weight: normal;
+}
+
+
+
+.Q-circuit p {
+
+ padding: 1rem;
+ color: hsl(
+
+ var( --Q-color-background-hue ),
+ var( --Q-color-background-saturation ),
+ 66%
+ );
+}
+
+
+
diff --git a/packages/quantum-js-vis/Q-Circuit-Editor.js b/packages/quantum-js-vis/Q-Circuit-Editor.js
new file mode 100644
index 0000000..7c225a6
--- /dev/null
+++ b/packages/quantum-js-vis/Q-Circuit-Editor.js
@@ -0,0 +1,2281 @@
+// Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+const {Q, Circuit, Gate, logger, misc, mathf } = require('quantum-js-util');
+Editor = function( circuit, targetEl ){
+ // First order of business,
+ // we require a valid circuit.
+
+ if( circuit instanceof Circuit !== true ) circuit = new Circuit()
+ this.circuit = circuit
+ this.index = Editor.index ++
+
+
+ // Editor is all about the DOM
+ // so we’re going to get some use out of this
+ // stupid (but convenient) shorthand here.
+
+ const createDiv = function(){
+
+ return document.createElement( 'div' )
+ }
+
+
+
+
+
+
+ // We want to “name” our circuit editor instance
+ // but more importantly we want to give it a unique DOM ID.
+ // Keep in mind we can have MULTIPLE editors
+ // for the SAME circuit!
+ // This is a verbose way to do it,
+ // but each step is clear and I needed clarity today! ;)
+
+ this.name = typeof circuit.name === 'string' ?
+ circuit.name :
+ 'Q Editor '+ this.index
+
+
+ // If we’ve been passed a target DOM element
+ // we should use that as our circuit element.
+
+ if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl )
+ const circuitEl = targetEl instanceof HTMLElement ? targetEl : createDiv()
+ circuitEl.classList.add( 'Q-circuit' )
+
+
+ // If the target element already has an ID
+ // then we want to use that as our domID.
+
+ if( typeof circuitEl.getAttribute( 'id' ) === 'string' ){
+
+ this.domId = circuitEl.getAttribute( 'id' )
+ }
+
+
+ // Otherwise let’s transform our name value
+ // into a usable domId.
+
+ else {
+
+ let domIdBase = this.name
+ .replace( /^[^a-z]+|[^\w:.-]+/gi, '-' ),
+ domId = domIdBase,
+ domIdAttempt = 1
+
+ while( document.getElementById( domId ) !== null ){
+
+ domIdAttempt ++
+ domId = domIdBase +'-'+ domIdAttempt
+ }
+ this.domId = domId
+ circuitEl.setAttribute( 'id', this.domId )
+ }
+
+
+
+
+ // We want a way to easily get to the circuit
+ // from this interface’s DOM element.
+ // (But we don’t need a way to reference this DOM element
+ // from the circuit. A circuit can have many DOM elements!)
+ // And we also want an easy way to reference this DOM element
+ // from this Editor instance.
+
+ circuitEl.circuit = circuit
+ this.domElement = circuitEl
+
+
+ // Create a toolbar for containing buttons.
+
+ const toolbarEl = createDiv()
+ circuitEl.appendChild( toolbarEl )
+ toolbarEl.classList.add( 'Q-circuit-toolbar' )
+
+
+ // Create a toggle switch for locking the circuit.
+
+ const lockToggle = createDiv()
+ toolbarEl.appendChild( lockToggle )
+ lockToggle.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-lock' )
+ lockToggle.setAttribute( 'title', 'Lock / unlock' )
+ lockToggle.innerText = '🔓'
+
+
+ // Create an “Undo” button
+ // that enables and disables
+ // based on available undo history.
+
+ const undoButton = createDiv()
+ toolbarEl.appendChild( undoButton )
+ undoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-undo' )
+ undoButton.setAttribute( 'title', 'Undo' )
+ undoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ undoButton.innerHTML = '⟲'
+ window.addEventListener( 'History undo is depleted', function( event ){
+
+ if( event.detail.instance === circuit )
+ undoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ })
+ window.addEventListener( 'History undo is capable', function( event ){
+
+ if( event.detail.instance === circuit )
+ undoButton.removeAttribute( 'Q-disabled' )
+ })
+
+
+ // Create an “Redo” button
+ // that enables and disables
+ // based on available redo history.
+
+ const redoButton = createDiv()
+ toolbarEl.appendChild( redoButton )
+ redoButton.classList.add( 'Q-circuit-button', 'Q-circuit-button-redo' )
+ redoButton.setAttribute( 'title', 'Redo' )
+ redoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ redoButton.innerHTML = '⟳'
+ window.addEventListener( 'History redo is depleted', function( event ){
+
+ if( event.detail.instance === circuit )
+ redoButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ })
+ window.addEventListener( 'History redo is capable', function( event ){
+
+ if( event.detail.instance === circuit )
+ redoButton.removeAttribute( 'Q-disabled' )
+ })
+
+
+ // Create a button for joining
+ // an “identity cursor”
+ // and one or more same-gate operations
+ // into a controlled operation.
+ // (Will be enabled / disabled from elsewhere.)
+
+ const controlButton = createDiv()
+ toolbarEl.appendChild( controlButton )
+ controlButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle', 'Q-circuit-toggle-control' )
+ controlButton.setAttribute( 'title', 'Create controlled operation' )
+ controlButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ controlButton.innerText = 'C'
+
+
+ // Create a button for joining
+ // two “identity cursors”
+ // into a swap operation.
+ // (Will be enabled / disabled from elsewhere.)
+
+ const swapButton = createDiv()
+ toolbarEl.appendChild( swapButton )
+ swapButton.classList.add( 'Q-circuit-button', 'Q-circuit-toggle-swap' )
+ swapButton.setAttribute( 'title', 'Create swap operation' )
+ swapButton.setAttribute( 'Q-disabled', 'Q-disabled' )
+ swapButton.innerText = 'S'
+
+
+ // Create a circuit board container
+ // so we can house a scrollable circuit board.
+
+ const boardContainerEl = createDiv()
+ circuitEl.appendChild( boardContainerEl )
+ boardContainerEl.classList.add( 'Q-circuit-board-container' )
+ //boardContainerEl.addEventListener( 'touchstart', Editor.onPointerPress )
+ boardContainerEl.addEventListener( 'mouseleave', function(){
+ Editor.unhighlightAll( circuitEl )
+ })
+
+ const boardEl = createDiv()
+ boardContainerEl.appendChild( boardEl )
+ boardEl.classList.add( 'Q-circuit-board' )
+
+ const backgroundEl = createDiv()
+ boardEl.appendChild( backgroundEl )
+ backgroundEl.classList.add( 'Q-circuit-board-background' )
+
+ const parameterEl = createDiv()
+ boardEl.appendChild( parameterEl )
+ parameterEl.classList.add( 'Q-parameters-box' )
+ // Create background highlight bars
+ // for each row.
+
+ for( let i = 0; i < circuit.bandwidth; i ++ ){
+
+ const rowEl = createDiv()
+ backgroundEl.appendChild( rowEl )
+ rowEl.style.position = 'relative'
+ rowEl.style.gridRowStart = i + 2
+ rowEl.style.gridColumnStart = 1
+ rowEl.style.gridColumnEnd = Editor.momentIndexToGridColumn( circuit.timewidth ) + 1
+ rowEl.setAttribute( 'register-index', i + 1 )
+
+ const wireEl = createDiv()
+ rowEl.appendChild( wireEl )
+ wireEl.classList.add( 'Q-circuit-register-wire' )
+ }
+
+
+ // Create background highlight bars
+ // for each column.
+
+ for( let i = 0; i < circuit.timewidth; i ++ ){
+
+ const columnEl = createDiv()
+ backgroundEl.appendChild( columnEl )
+ columnEl.style.gridRowStart = 2
+ columnEl.style.gridRowEnd = Editor.registerIndexToGridRow( circuit.bandwidth ) + 1
+ columnEl.style.gridColumnStart = i + 3
+ columnEl.setAttribute( 'moment-index', i + 1 )
+ }
+
+
+ // Create the circuit board foreground
+ // for all interactive elements.
+
+ const foregroundEl = createDiv()
+ boardEl.appendChild( foregroundEl )
+ foregroundEl.classList.add( 'Q-circuit-board-foreground' )
+
+
+ // Add “Select All” toggle button to upper-left corner.
+
+ const selectallEl = createDiv()
+ foregroundEl.appendChild( selectallEl )
+ selectallEl.classList.add( 'Q-circuit-header', 'Q-circuit-selectall' )
+ selectallEl.setAttribute( 'title', 'Select all' )
+ selectallEl.setAttribute( 'moment-index', '0' )
+ selectallEl.setAttribute( 'register-index', '0' )
+ selectallEl.innerHTML = '↘'
+
+
+ // Add register index symbols to left-hand column.
+
+ for( let i = 0; i < circuit.bandwidth; i ++ ){
+
+ const
+ registerIndex = i + 1,
+ registersymbolEl = createDiv()
+
+ foregroundEl.appendChild( registersymbolEl )
+ registersymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-label' )
+ registersymbolEl.setAttribute( 'title', 'Register '+ registerIndex +' of '+ circuit.bandwidth )
+ registersymbolEl.setAttribute( 'register-index', registerIndex )
+ registersymbolEl.style.gridRowStart = Editor.registerIndexToGridRow( registerIndex )
+ registersymbolEl.innerText = registerIndex
+ }
+
+
+ // Add “Add register” button.q
+
+ const addRegisterEl = createDiv()
+ foregroundEl.appendChild( addRegisterEl )
+ addRegisterEl.classList.add( 'Q-circuit-header', 'Q-circuit-register-add' )
+ addRegisterEl.setAttribute( 'title', 'Add register' )
+ addRegisterEl.style.gridRowStart = Editor.registerIndexToGridRow( circuit.bandwidth + 1 )
+ addRegisterEl.innerText = '+'
+
+
+ // Add moment index symbols to top row.
+
+ for( let i = 0; i < circuit.timewidth; i ++ ){
+
+ const
+ momentIndex = i + 1,
+ momentsymbolEl = createDiv()
+
+ foregroundEl.appendChild( momentsymbolEl )
+ momentsymbolEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-label' )
+ momentsymbolEl.setAttribute( 'title', 'Moment '+ momentIndex +' of '+ circuit.timewidth )
+ momentsymbolEl.setAttribute( 'moment-index', momentIndex )
+ momentsymbolEl.style.gridColumnStart = Editor.momentIndexToGridColumn( momentIndex )
+ momentsymbolEl.innerText = momentIndex
+ }
+
+
+ // Add “Add moment” button.
+
+ const addMomentEl = createDiv()
+ foregroundEl.appendChild( addMomentEl )
+ addMomentEl.classList.add( 'Q-circuit-header', 'Q-circuit-moment-add' )
+ addMomentEl.setAttribute( 'title', 'Add moment' )
+ addMomentEl.style.gridColumnStart = Editor.momentIndexToGridColumn( circuit.timewidth + 1 )
+ addMomentEl.innerText = '+'
+
+
+ // Add input values.
+
+ circuit.qubits.forEach( function( qubit, i ){
+
+ const
+ rowIndex = i + 1,
+ inputEl = createDiv()
+
+ inputEl.classList.add( 'Q-circuit-header', 'Q-circuit-input' )
+ inputEl.setAttribute( 'title', `Qubit #${ rowIndex } starting value` )
+ inputEl.setAttribute( 'register-index', rowIndex )
+ inputEl.style.gridRowStart = Editor.registerIndexToGridRow( rowIndex )
+ inputEl.innerText = qubit.beta.toText()
+ foregroundEl.appendChild( inputEl )
+ })
+
+
+ // Add operations.
+
+ circuit.operations.forEach( function( operation ){
+ Editor.set( circuitEl, operation )
+ })
+
+
+ // Add event listeners.
+
+ circuitEl.addEventListener( 'mousedown', Editor.onPointerPress )
+ circuitEl.addEventListener( 'touchstart', Editor.onPointerPress )
+ window.addEventListener(
+
+ 'Circuit.set$',
+ Editor.prototype.onExternalSet.bind( this )
+ )
+ window.addEventListener(
+
+ 'Circuit.clear$',
+ Editor.prototype.onExternalClear.bind( this )
+ )
+
+
+ // How can we interact with this circuit
+ // through code? (How cool is this?!)
+
+ const referenceEl = document.createElement( 'p' )
+ circuitEl.appendChild( referenceEl )
+ referenceEl.innerHTML = `
+ This circuit is accessible in your
+ JavaScript console
+ as document.getElementById('${ this.domId }').circuit`
+ //document.getElementById('Q-Editor-0').circuit
+ //$('#${ this.domId }')
+
+
+ // Put a note in the JavaScript console
+ // that includes how to reference the circuit via code
+ // and an ASCII diagram for reference.
+
+ logger.warn( 0.5,
+ `\n\nCreated a DOM interface for $('#${ this.domId }').circuit\n\n`,
+ circuit.toDiagram(),
+ '\n\n\n'
+ )
+}
+
+
+// Augment Circuit to have this functionality.
+
+Circuit.toDom = function( circuit, targetEl ){
+
+ return new Editor( circuit, targetEl ).domElement
+}
+Circuit.prototype.toDom = function( targetEl ){
+
+ return new Editor( this, targetEl ).domElement
+}
+
+
+
+
+
+
+
+
+Object.assign( Editor, {
+
+ index: 0,
+ help: function(){ return logger.help( this )},
+ dragEl: null,
+ gridColumnToMomentIndex: function( gridColumn ){ return +gridColumn - 2 },
+ momentIndexToGridColumn: function( momentIndex ){ return momentIndex + 2 },
+ gridRowToRegisterIndex: function( gridRow ){ return +gridRow - 1 },
+ registerIndexToGridRow: function( registerIndex ){ return registerIndex + 1 },
+ gridSize: 4,// CSS: grid-auto-columns = grid-auto-rows = 4rem.
+ pointToGrid: function( p ){
+
+
+ // Take a 1-dimensional point value
+ // (so either an X or a Y but not both)
+ // and return what CSS grid cell contains it
+ // based on our 4rem × 4rem grid setup.
+
+ const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize )
+ return 1 + Math.floor( p / ( rem * Editor.gridSize ))
+ },
+ gridToPoint: function( g ){
+
+
+ // Take a 1-dimensional grid cell value
+ // (so either a row or a column but not both)
+ // and return the minimum point value it contains.
+
+ const rem = parseFloat( getComputedStyle( document.documentElement ).fontSize )
+ return rem * Editor.gridSize * ( g - 1 )
+ },
+ getInteractionCoordinates: function( event, pageOrClient ){
+
+ if( typeof pageOrClient !== 'string' ) pageOrClient = 'client'//page
+ if( event.changedTouches &&
+ event.changedTouches.length ) return {
+
+ x: event.changedTouches[ 0 ][ pageOrClient +'X' ],
+ y: event.changedTouches[ 0 ][ pageOrClient +'Y' ]
+ }
+ return {
+ x: event[ pageOrClient +'X' ],
+ y: event[ pageOrClient +'Y' ]
+ }
+ },
+ createNewElement :function(element_type, element_parent, element_css) {
+ element = document.createElement(element_type)
+ if(element_css) element.classList.add(element_css)
+ if(element_parent) element_parent.appendChild( element )
+ return element
+ },
+ createPalette: function( targetEl ){
+
+ if( typeof targetEl === 'string' ) targetEl = document.getElementById( targetEl )
+
+ const
+ paletteEl = targetEl instanceof HTMLElement ? targetEl : document.createElement( 'div' ),
+ randomRangeAndSign = function( min, max ){
+
+ const r = min + Math.random() * ( max - min )
+ return Math.floor( Math.random() * 2 ) ? r : -r
+ }
+
+ //ltnln: added missing Braket operations.
+ paletteEl.classList.add( 'Q-circuit-palette' );
+ 'H,X,Y,Z,P,Rx,Ry,Rz,U,V,V†,S*,S†,T,T†,00,01,10,√S,iS,XX,XY,YY,ZZ,*'
+ .split( ',' )
+ .forEach( function( symbol ){
+
+ const gate = Gate.findBySymbol( symbol )
+
+ const operationEl = document.createElement( 'div' )
+ paletteEl.appendChild( operationEl )
+ operationEl.classList.add( 'Q-circuit-operation' )
+ operationEl.classList.add( 'Q-circuit-operation-'+ gate.nameCss )
+ operationEl.setAttribute( 'gate-symbol', symbol )
+ operationEl.setAttribute( 'title', gate.name )
+
+ const tileEl = document.createElement( 'div' )
+ operationEl.appendChild( tileEl )
+ tileEl.classList.add( 'Q-circuit-operation-tile' )
+ if( symbol !== Gate.CURSOR.symbol ) tileEl.innerText = symbol
+
+ ;[ 'before', 'after' ].forEach( function( layer ){
+
+ tileEl.style.setProperty( '--Q-'+ layer +'-rotation', randomRangeAndSign( 0.5, 4 ) +'deg' )
+ tileEl.style.setProperty( '--Q-'+ layer +'-x', randomRangeAndSign( 1, 4 ) +'px' )
+ tileEl.style.setProperty( '--Q-'+ layer +'-y', randomRangeAndSign( 1, 3 ) +'px' )
+ })
+ })
+
+ paletteEl.addEventListener( 'mousedown', Editor.onPointerPress )
+ paletteEl.addEventListener( 'touchstart', Editor.onPointerPress )
+ return paletteEl
+ },
+ toDom: function( circuit, targetEl ){
+
+ return new Editor( circuit, targetEl ).domElement
+ }
+})
+
+
+
+
+
+
+ /////////////////////////
+ // //
+ // Operation CLEAR //
+ // //
+/////////////////////////
+
+
+Editor.prototype.onExternalClear = function( event ){
+
+ if( event.detail.circuit === this.circuit ){
+
+ Editor.clear( this.domElement, {
+
+ momentIndex: event.detail.momentIndex,
+ registerIndices: event.detail.registerIndices
+ })
+ }
+}
+Editor.clear = function( circuitEl, operation ){
+
+ const momentIndex = operation.momentIndex
+ operation.registerIndices.forEach( function( registerIndex ){
+
+ Array
+ .from( circuitEl.querySelectorAll(
+
+ `[moment-index="${ momentIndex }"]`+
+ `[register-index="${ registerIndex }"]`
+
+ ))
+ .forEach( function( op ){
+
+ op.parentNode.removeChild( op )
+ })
+ })
+}
+
+
+
+
+
+
+ ///////////////////////
+ // //
+ // Operation SET //
+ // //
+///////////////////////
+
+
+Editor.prototype.onExternalSet = function( event ){
+
+ if( event.detail.circuit === this.circuit ){
+
+ Editor.set( this.domElement, event.detail.operation )
+ }
+}
+Editor.set = function( circuitEl, operation ){
+ const
+ backgroundEl = circuitEl.querySelector( '.Q-circuit-board-background' ),
+ foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
+ circuit = circuitEl.circuit,
+ operationIndex = circuitEl.circuit.operations.indexOf( operation )
+
+ operation.registerIndices.forEach( function( registerIndex, i ){
+ const operationEl = document.createElement( 'div' )
+ foregroundEl.appendChild( operationEl )
+ operationEl.classList.add( 'Q-circuit-operation', 'Q-circuit-operation-'+ operation.gate.nameCss )
+ // operationEl.setAttribute( 'operation-index', operationIndex )
+ operationEl.setAttribute( 'gate-symbol', operation.gate.symbol )
+ operationEl.setAttribute( 'gate-index', operation.gate.index )// Used as an application-wide unique ID!
+ operationEl.setAttribute( 'moment-index', operation.momentIndex )
+ operationEl.setAttribute( 'register-index', registerIndex )
+ operationEl.setAttribute( 'register-array-index', i )// Where within the registerIndices array is this operations fragment located?
+ operationEl.setAttribute( 'is-controlled', operation.isControlled )
+ operationEl.setAttribute( 'title', operation.gate.name )
+ operationEl.style.gridColumnStart = Editor.momentIndexToGridColumn( operation.momentIndex )
+ operationEl.style.gridRowStart = Editor.registerIndexToGridRow( registerIndex )
+ if( operation.gate.has_parameters ) Object.keys(operation.gate.parameters).forEach( element => {
+ operationEl.setAttribute( element, operation.gate.parameters[element] ) //adds a parameter attribute to the operation!
+ })
+ const tileEl = document.createElement( 'div' )
+ operationEl.appendChild( tileEl )
+ tileEl.classList.add( 'Q-circuit-operation-tile' )
+ if( operation.gate.symbol !== Gate.CURSOR.symbol ) tileEl.innerText = operation.gate.symbol
+
+
+ // Add operation link wires
+ // for multi-qubit operations.
+
+ if( operation.registerIndices.length > 1 ){
+
+ operationEl.setAttribute( 'register-indices', operation.registerIndices )
+ operationEl.setAttribute( 'register-indices-index', i )
+ operationEl.setAttribute(
+
+ 'sibling-indices',
+ operation.registerIndices
+ .filter( function( siblingRegisterIndex ){
+
+ return registerIndex !== siblingRegisterIndex
+ })
+ )
+ operation.registerIndices.forEach( function( registerIndex, i ){
+
+ if( i < operation.registerIndices.length - 1 ){
+
+ const
+ siblingRegisterIndex = operation.registerIndices[ i + 1 ],
+ registerDelta = Math.abs( siblingRegisterIndex - registerIndex ),
+ start = Math.min( registerIndex, siblingRegisterIndex ),
+ end = Math.max( registerIndex, siblingRegisterIndex ),
+ containerEl = document.createElement( 'div' ),
+ linkEl = document.createElement( 'div' )
+
+ backgroundEl.appendChild( containerEl )
+ containerEl.setAttribute( 'moment-index', operation.momentIndex )
+ containerEl.setAttribute( 'register-index', registerIndex )
+ containerEl.classList.add( 'Q-circuit-operation-link-container' )
+ containerEl.style.gridRowStart = Editor.registerIndexToGridRow( start )
+ containerEl.style.gridRowEnd = Editor.registerIndexToGridRow( end + 1 )
+ containerEl.style.gridColumn = Editor.momentIndexToGridColumn( operation.momentIndex )
+
+ containerEl.appendChild( linkEl )
+ linkEl.classList.add( 'Q-circuit-operation-link' )
+ if( registerDelta > 1 ) linkEl.classList.add( 'Q-circuit-operation-link-curved' )
+ }
+ })
+ if( operation.isControlled && i === 0 ){
+ operationEl.classList.add( 'Q-circuit-operation-control' )
+ operationEl.setAttribute( 'title', 'Control' )
+ tileEl.innerText = ''
+ }
+ else operationEl.classList.add( 'Q-circuit-operation-target' )
+ }
+ })
+}
+
+
+
+
+Editor.isValidControlCandidate = function( circuitEl ){
+
+ const
+ selectedOperations = Array
+ .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
+
+
+ // We must have at least two operations selected,
+ // hopefully a control and something else,
+ // in order to attempt a join.
+
+ if( selectedOperations.length < 2 ) return false
+
+
+ // Note the different moment indices present
+ // among the selected operations.
+
+ const moments = selectedOperations.reduce( function( moments, operationEl ){
+
+ moments[ operationEl.getAttribute( 'moment-index' )] = true
+ return moments
+
+ }, {} )
+
+
+ // All selected operations must be in the same moment.
+
+ if( Object.keys( moments ).length > 1 ) return false
+
+
+ // If there are multi-register operations present,
+ // regardless of whether those are controls or swaps,
+ // all siblings must be present
+ // in order to join a new gate to this selection.
+
+ // I’m sure we can make this whole routine much more efficient
+ // but its results are correct and boy am I tired ;)
+
+ const allSiblingsPresent = selectedOperations
+ .reduce( function( status, operationEl ){
+
+ const registerIndicesString = operationEl.getAttribute( 'register-indices' )
+
+
+ // If it’s a single-register operation
+ // there’s no need to search further.
+
+ if( !registerIndicesString ) return status
+
+
+ // How many registers are in use
+ // by this operation?
+
+ const
+ registerIndicesLength = registerIndicesString
+ .split( ',' )
+ .map( function( registerIndex ){
+
+ return +registerIndex
+ })
+ .length,
+
+
+ // How many of this operation’s siblings
+ // (including itself) can we find?
+
+ allSiblingsLength = selectedOperations
+ .reduce( function( siblings, operationEl ){
+
+ if( operationEl.getAttribute( 'register-indices' ) === registerIndicesString ){
+
+ siblings.push( operationEl )
+ }
+ return siblings
+
+ }, [])
+ .length
+
+
+ // Did we find all of the siblings for this operation?
+ // Square that with previous searches.
+
+ return status && allSiblingsLength === registerIndicesLength
+
+ }, true )
+
+
+ // If we’re missing some siblings
+ // then we cannot modify whatever we have selected here.
+
+ if( allSiblingsPresent !== true ) return false
+
+ // Note the different gate types present
+ // among the selected operations.
+
+ const gates = selectedOperations.reduce( function( gates, operationEl ){
+ const gateSymbol = operationEl.getAttribute( 'gate-symbol' )
+ if( !mathf.isUsefulInteger( gates[ gateSymbol ])) gates[ gateSymbol ] = 1
+ else gates[ gateSymbol ] ++
+ return gates
+
+ }, {} )
+
+
+ // Note if each operation is already controlled or not.
+
+ const {
+
+ totalControlled,
+ totalNotControlled
+
+ } = selectedOperations
+ .reduce( function( stats, operationEl ){
+
+ if( operationEl.getAttribute( 'is-controlled' ) === 'true' )
+ stats.totalControlled ++
+ else stats.totalNotControlled ++
+ return stats
+
+ }, {
+
+ totalControlled: 0,
+ totalNotControlled: 0
+ })
+
+ // This could be ONE “identity cursor”
+ // and one or more of a regular single gate
+ // that is NOT already controlled.
+
+ if( gates[ Gate.CURSOR.symbol ] === 1 &&
+ Object.keys( gates ).length === 2 &&
+ totalNotControlled === selectedOperations.length ){
+
+ return true
+ }
+
+
+ // There’s NO “identity cursor”
+ // but there is one or more of specific gate type
+ // and at least one of those is already controlled.
+
+ if( gates[ Gate.CURSOR.symbol ] === undefined &&
+ Object.keys( gates ).length === 1 &&
+ totalControlled > 0 &&
+ totalNotControlled > 0 ){
+
+ return true
+ }
+
+
+ // Any other combination allowed? Nope!
+
+ return false
+}
+Editor.createControl = function( circuitEl ){
+
+ if( Editor.isValidControlCandidate( circuitEl ) !== true ) return this
+
+
+ const
+ circuit = circuitEl.circuit,
+ selectedOperations = Array
+ .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )),
+
+
+ // Are any of these controlled operations??
+ // If so, we need to find its control component
+ // and re-use it.
+
+ existingControlEl = selectedOperations.find( function( operationEl ){
+
+ return (
+
+ operationEl.getAttribute( 'is-controlled' ) === 'true' &&
+ operationEl.getAttribute( 'register-array-index' ) === '0'
+ )
+ }),
+
+
+ // One control. One or more targets.
+
+ control = existingControlEl || selectedOperations
+ .find( function( el ){
+
+ return el.getAttribute( 'gate-symbol' ) === Gate.CURSOR.symbol
+ }),
+ targets = selectedOperations
+ .reduce( function( targets, el ){
+
+ //if( el.getAttribute( 'gate-symbol' ) !== '!' ) targets.push( el )
+ if( el !== control ) targets.push( el )
+ return targets
+
+ }, [] )
+
+
+ // Ready to roll.
+
+ circuit.history.createEntry$()
+ selectedOperations.forEach( function( operationEl ){
+
+ circuit.clear$(
+
+ +operationEl.getAttribute( 'moment-index' ),
+ +operationEl.getAttribute( 'register-index' )
+ )
+ })
+ circuit.set$(
+ targets[ 0 ].getAttribute( 'gate-symbol' ),
+ +control.getAttribute( 'moment-index' ),
+ [ +control.getAttribute( 'register-index' )].concat(
+
+ targets.reduce( function( registers, operationEl ){
+
+ registers.push( +operationEl.getAttribute( 'register-index' ))
+ return registers
+
+ }, [] )
+ )
+ )
+
+
+ // Update our toolbar button states.
+
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
+
+ return this
+}
+
+
+
+
+Editor.isValidSwapCandidate = function( circuitEl ){
+
+ const
+ selectedOperations = Array
+ .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
+
+
+ // We can only swap between two registers.
+ // No crazy rotation-swap bullshit. (Yet.)
+ if( selectedOperations.length !== 2 ) return false
+
+
+ // Both operations must be “identity cursors.”
+ // If so, we are good to go.
+
+ areBothCursors = selectedOperations.every( function( operationEl ){
+
+ return operationEl.getAttribute( 'gate-symbol' ) === Gate.CURSOR.symbol
+ })
+ if( areBothCursors ) return true
+
+
+ // Otherwise this is not a valid swap candidate.
+
+ return false
+}
+Editor.createSwap = function( circuitEl ){
+
+ if( Editor.isValidSwapCandidate( circuitEl ) !== true ) return this
+
+ const
+ selectedOperations = Array
+ .from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' )),
+ momentIndex = +selectedOperations[ 0 ].getAttribute( 'moment-index' )
+ registerIndices = selectedOperations
+ .reduce( function( registerIndices, operationEl ){
+
+ registerIndices.push( +operationEl.getAttribute( 'register-index' ))
+ return registerIndices
+
+ }, [] ),
+ circuit = circuitEl.circuit
+
+
+ // Create the swap operation.
+
+ circuit.history.createEntry$()
+ selectedOperations.forEach( function( operation ){
+
+ circuit.clear$(
+
+ +operation.getAttribute( 'moment-index' ),
+ +operation.getAttribute( 'register-index' )
+ )
+ })
+ circuit.set$(
+
+ Gate.SWAP,
+ momentIndex,
+ registerIndices
+ )
+
+
+ // Update our toolbar button states.
+
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
+
+ return this
+}
+
+
+
+
+Editor.onSelectionChanged = function( circuitEl ){
+
+ const controlButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-control' )
+ if( Editor.isValidControlCandidate( circuitEl )){
+
+ controlButtonEl.removeAttribute( 'Q-disabled' )
+ }
+ else controlButtonEl.setAttribute( 'Q-disabled', true )
+
+ const swapButtonEl = circuitEl.querySelector( '.Q-circuit-toggle-swap' )
+ if( Editor.isValidSwapCandidate( circuitEl )){
+
+ swapButtonEl.removeAttribute( 'Q-disabled' )
+ }
+ else swapButtonEl.setAttribute( 'Q-disabled', true )
+}
+Editor.onCircuitChanged = function( circuitEl ){
+
+ const circuit = circuitEl.circuit
+ window.dispatchEvent( new CustomEvent(
+
+ 'Q gui altered circuit',
+ { detail: { circuit: circuit }}
+ ))
+
+ // Should we trigger a circuit.evaluate$() here?
+ // Particularly when we move all that to a new thread??
+ // console.log( originCircuit.report$() ) ??
+}
+
+
+
+
+
+Editor.unhighlightAll = function( circuitEl ){
+
+ Array.from( circuitEl.querySelectorAll(
+
+ '.Q-circuit-board-background > div,'+
+ '.Q-circuit-board-foreground > div'
+ ))
+ .forEach( function( el ){
+
+ el.classList.remove( 'Q-circuit-cell-highlighted' )
+ })
+}
+
+
+
+
+
+
+ //////////////////////
+ // //
+ // Pointer MOVE //
+ // //
+//////////////////////
+
+
+Editor.onPointerMove = function( event ){
+
+
+ // We need our cursor coordinates straight away.
+ // We’ll use that both for dragging (immediately below)
+ // and for hover highlighting (further below).
+ // Let’s also hold on to a list of all DOM elements
+ // that contain this X, Y point
+ // and also see if one of those is a circuit board container.
+
+ const
+ { x, y } = Editor.getInteractionCoordinates( event ),
+ foundEls = document.elementsFromPoint( x, y ),
+ boardContainerEl = foundEls.find( function( el ){
+
+ return el.classList.contains( 'Q-circuit-board-container' )
+ })
+
+
+ // Are we in the middle of a circuit clipboard drag?
+ // If so we need to move that thing!
+
+ if( Editor.dragEl !== null ){
+
+
+ // ex. Don’t scroll on touch devices!
+
+ event.preventDefault()
+
+
+ // This was a very useful resource
+ // for a reality check on DOM coordinates:
+ // https://javascript.info/coordinates
+
+ Editor.dragEl.style.left = ( x + window.pageXOffset + Editor.dragEl.offsetX ) +'px'
+ Editor.dragEl.style.top = ( y + window.pageYOffset + Editor.dragEl.offsetY ) +'px'
+
+ if( !boardContainerEl && Editor.dragEl.circuitEl ) Editor.dragEl.classList.add( 'Q-circuit-clipboard-danger' )
+ else Editor.dragEl.classList.remove( 'Q-circuit-clipboard-danger' )
+ }
+
+
+ // If we’re not over a circuit board container
+ // then there’s no highlighting work to do
+ // so let’s bail now.
+
+ if( !boardContainerEl ) return
+
+
+ // Now we know we have a circuit board
+ // so we must have a circuit
+ // and if that’s locked then highlighting changes allowed!
+
+ const circuitEl = boardContainerEl.closest( '.Q-circuit' )
+ if( circuitEl.classList.contains( 'Q-circuit-locked' )) return
+
+
+ // Ok, we’ve found a circuit board.
+ // First, un-highlight everything.
+
+ Array.from( boardContainerEl.querySelectorAll(`
+
+ .Q-circuit-board-background > div,
+ .Q-circuit-board-foreground > div
+
+ `)).forEach( function( el ){
+
+ el.classList.remove( 'Q-circuit-cell-highlighted' )
+ })
+
+
+ // Let’s prioritize any element that is “sticky”
+ // which means it can appear OVER another grid cell.
+
+
+ const
+ cellEl = foundEls.find( function( el ){
+
+ const style = window.getComputedStyle( el )
+ return (
+
+ style.position === 'sticky' && (
+
+ el.getAttribute( 'moment-index' ) !== null ||
+ el.getAttribute( 'register-index' ) !== null
+ )
+ )
+ }),
+ highlightByQuery = function( query ){
+
+ Array.from( boardContainerEl.querySelectorAll( query ))
+ .forEach( function( el ){
+
+ el.classList.add( 'Q-circuit-cell-highlighted' )
+ })
+ }
+
+
+ // If we’ve found one of these “sticky” cells
+ // let’s use its moment and/or register data
+ // to highlight moments or registers (or all).
+
+ if( cellEl ){
+
+ const
+ momentIndex = cellEl.getAttribute( 'moment-index' ),
+ registerIndex = cellEl.getAttribute( 'register-index' )
+
+ if( momentIndex === null ){
+
+ highlightByQuery( `div[register-index="${ registerIndex }"]` )
+ return
+ }
+ if( registerIndex === null ){
+
+ highlightByQuery( `div[moment-index="${ momentIndex }"]` )
+ return
+ }
+ highlightByQuery(`
+
+ .Q-circuit-board-background > div[moment-index],
+ .Q-circuit-board-foreground > .Q-circuit-operation
+
+ `)
+ return
+ }
+
+
+ // Ok, we know we’re hovering over the circuit board
+ // but we’re not on a “sticky” cell.
+ // We might be over an operation, but we might not.
+ // No matter -- we’ll infer the moment and register indices
+ // from the cursor position.
+
+ const
+ boardElBounds = boardContainerEl.getBoundingClientRect(),
+ xLocal = x - boardElBounds.left + boardContainerEl.scrollLeft + 1,
+ yLocal = y - boardElBounds.top + boardContainerEl.scrollTop + 1,
+ columnIndex = Editor.pointToGrid( xLocal ),
+ rowIndex = Editor.pointToGrid( yLocal ),
+ momentIndex = Editor.gridColumnToMomentIndex( columnIndex ),
+ registerIndex = Editor.gridRowToRegisterIndex( rowIndex )
+
+
+ // If this hover is “out of bounds”
+ // ie. on the same row or column as an “Add register” or “Add moment” button
+ // then let’s not highlight anything.
+
+ if( momentIndex > circuitEl.circuit.timewidth ||
+ registerIndex > circuitEl.circuit.bandwidth ) return
+
+
+ // If we’re at 0, 0 or below that either means
+ // we’re over the “Select all” button (already taken care of above)
+ // or over the lock toggle button.
+ // Either way, it’s time to bail.
+
+ if( momentIndex < 1 || registerIndex < 1 ) return
+
+
+ // If we’ve made it this far that means
+ // we have valid moment and register indices.
+ // Highlight them!
+
+ highlightByQuery(`
+
+ div[moment-index="${ momentIndex }"],
+ div[register-index="${ registerIndex }"]
+ `)
+ return
+}
+
+
+
+ ///////////////////////
+ // //
+ // Pointer PRESS //
+ // //
+///////////////////////
+
+
+Editor.onPointerPress = function( event ){
+ // This is just a safety net
+ // in case something terrible has ocurred.
+ // (ex. Did the user click and then their mouse ran
+ // outside the window but browser didn’t catch it?)
+
+ if( Editor.dragEl !== null ){
+
+ Editor.onPointerRelease( event )
+ return
+ }
+ const
+ targetEl = event.target,
+ circuitEl = targetEl.closest( '.Q-circuit' ),
+ paletteEl = targetEl.closest( '.Q-circuit-palette' )
+ parameterEl = targetEl.closest( '.Q-parameters-box' )
+
+ // If we can’t find a circuit that’s a really bad sign
+ // considering this event should be fired when a circuit
+ // is clicked on. So... bail!
+
+ if( !circuitEl && !paletteEl ) return
+
+ // This is a bit of a gamble.
+ // There’s a possibility we’re not going to drag anything,
+ // but we’ll prep these variables here anyway
+ // because both branches of if( circuitEl ) and if( paletteEl )
+ // below will have access to this scope.
+
+ dragEl = document.createElement( 'div' )
+ dragEl.classList.add( 'Q-circuit-clipboard' )
+ const { x, y } = Editor.getInteractionCoordinates( event )
+
+
+ // Are we dealing with a circuit interface?
+ // ie. NOT a palette interface.
+
+ if( circuitEl && !parameterEl ){
+
+ // Shall we toggle the circuit lock?
+
+ const
+ circuit = circuitEl.circuit,
+ circuitIsLocked = circuitEl.classList.contains( 'Q-circuit-locked' ),
+ lockEl = targetEl.closest( '.Q-circuit-toggle-lock' )
+
+ if( lockEl ){
+
+ // const toolbarEl = Array.from( circuitEl.querySelectorAll( '.Q-circuit-button' ))
+ if( circuitIsLocked ){
+
+ circuitEl.classList.remove( 'Q-circuit-locked' )
+ lockEl.innerText = '🔓'
+ }
+ else {
+
+ circuitEl.classList.add( 'Q-circuit-locked' )
+ lockEl.innerText = '🔒'
+ Editor.unhighlightAll( circuitEl )
+ }
+
+
+ // We’ve toggled the circuit lock button
+ // so we should prevent further propagation
+ // before proceeding further.
+ // That includes running all this code again
+ // if it was originally fired by a mouse event
+ // and about to be fired by a touch event!
+
+ event.preventDefault()
+ event.stopPropagation()
+ return
+ }
+
+
+ // If our circuit is already “locked”
+ // then there’s nothing more to do here.
+
+ if( circuitIsLocked ) {
+
+ logger.warn( `User attempted to interact with a circuit editor but it was locked.` )
+ return
+ }
+
+
+ const
+ cellEl = targetEl.closest(`
+
+ .Q-circuit-board-foreground > div,
+ .Q-circuit-palette > div
+ `),
+ undoEl = targetEl.closest( '.Q-circuit-button-undo' ),
+ redoEl = targetEl.closest( '.Q-circuit-button-redo' ),
+ controlEl = targetEl.closest( '.Q-circuit-toggle-control' ),
+ swapEl = targetEl.closest( '.Q-circuit-toggle-swap' ),
+ addMomentEl = targetEl.closest( '.Q-circuit-moment-add' ),
+ addRegisterEl = targetEl.closest( '.Q-circuit-register-add' )
+
+ if( !cellEl &&
+ !undoEl &&
+ !redoEl &&
+ !controlEl &&
+ !swapEl &&
+ !addMomentEl &&
+ !addRegisterEl ) return
+
+
+ // By this point we know that the circuit is unlocked
+ // and that we’ll activate a button / drag event / etc.
+ // So we need to hault futher event propagation
+ // including running this exact code again if this was
+ // fired by a touch event and about to again by mouse.
+ // This may SEEM redundant because we did this above
+ // within the lock-toggle button code
+ // but we needed to NOT stop propagation if the circuit
+ // was already locked -- for scrolling and such.
+
+ event.preventDefault()
+ event.stopPropagation()
+
+
+ if( undoEl && circuit.history.undo$() ){
+
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
+ }
+ if( redoEl && circuit.history.redo$() ){
+
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
+ }
+ if( controlEl ) Editor.createControl( circuitEl )
+ if( swapEl ) Editor.createSwap( circuitEl )
+ if( addMomentEl ) console.log( '→ Add moment' )
+ if( addRegisterEl ) console.log( '→ Add register' )
+
+
+ // We’re done dealing with external buttons.
+ // So if we can’t find a circuit CELL
+ // then there’s nothing more to do here.
+
+ if( !cellEl ) return
+
+ // Once we know what cell we’ve pressed on
+ // we can get the momentIndex and registerIndex
+ // from its pre-defined attributes.
+ // NOTE that we are getting CSS grid column and row
+ // from our own conversion function and NOT from
+ // asking its styles. Why? Because browsers convert
+ // grid commands to a shorthand less easily parsable
+ // and therefore makes our code and reasoning
+ // more prone to quirks / errors. Trust me!
+
+ const
+ momentIndex = +cellEl.getAttribute( 'moment-index' ),
+ registerIndex = +cellEl.getAttribute( 'register-index' ),
+ columnIndex = Editor.momentIndexToGridColumn( momentIndex ),
+ rowIndex = Editor.registerIndexToGridRow( registerIndex )
+
+
+ // Looks like our circuit is NOT locked
+ // and we have a valid circuit CELL
+ // so let’s find everything else we could need.
+
+ const
+ selectallEl = targetEl.closest( '.Q-circuit-selectall' ),
+ registersymbolEl = targetEl.closest( '.Q-circuit-register-label' ),
+ momentsymbolEl = targetEl.closest( '.Q-circuit-moment-label' ),
+ inputEl = targetEl.closest( '.Q-circuit-input' ),
+ operationEl = targetEl.closest( '.Q-circuit-operation' )
+
+ // +++++++++++++++
+ // We’ll have to add some input editing capability later...
+ // Of course you can already do this in code!
+ // For now though most quantum code assumes all qubits
+ // begin with a value of zero so this is mostly ok ;)
+
+ if( inputEl ){
+
+ console.log( '→ Edit input Qubit value at', registerIndex )
+ return
+ }
+
+
+ // Let’s inspect a group of items via a CSS query.
+ // If any of them are NOT “selected” (highlighted)
+ // then select them all.
+ // But if ALL of them are already selected
+ // then UNSELECT them all.
+
+ function toggleSelection( query ){
+
+ const
+ operations = Array.from( circuitEl.querySelectorAll( query )),
+ operationsSelectedLength = operations.reduce( function( sum, element ){
+
+ sum += +element.classList.contains( 'Q-circuit-cell-selected' )
+ return sum
+
+ }, 0 )
+
+ if( operationsSelectedLength === operations.length ){
+
+ operations.forEach( function( el ){
+ el.classList.remove( 'Q-circuit-cell-selected' )
+ })
+ }
+ else {
+
+ operations.forEach( function( el ){
+
+ el.classList.add( 'Q-circuit-cell-selected' )
+ })
+ }
+ Editor.onSelectionChanged( circuitEl )
+ }
+
+
+ // Clicking on the “selectAll” button
+ // or any of the Moment symbols / Register symbols
+ // causes a selection toggle.
+ // In the future we may want to add
+ // dragging of entire Moment columns / Register rows
+ // to splice them out / insert them elsewhere
+ // when a user clicks and drags them.
+
+ if( selectallEl ){
+
+ toggleSelection( '.Q-circuit-operation' )
+ return
+ }
+ if( momentsymbolEl ){
+
+ toggleSelection( `.Q-circuit-operation[moment-index="${ momentIndex }"]` )
+ return
+ }
+ if( registersymbolEl ){
+
+ toggleSelection( `.Q-circuit-operation[register-index="${ registerIndex }"]` )
+ return
+ }
+
+
+ // Right here we can made a big decision:
+ // If you’re not pressing on an operation
+ // then GO HOME.
+
+ if( !operationEl ) return
+ // If we've doubleclicked on an operation and the operation has parameters, we should be able
+ // to edit those parameters regardless of whether or not the circuit is locked.
+ if( event.detail == 2) {
+ const operation = Gate.findBySymbol(operationEl.getAttribute( 'gate-symbol' ))
+ if( operation.has_parameters ) {
+ Editor.onDoubleclick( event, operationEl )
+ return
+ }
+ }
+
+ // Ok now we know we are dealing with an operation.
+ // This preserved selection state information
+ // will be useful for when onPointerRelease is fired.
+
+ if( operationEl.classList.contains( 'Q-circuit-cell-selected' )){
+ operationEl.wasSelected = true
+ }
+ else operationEl.wasSelected = false
+
+
+ // And now we can proceed knowing that
+ // we need to select this operation
+ // and possibly drag it
+ // as well as any other selected operations.
+
+ operationEl.classList.add( 'Q-circuit-cell-selected' )
+ const selectedOperations = Array.from( circuitEl.querySelectorAll( '.Q-circuit-cell-selected' ))
+ dragEl.circuitEl = circuitEl
+ dragEl.originEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
+
+
+ // These are the default values;
+ // will be used if we’re only dragging one operation around.
+ // But if dragging more than one operation
+ // and we’re dragging the clipboard by an operation
+ // that is NOT in the upper-left corner of the clipboard
+ // then we need to know what the offset is.
+ // (Will be calculated below.)
+
+ dragEl.columnIndexOffset = 1
+ dragEl.rowIndexOffset = 1
+
+
+ // Now collect all of the selected operations,
+ // rip them from the circuit board’s foreground layer
+ // and place them on the clipboard.
+
+ let
+ columnIndexMin = Infinity,
+ rowIndexMin = Infinity
+
+ selectedOperations.forEach( function( el ){
+
+
+ // WORTH REPEATING:
+ // Once we know what cell we’ve pressed on
+ // we can get the momentIndex and registerIndex
+ // from its pre-defined attributes.
+ // NOTE that we are getting CSS grid column and row
+ // from our own conversion function and NOT from
+ // asking its styles. Why? Because browsers convert
+ // grid commands to a shorthand less easily parsable
+ // and therefore makes our code and reasoning
+ // more prone to quirks / errors. Trust me!
+
+ const
+ momentIndex = +el.getAttribute( 'moment-index' ),
+ registerIndex = +el.getAttribute( 'register-index' ),
+ columnIndex = Editor.momentIndexToGridColumn( momentIndex ),
+ rowIndex = Editor.registerIndexToGridRow( registerIndex )
+
+ columnIndexMin = Math.min( columnIndexMin, columnIndex )
+ rowIndexMin = Math.min( rowIndexMin, rowIndex )
+ el.classList.remove( 'Q-circuit-cell-selected' )
+ el.origin = { momentIndex, registerIndex, columnIndex, rowIndex }
+ dragEl.appendChild( el )
+ })
+ selectedOperations.forEach( function( el ){
+
+ const
+ columnIndexForClipboard = 1 + el.origin.columnIndex - columnIndexMin,
+ rowIndexForClipboard = 1 + el.origin.rowIndex - rowIndexMin
+
+ el.style.gridColumn = columnIndexForClipboard
+ el.style.gridRow = rowIndexForClipboard
+
+
+ // If this operation element is the one we grabbed
+ // (mostly relevant if we’re moving multiple operations at once)
+ // we need to know what the “offset” so everything can be
+ // placed correctly relative to this drag-and-dropped item.
+
+ if( el.origin.columnIndex === columnIndex &&
+ el.origin.rowIndex === rowIndex ){
+
+ dragEl.columnIndexOffset = columnIndexForClipboard
+ dragEl.rowIndexOffset = rowIndexForClipboard
+ }
+ })
+
+
+ // We need an XY offset that describes the difference
+ // between the mouse / finger press position
+ // and the clipboard’s intended upper-left position.
+ // To do that we need to know the press position (obviously!),
+ // the upper-left bounds of the circuit board’s foreground,
+ // and the intended upper-left bound of clipboard.
+
+ const
+ boardEl = circuitEl.querySelector( '.Q-circuit-board-foreground' ),
+ bounds = boardEl.getBoundingClientRect(),
+ minX = Editor.gridToPoint( columnIndexMin ),
+ minY = Editor.gridToPoint( rowIndexMin )
+
+ dragEl.offsetX = bounds.left + minX - x
+ dragEl.offsetY = bounds.top + minY - y
+ dragEl.momentIndex = momentIndex
+ dragEl.registerIndex = registerIndex
+ }
+ else if( paletteEl ){
+ const operationEl = targetEl.closest( '.Q-circuit-operation' )
+
+ if( !operationEl ) return
+
+ const
+ bounds = operationEl.getBoundingClientRect(),
+ { x, y } = Editor.getInteractionCoordinates( event )
+
+ dragEl.appendChild( operationEl.cloneNode( true ))
+ dragEl.originEl = paletteEl
+ dragEl.offsetX = bounds.left - x
+ dragEl.offsetY = bounds.top - y
+ }
+ else if( parameterEl ){
+ const exitEl = targetEl.closest( '.Q-parameter-box-exit' )
+ if( !exitEl ) return
+ parameterEl.style.display = 'none'
+ const foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
+ operationEl = foregroundEl.querySelector( `[moment-index="${ parameterEl.getAttribute( 'operation-moment-index' )}"]` +
+ `[register-index="${ parameterEl.getAttribute( 'operation-register-index' )}"]` )
+ parameters = {}
+ operationSkeleton = Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' ))
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = operationEl.getAttribute( key ) ? operationEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ //upon exiting, we should update the circuit!!!
+ circuitEl.circuit.set$(
+ operationEl.getAttribute( 'gate-symbol' ),
+ +operationEl.getAttribute( 'moment-index' ),
+ operationEl.getAttribute( 'register-indices' ) ? operationEl.getAttribute( 'register-indices' ).split(',').map( i => +i ) :
+ [ +operationEl.getAttribute( 'register-index' )],
+ parameters
+ )
+ //on exiting the parameter-input-box, we should update the circuit!!
+ parameterEl.innerHTML = ""
+ return
+ }
+ dragEl.timestamp = Date.now()
+
+
+ // Append the clipboard to the document,
+ // establish a global reference to it,
+ // and trigger a draw of it in the correct spot.
+
+ document.body.appendChild( dragEl )
+ Editor.dragEl = dragEl
+ Editor.onPointerMove( event )
+}
+
+
+
+
+
+
+ /////////////////////////
+ // //
+ // Pointer RELEASE //
+ // //
+/////////////////////////
+
+
+Editor.onPointerRelease = function( event ){
+
+
+ // If there’s no dragEl then bail immediately.
+ if( Editor.dragEl === null ) return
+ // Looks like we’re moving forward with this plan,
+ // so we’ll take control of the input now.
+ event.preventDefault()
+ event.stopPropagation()
+
+
+ // We can’t get the drop target from the event.
+ // Think about it: What was under the mouse / finger
+ // when this drop event was fired? THE CLIPBOARD !
+ // So instead we need to peek at what elements are
+ // under the mouse / finger, skipping element [0]
+ // because that will be the clipboard.
+
+ const { x, y } = Editor.getInteractionCoordinates( event )
+ const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container")
+ if( boardContainerAll.length === 0 ) return
+ let boardContainerEl = Array.from(boardContainerAll).find((element) => {
+ let rect = element.getBoundingClientRect()
+ let clientX = rect.left
+ let clientY = rect.top
+ let height = element.offsetHeight
+ let width = element.offsetWidth
+ return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height )
+ })
+ returnToOrigin = function(){
+
+
+ // We can only do a “true” return to origin
+ // if we were dragging from a circuit.
+ // If we were dragging from a palette
+ // we can just stop dragging.
+
+ if( Editor.dragEl.circuitEl ){
+
+ Array.from( Editor.dragEl.children ).forEach( function( el ){
+
+ Editor.dragEl.originEl.appendChild( el )
+ el.style.gridColumn = el.origin.columnIndex
+ el.style.gridRow = el.origin.rowIndex
+ if( el.wasSelected === true ) el.classList.remove( 'Q-circuit-cell-selected' )
+ else el.classList.add( 'Q-circuit-cell-selected' )
+ })
+ Editor.onSelectionChanged( Editor.dragEl.circuitEl )
+ }
+ document.body.removeChild( Editor.dragEl )
+ Editor.dragEl = null
+ }
+
+
+ // If we have not dragged on to a circuit board
+ // then we’re throwing away this operation.
+
+ if( !boardContainerEl ){
+
+ if( Editor.dragEl.circuitEl ){
+
+ const
+ originCircuitEl = Editor.dragEl.circuitEl
+ originCircuit = originCircuitEl.circuit
+
+ originCircuit.history.createEntry$()
+ Array
+ .from( Editor.dragEl.children )
+ .forEach( function( child ){
+
+ originCircuit.clear$(
+
+ child.origin.momentIndex,
+ child.origin.registerIndex
+ )
+ })
+ Editor.onSelectionChanged( originCircuitEl )
+ Editor.onCircuitChanged( originCircuitEl )
+ }
+
+
+ // TIME TO DIE.
+ // Let’s keep a private reference to
+ // the current clipboard.
+
+ let clipboardToDestroy = Editor.dragEl
+
+
+ // Now we can remove our dragging reference.
+
+ Editor.dragEl = null
+
+
+ // Add our CSS animation routine
+ // which will run for 1 second.
+ // If we were SUPER AWESOME
+ // we would have also calculated drag momentum
+ // and we’d let this glide away!
+
+ clipboardToDestroy.classList.add( 'Q-circuit-clipboard-destroy' )
+
+
+ // And around the time that animation is completing
+ // we can go ahead and remove our clipboard from the DOM
+ // and kill the reference.
+
+ setTimeout( function(){
+
+ document.body.removeChild( clipboardToDestroy )
+ clipboardToDestroy = null
+
+ }, 500 )
+
+
+ // No more to do here. Goodbye.
+
+ return
+ }
+
+
+ // If we couldn’t determine a circuitEl
+ // from the drop target,
+ // or if there is a target circuit but it’s locked,
+ // then we need to return these dragged items
+ // to their original circuit.
+
+ const circuitEl = boardContainerEl.closest( '.Q-circuit' )
+ if( circuitEl.classList.contains( 'Q-circuit-locked' )){
+
+ returnToOrigin()
+ return
+ }
+
+
+ // Time to get serious.
+ // Where exactly are we dropping on to this circuit??
+
+ const
+ circuit = circuitEl.circuit,
+ bounds = boardContainerEl.getBoundingClientRect(),
+ droppedAtX = x - bounds.left + boardContainerEl.scrollLeft,
+ droppedAtY = y - bounds.top + boardContainerEl.scrollTop,
+ droppedAtMomentIndex = Editor.gridColumnToMomentIndex(
+
+ Editor.pointToGrid( droppedAtX )
+ ),
+ droppedAtRegisterIndex = Editor.gridRowToRegisterIndex(
+
+ Editor.pointToGrid( droppedAtY )
+ ),
+ foregroundEl = circuitEl.querySelector( '.Q-circuit-board-foreground' )
+
+
+ // If this is a self-drop
+ // we can also just return to origin and bail.
+
+ if( Editor.dragEl.circuitEl === circuitEl &&
+ Editor.dragEl.momentIndex === droppedAtMomentIndex &&
+ Editor.dragEl.registerIndex === droppedAtRegisterIndex ){
+
+ returnToOrigin()
+ return
+ }
+
+
+ // Is this a valid drop target within this circuit?
+
+ if(
+ droppedAtMomentIndex < 1 ||
+ droppedAtMomentIndex > circuit.timewidth ||
+ droppedAtRegisterIndex < 1 ||
+ droppedAtRegisterIndex > circuit.bandwidth
+ ){
+ returnToOrigin()
+ return
+ }
+
+
+ // Finally! Work is about to be done!
+ // All we need to do is tell the circuit itself
+ // where we need to place these dragged items.
+ // It will do all the validation for us
+ // and then fire events that will place new elements
+ // where they need to go!
+
+ const
+ draggedOperations = Array.from( Editor.dragEl.children ),
+ draggedMomentDelta = droppedAtMomentIndex - Editor.dragEl.momentIndex,
+ draggedRegisterDelta = droppedAtRegisterIndex - Editor.dragEl.registerIndex,
+ setCommands = []
+
+
+ // Whatever the next action is that we perform on the circuit,
+ // this was user-initiated via the graphic user interface (GUI).
+
+ circuit.history.createEntry$()
+
+
+ // Now let’s work our way through each of the dragged operations.
+ // If some of these are components of a multi-register operation
+ // the sibling components will get spliced out of the array
+ // to avoid processing any specific operation more than once.
+
+ draggedOperations.forEach( function( childEl, i ){
+
+ let
+ momentIndexTarget = droppedAtMomentIndex,
+ registerIndexTarget = droppedAtRegisterIndex
+
+ if( Editor.dragEl.circuitEl ){
+
+ momentIndexTarget += childEl.origin.momentIndex - Editor.dragEl.momentIndex
+ registerIndexTarget += childEl.origin.registerIndex - Editor.dragEl.registerIndex
+ }
+
+
+ // Is this a multi-register operation?
+ // If so, this is also a from-circuit drop
+ // rather than a from-palette drop.
+
+ const registerIndicesString = childEl.getAttribute( 'register-indices' )
+ if( registerIndicesString ){
+
+ // What are ALL of the registerIndices
+ // associated with this multi-register operation?
+ // (We may use them later as a checklist.)
+
+ const
+ registerIndices = registerIndicesString
+ .split( ',' )
+ .map( function( str ){ return +str }),
+
+
+ // Lets look for ALL of the sibling components of this operation.
+ // Later we’ll check and see if the length of this array
+ // is equal to the total number of components for this operation.
+ // If they’re equal then we know we’re dragging the WHOLE thing.
+ // Otherwise we need to determine if it needs to break apart
+ // and if so, what that nature of that break might be.
+
+ foundComponents = Array.from(
+
+ Editor.dragEl.querySelectorAll(
+
+ `[moment-index="${ childEl.origin.momentIndex }"]`+
+ `[register-indices="${ registerIndicesString }"]`
+ )
+ )
+ .sort( function( a, b ){
+
+ const
+ aRegisterIndicesIndex = +a.getAttribute( 'register-indices-index' ),
+ bRegisterIndicesIndex = +b.getAttribute( 'register-indices-index' )
+
+ return aRegisterIndicesIndex - bRegisterIndicesIndex
+ }),
+ allComponents = Array.from( Editor.dragEl.circuitEl.querySelectorAll(
+
+ `[moment-index="${ childEl.origin.momentIndex }"]`+
+ `[register-indices="${ registerIndicesString }"]`
+ )),
+ remainingComponents = allComponents.filter( function( componentEl, i ){
+
+ return !foundComponents.includes( componentEl )
+ }),
+
+
+ // We can’t pick the gate symbol
+ // off the 0th gate in the register indices array
+ // because that will be an identity / control / null gate.
+ // We need to look at slot 1.
+
+ component1 = Editor.dragEl.querySelector(
+
+ `[moment-index="${ childEl.origin.momentIndex }"]`+
+ `[register-index="${ registerIndices[ 1 ] }"]`
+ ),
+ gatesymbol = component1 ?
+ component1.getAttribute( 'gate-symbol' ) :
+ childEl.getAttribute( 'gate-symbol' )
+
+
+ // We needed to grab the above gatesymbol information
+ // before we sent any clear$ commands
+ // which would in turn delete those componentEls.
+ // We’ve just completed that,
+ // so now’s the time to send a clear$ command
+ // before we do any set$ commands.
+
+ draggedOperations.forEach( function( childEl ){
+
+ Editor.dragEl.circuitEl.circuit.clear$(
+
+ childEl.origin.momentIndex,
+ childEl.origin.registerIndex
+ )
+ })
+
+
+ // FULL MULTI-REGISTER DRAG (TO ANY POSITION ON ANY CIRCUIT).
+ // If we are dragging all of the components
+ // of a multi-register operation
+ // then we are good to go.
+
+ if( registerIndices.length === foundComponents.length ){
+
+ const operationSkeleton = Gate.findBySymbol( gatesymbol )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ //circuit.set$(
+ setCommands.push([
+
+ gatesymbol,
+ momentIndexTarget,
+
+
+ // We need to remap EACH register index here
+ // according to the drop position.
+ // Let’s let set$ do all the validation on this.
+
+ registerIndices.map( function( registerIndex ){
+
+ const siblingDelta = registerIndex - childEl.origin.registerIndex
+ registerIndexTarget = droppedAtRegisterIndex
+ if( Editor.dragEl.circuitEl ){
+
+ registerIndexTarget += childEl.origin.registerIndex - Editor.dragEl.registerIndex + siblingDelta
+ }
+ return registerIndexTarget
+ }),
+ parameters
+ // )
+ ])
+ }
+
+
+ // IN-MOMENT (IN-CIRCUIT) PARTIAL MULTI-REGISTER DRAG.
+ // It appears we are NOT dragging all components
+ // of a multi-register operation.
+ // But if we’re dragging within the same circuit
+ // and we’re staying within the same moment index
+ // that might be ok!
+
+ else if( Editor.dragEl.circuitEl === circuitEl &&
+ momentIndexTarget === childEl.origin.momentIndex ){
+
+
+ // We must ensure that only one component
+ // can sit at each register index.
+ // This copies registerIndices,
+ // but inverts the key : property relationship.
+ const registerMap = registerIndices
+ .reduce( function( registerMap, registerIndex, r ){
+
+ registerMap[ registerIndex ] = r
+ return registerMap
+
+ }, {} )
+
+
+ // First, we must remove each dragged component
+ // from the register it was sitting at.
+
+ foundComponents.forEach( function( component ){
+
+ const componentRegisterIndex = +component.getAttribute( 'register-index' )
+
+
+ // Remove this component from
+ // where this component used to be.
+
+ delete registerMap[ componentRegisterIndex ]
+ })
+
+
+ // Now we can seat it at its new position.
+ // Note: This may OVERWRITE one of its siblings!
+ // And that’s ok.
+ foundComponents.forEach( function( component ){
+
+ const
+ componentRegisterIndex = +component.getAttribute( 'register-index' ),
+ registerGrabDelta = componentRegisterIndex - Editor.dragEl.registerIndex
+
+
+ // Now put it where it wants to go,
+ // possibly overwriting a sibling component!
+ //ltnln: if a multiqubit operation component that requires a sibling, overwrites its sibling, both/all components should be destroyed
+ registerMap[
+
+ componentRegisterIndex + draggedRegisterDelta
+
+ ] = +component.getAttribute( 'register-indices-index' )
+ })
+
+
+ // Now let’s flip that registerMap
+ // back into an array of register indices.
+
+ const fixedRegistersIndices = Object.entries( registerMap )
+ .reduce( function( registers, entry, i ){
+
+ registers[ +entry[ 1 ]] = +entry[ 0 ]
+ return registers
+
+ }, [] )
+
+
+ // This will remove any blank entries in the array
+ // ie. if a dragged sibling overwrote a seated one.
+
+ .filter( function( entry ){
+ return mathf.isUsefulInteger( entry )
+ })
+
+ const operationSkeleton = Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ // Finally, we’re ready to set.
+ // circuit.set$(
+ setCommands.push([
+ //ltnln: if a component of an operation that requires a sibling pair overwrites its sibling, we want it removed entirely.
+ fixedRegistersIndices.length < 2 && Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ Gate.NOOP :
+ childEl.getAttribute( 'gate-symbol' ),
+ momentIndexTarget,
+ fixedRegistersIndices,
+ parameters
+ // )
+ ])
+ }
+ else {
+ remainingComponents.forEach( function( componentEl, i ){
+ //circuit.set$(
+ const operationSkeleton = Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ setCommands.push([
+
+ +componentEl.getAttribute( 'register-indices-index' ) && !Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ gatesymbol :
+ Gate.NOOP,
+ +componentEl.getAttribute( 'moment-index' ),
+ +componentEl.getAttribute( 'register-index' ),
+ parameters
+ // )
+ ])
+ })
+
+
+ // Finally, let’s separate and update
+ // all the components that were part of the drag.
+
+ foundComponents.forEach( function( componentEl ){
+ const operationSkeleton = Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) )
+ parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = +componentEl.getAttribute( key ) ? +componentEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ setCommands.push([
+ //ltnln: temporary fix: certain multiqubit operations should only be represented in pairs of registers. If one is removed (i.e. a single component of the pair)
+ //then the entire operation should be removed.
+ +componentEl.getAttribute( 'register-indices-index' ) && !Gate.findBySymbol( componentEl.getAttribute( 'gate-symbol' ) ).is_multi_qubit ?
+ componentEl.getAttribute( 'gate-symbol' ) :
+ Gate.NOOP,
+ +componentEl.getAttribute( 'moment-index' ) + draggedMomentDelta,
+ +componentEl.getAttribute( 'register-index' ) + draggedRegisterDelta,
+ parameters
+ // )
+ ])
+ })
+ }
+
+
+ // We’ve just completed the movement
+ // of a multi-register operation.
+ // But all of the sibling components
+ // will also trigger this process
+ // unless we remove them
+ // from the draggd operations array.
+
+ let j = i + 1
+ while( j < draggedOperations.length ){
+
+ const possibleSibling = draggedOperations[ j ]
+ if( possibleSibling.getAttribute( 'gate-symbol' ) === gatesymbol &&
+ possibleSibling.getAttribute( 'register-indices' ) === registerIndicesString ){
+
+ draggedOperations.splice( j, 1 )
+ }
+ else j ++
+ }
+ }
+
+
+ // This is just a single-register operation.
+ // How simple this looks
+ // compared to all the gibberish above.
+
+ else {
+
+
+ // First, if this operation comes from a circuit
+ // (and not a circuit palette)
+ // make sure the old positions are cleared away.
+
+ if( Editor.dragEl.circuitEl ){
+
+ draggedOperations.forEach( function( childEl ){
+
+ Editor.dragEl.circuitEl.circuit.clear$(
+
+ childEl.origin.momentIndex,
+ childEl.origin.registerIndex
+ )
+ })
+ }
+
+
+ // And now set$ the operation
+ // in its new home.
+
+ // circuit.set$(
+ let registerIndices = [ registerIndexTarget ]
+ //ltnln: By default, multiqubit gates appear in pairs on the circuit rather than
+ // requiring the user to have to pair them like with Swap/CNot.
+ const operationSkeleton = Gate.findBySymbol( childEl.getAttribute( 'gate-symbol' ))
+ if(operationSkeleton.is_multi_qubit ) {
+ registerIndices.push( registerIndexTarget == circuit.bandwidth ? registerIndexTarget - 1 : registerIndexTarget + 1)
+ }
+ let parameters = {}
+ if( operationSkeleton.has_parameters ) {
+ Object.keys( operationSkeleton.parameters ).forEach( key => {
+ parameters[ key ] = childEl.getAttribute( key ) ? childEl.getAttribute( key ) : operationSkeleton.parameters[ key ]
+ })
+ }
+ setCommands.push([
+ childEl.getAttribute( 'gate-symbol' ),
+ momentIndexTarget,
+ registerIndices,
+ parameters
+ // )
+ ])
+ }
+ })
+
+
+ // DO IT DO IT DO IT
+
+ setCommands.forEach( function( setCommand ){
+
+ circuit.set$.apply( circuit, setCommand )
+ })
+
+
+ // Are we capable of making controls? Swaps?
+
+ Editor.onSelectionChanged( circuitEl )
+ Editor.onCircuitChanged( circuitEl )
+
+
+ // If the original circuit and destination circuit
+ // are not the same thing
+ // then we need to also eval the original circuit.
+
+ if( Editor.dragEl.circuitEl &&
+ Editor.dragEl.circuitEl !== circuitEl ){
+
+ const originCircuitEl = Editor.dragEl.circuitEl
+ Editor.onSelectionChanged( originCircuitEl )
+ Editor.onCircuitChanged( originCircuitEl )
+ }
+
+
+ // We’re finally done here.
+ // Clean up and go home.
+ // It’s been a long journey.
+ // I love you all.
+
+ document.body.removeChild( Editor.dragEl )
+ Editor.dragEl = null
+}
+
+
+ /////////////////////////
+ // //
+ // Pointer DOUBLECLICK //
+ // //
+/////////////////////////
+//ltnln: my trying out an idea for parameterized gates...
+Editor.onDoubleclick = function( event, operationEl ) {
+ const operation = Gate.findBySymbol( operationEl.getAttribute( 'gate-symbol' ))
+ const { x, y } = Editor.getInteractionCoordinates( event )
+ const boardContainerAll = document.querySelectorAll(".Q-circuit-board-container")
+ if( boardContainerAll.length === 0 ) return
+ let boardContainerEl = Array.from(boardContainerAll).find((element) => {
+ let rect = element.getBoundingClientRect()
+ let clientX = rect.left
+ let clientY = rect.top
+ let height = element.offsetHeight
+ let width = element.offsetWidth
+ return ( x >= clientX && x <= clientX + width ) && ( y >= clientY && y <= clientY + height )
+ })
+ if( !boardContainerEl ) return;
+ const parameterEl = boardContainerEl.querySelector('.Q-parameters-box')
+ const exit = Editor.createNewElement( 'button', parameterEl, 'Q-parameter-box-exit')
+ exit.appendChild(document.createTextNode( '⬅' ))
+ parameterEl.setAttribute( "operation-moment-index", operationEl.getAttribute( 'moment-index' ))
+ parameterEl.setAttribute( "operation-register-index", operationEl.getAttribute( 'register-index' ))
+ //here we generate queries for each parameter that the gate operation takes!
+ const parameters = Object.keys(operation.parameters)
+ parameters.forEach( element => {
+ if( operation.parameters && operation.parameters[element] !== null ) {
+ const input_fields = document.createElement( 'div' )
+ parameterEl.appendChild( input_fields )
+ input_fields.classList.add( 'Q-parameter-box-input-container' )
+
+ const label = Editor.createNewElement( "span", input_fields, 'Q-parameter-input-label' )
+ label.appendChild(document.createTextNode( element ))
+
+ const textbox = Editor.createNewElement( "input", input_fields, 'Q-parameter-box-input')
+ textbox.setAttribute( 'type', 'text' )
+ textbox.setAttribute( 'placeholder', element )
+ textbox.setAttribute( 'value', operationEl.getAttribute(element) ? operationEl.getAttribute(element) : operation.parameters[element] )
+ //set textbox to update the operation instance (cellEl)'s parameters on value change
+ textbox.addEventListener( "change", () => {
+ let parameterValue = +textbox.value;
+ let oldValue = operationEl.getAttribute( element )
+ if( !oldValue ) oldValue = operation.parameters[ element ]
+ if( parameterValue === null || parameterValue === Infinity ) textbox.value = oldValue.toString()
+ else {
+ operationEl.setAttribute( element, parameterValue )
+ textbox.value = parameterValue
+ }
+ })
+
+
+ }
+ })
+ parameterEl.classList.toggle('overlay')
+ parameterEl.style.display = 'block'
+}
+
+
+
+ ///////////////////
+ // //
+ // Listeners //
+ // //
+///////////////////
+
+
+// These listeners must be appliedm
+// to the entire WINDOW (and not just document.body!)
+
+window.addEventListener( 'mousemove', Editor.onPointerMove )
+window.addEventListener( 'touchmove', Editor.onPointerMove )
+window.addEventListener( 'mouseup', Editor.onPointerRelease )
+window.addEventListener( 'touchend', Editor.onPointerRelease )
+module.exports = {Editor}
\ No newline at end of file
diff --git a/build/q-old.css b/packages/quantum-js-vis/Q.css
similarity index 94%
rename from build/q-old.css
rename to packages/quantum-js-vis/Q.css
index e4a6620..ebacba8 100644
--- a/build/q-old.css
+++ b/packages/quantum-js-vis/Q.css
@@ -1,6 +1,6 @@
/*
- Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
*/
@charset "utf-8";
@@ -11,8 +11,8 @@
/*
This file is in the process of being separated
- in to “essential global Q.js styles†which will
- remain here in Q.css, and “documentation-specificâ€
+ in to “essential global Q.js styles” which will
+ remain here in Q.css, and “documentation-specific”
styles which will be removed from here and placed
within the /other/documentation.css file instead.
@@ -319,7 +319,7 @@ svg, :root {
/*
- The below still need to be prefaced with “Q-â€
+ The below still need to be prefaced with “Q-”
and for the HTML pages to be updated accordingly.
*/
@@ -341,8 +341,6 @@ svg, :root {
max-width: 100%;
overflow-x: auto;
font-family: var( --Q-font-family-sans );
- /*letter-spacing: 0.03em;*/
- word-spacing: 0.2em;
}
dd .maths {
@@ -395,23 +393,22 @@ dd .maths {
vertical-align: middle;
position: relative;
align: middle;
- margin: 1em 0.5em;
+ margin: 1em;
padding: 1em;
- /*font-family: var( --Q-font-family-mono );*/
+ font-family: var( --Q-font-family-mono );
font-weight: 300;
line-height: 1em;
- /*text-align: right;*/
- text-align: center;
+ text-align: right;
}
.matrix td {
- padding: 0.25em 0.5em;
+ padding: 5px 10px;
}
.matrix-bracket-left, .matrix-bracket-right {
position: absolute;
top: 0;
- width: 0.5em;
+ width: 5px;
height: 100%;
border: 1px solid #CCC;
}
@@ -440,7 +437,7 @@ dd .maths {
.Q-state-vector.bra::before,
.complex-vector.bra::before {
- content: '⟨';
+ content: '⟨';
color: #BBB;
}
.Q-state-vector.bra::after,
@@ -458,7 +455,7 @@ dd .maths {
.Q-state-vector.ket::after,
.complex-vector.ket::after {
- content: '⟩';
+ content: '⟩';
color: #BBB;
}
.Q-state-vector.bra + .Q-state-vector.ket::before,
@@ -475,7 +472,7 @@ dd .maths {
/*
- Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
+ Copyright © 2019–2020, Stewart Smith. See LICENSE for details.
*/
@charset "utf-8";
@@ -487,6 +484,7 @@ dd .maths {
+
/*
Z indices:
@@ -569,12 +567,11 @@ dd .maths {
-
.Q-circuit,
.Q-circuit-palette {
position: relative;
- width: 50%;
+ width: 100%;
}
.Q-circuit-palette {
@@ -598,7 +595,6 @@ dd .maths {
margin: 1rem 0 2rem 0;
/*border-top: 2px solid hsl( 0, 0%, 50% );*/
}
-.Q-parameters-box,
.Q-circuit-board-foreground {
line-height: 3.85rem;
@@ -751,19 +747,6 @@ dd .maths {
grid-auto-columns: 4rem;
grid-auto-flow: column;
}
-
-.Q-parameters-box {
-
- position: absolute;
- display: none;
- z-index: 100;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: whitesmoke;
-}
-
/*.Q-circuit-palette,*/
.Q-circuit-board-foreground,
.Q-circuit-board-background {
@@ -853,17 +836,8 @@ dd .maths {
);
}
-.Q-parameter-box-exit {
- position: relative;
- right: 0;
- left: 0;
- width: 5rem;
- height: 2.5rem;
- background-color: whitesmoke;
- border-radius: 0;
-}
-.Q-parameters-box > div,
+
.Q-circuit-palette > div,
.Q-circuit-clipboard > div,
.Q-circuit-board-foreground > div {
@@ -1105,7 +1079,7 @@ dd .maths {
rgba( 0, 0, 0, 0.05 )
)*/;
}
-.Q-parameter-box-exit .Q-circuit-palette .Q-circuit-operation:hover {
+.Q-circuit-palette .Q-circuit-operation:hover {
/*background-color: rgba( 255, 255, 255, 0.6 );*/
background-color: white;
@@ -1218,25 +1192,6 @@ dd .maths {
}
-.Q-parameter-box-input-container {
- position: relative;
- text-align: center;
- grid-auto-columns: 4rem;
- grid-auto-flow: column;
-}
-
-.Q-parameter-box-input {
- position: relative;
- border-radius: .2rem;
- margin-left: 10px;
- font-family: var( --Q-font-family-mono );
-}
-
-.Q-parameter-input-label {
- position: relative;
- color: var( --Q-color-blue );
- font-family: var( --Q-font-family-mono );
-}
diff --git a/packages/quantum-js-vis/index.js b/packages/quantum-js-vis/index.js
new file mode 100644
index 0000000..e993585
--- /dev/null
+++ b/packages/quantum-js-vis/index.js
@@ -0,0 +1,92 @@
+const {Editor} = require('./Q-Circuit-Editor');
+const {circuit} = require('quantum-js-util');
+const {BlochSphere} = require('./Q-BlochSphere');
+console.log("Welcome to Q.js! The GUI experience!\n");
+
+braket = function(){
+
+
+ // Create the HTML bits we need,
+ // contain them all together,
+ // and output them to Jupyter.
+ if( arguments.length === 0 || arguments.length > 3 ) return;
+ const element = arguments[0];
+ const args = (Array.from(arguments)).slice(1);
+ let circuit
+ if(args.length === 0) {
+ circuit = new Q( 4, 8 )
+ }
+ else if(args.length === 1) {
+ circuit = new Q( args[0] )
+ }
+ else {
+ if(args[0] <= 0 || args[1] <= 0) circuit = new Q(4, 8);
+ else circuit = new Q( args[0], args[1] )
+ }
+ container = document.createElement( 'div' )
+ let paletteEl = Editor.createPalette();
+ paletteEl.style.width = "50%";
+ container.appendChild( paletteEl );
+ 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( 'Circuit.evaluate completed', function( event ) {
+ if( event.detail.circuit === circuit ) {
+ nextNextCell.set_text( circuit.report$() )
+ }
+ })
+
+
+
+ // nextCell.render()
+
+ // console.log( 'thisCell', thisCell )
+ // console.log( 'nextCell', nextCell )
+ // console.log( 'thisCellIndex', thisCellIndex )
+
+ // code = Jupyter.notebook.insert_cell_{0}('code');
+ // code.set_text(atob("{1}"))
+
+ // var t_cell = Jupyter.notebook.get_selected_cell()
+ // t_cell.set_text(' \\n{}')
+ // var t_index = Jupyter.notebook.get_cells().indexOf(t_cell)
+ // Jupyter.notebook.to_markdown(t_index)
+ // Jupyter.notebook.get_cell(t_index).render()
+}
+
+
+module.exports = {Editor, BlochSphere, braket};
\ No newline at end of file
diff --git a/packages/quantum-js-vis/package-lock.json b/packages/quantum-js-vis/package-lock.json
new file mode 100644
index 0000000..95e01ab
--- /dev/null
+++ b/packages/quantum-js-vis/package-lock.json
@@ -0,0 +1,35 @@
+{
+ "name": "quantum-js-vis",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {
+ "prettier": "^2.3.2"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
+ "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ }
+ },
+ "dependencies": {
+ "prettier": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
+ "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
+ "dev": true
+ }
+ }
+}
diff --git a/packages/quantum-js-vis/package.json b/packages/quantum-js-vis/package.json
new file mode 100644
index 0000000..f17495b
--- /dev/null
+++ b/packages/quantum-js-vis/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "quantum-js-vis",
+ "version": "1.0.0",
+ "description": "Visualization Components for Q.js",
+ "main": "index.js",
+ "scripts": {
+ "test": "jest",
+ "prettier": "npx prettier --write **/*.js" ,
+ "lint": "npx eslint **/*.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "prettier": "^2.3.2",
+ "window": "^4.2.7"
+
+ },
+ "dependencies": {
+ "requirejs": "^2.3.6"
+ }
+}
diff --git a/playground.html b/playground.html
index 10543ca..47aeead 100644
--- a/playground.html
+++ b/playground.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
@@ -246,7 +239,7 @@ Whiplash
.from( document.querySelectorAll( '.Q-circuit-palette' ))
.forEach( function( el ){
- Q.Circuit.Editor.createPalette( el )
+ Editor.createPalette( el )
})
@@ -291,7 +284,7 @@ Whiplash
document.getElementById( 'playground-apply-button' ).setAttribute( 'disabled', 'disabled' )
const circuit = Q( text )
- if( circuit instanceof Q.Circuit ){//+++++ This validation appears broken!
+ if( circuit instanceof Circuit ){//+++++ This validation appears broken!
circuit.name = 'playground'
const domEl = document.getElementById( 'playground' )
@@ -371,7 +364,7 @@ Whiplash
// EVALUATION.
-window.addEventListener( 'Q.Circuit.evaluate began', function( event ){
+window.addEventListener( 'Circuit.evaluate began', function( event ){
console.log(
@@ -379,7 +372,7 @@ Whiplash
event.detail.circuit.toDiagram() +'\n\n'
)
})
-window.addEventListener( 'Q.Circuit.evaluate progressed', function( event ){
+window.addEventListener( 'Circuit.evaluate progressed', function( event ){
const
length = 20,
@@ -408,7 +401,7 @@ Whiplash
// console.log( 'state width', state.getWidth(), 'state height', state.getHeight() )
// console.log( 'state', state.toTsv() )
})
-window.addEventListener( 'Q.Circuit.evaluate completed', function( event ){
+window.addEventListener( 'Circuit.evaluate completed', function( event ){
console.log(
diff --git a/resources.html b/resources.html
index 21da767..bfb88dc 100644
--- a/resources.html
+++ b/resources.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+
diff --git a/tutorials.html b/tutorials.html
index 9d6a2e5..7218945 100644
--- a/tutorials.html
+++ b/tutorials.html
@@ -40,20 +40,13 @@
-
-
+
+
-
-
-
-
-
-
-
-
+