quantum-circuit


Quantum circuit simulator implemented in javascript. Smoothly runs 20-qubit simulations on laptop. Can run in browser or at server (node.js). No UI: you can use it in your program to run quantum simulations. Circuit can be imported from and exported to OpenQASM. Circuit drawing can be exported to SVG vector image.

About algorithm

Memory usage: up to 2 * (2^numQubits) * sizeOfComplexNumber

  • Naive implementation stores entire state vector in an array of size 2^numQubits. We are storing state in a "map", and only amplitudes with non-zero probabilities are stored. So, in worst case, size of state map is 2^n, but it's less most of the time because we don't store zeroes.

  • Naive implementation creates transformation matrix and multiplies it with state vector. We are not creating and not storing entire transformation matrix in memory. Instead, elements of transformation matrix are calculated one by one and state is multiplied and stored in new state map on the fly. This way, memory usage is minimal (in worst case we have two 2^n state vectors at a time).

  • Algorithm is parallelizable so it could use GPU, but GPU support is not implemented yet (work in progress).

Benchmark 1

Benchmark 2

Using in browser

Simply include quantum-circuit.min.js into your html page (available via unpkg CDN https://unpkg.com/quantum-circuit)

        Quantum Circuit Simulator Example
        <script type="text/javascript" src="https://unpkg.com/quantum-circuit"></script> 
        <script type="text/javascript">

See live example

Using at server with node.js

Install quantum-circuit npm module:

npm install --save quantum-circuit

And then import it into your program:

Getting started

Create instance of QuantumCircuit class passing number of qubits (wires) to constructor:

Note: number of qubits is optional argument - circuit will expand automatically if you add gates to non-existing wires

Add single-qubit gates

Call addGate method passing gate name, column index and qubit (wire) index:

For example, to add Hadamard gate as a first gate (column 0) at second qubit (wire 1) type:

Result is:

 Column 0 Wire 0 ----...---- |---| Wire 1 ---| H |--- |---| 

Note: if column is negative integer then gate will be added to the end of the wire

Add multi-qubit gates

Call addGate method passing gate name, column index and array of connected qubits (wires):

For example, to add CNOT as a second gate (column 1) controlled by second qubit (wire 1) at third qubit as target (wire 2) do:

 Column 0 Column 1 Wire 0 ----...---------...----- Wire 1 ----...----------o------ | |-----| Wire 2 ----...-------| CX |--- |-----| 

Note: if column is negative integer then gate will be added to the end

Implemented gates

Single-qubit gates

  • h Hadamard gate
  • x Pauli X (PI rotation over X-axis) aka "NOT" gate
  • y Pauli Y (PI rotation over Y-axis)
  • z Pauli Z (PI rotation over Z-axis)
  • r2 PI/2 rotation over Z-axis aka "Phase PI/2"
  • r4 PI/4 rotation over Z-axis aka "Phase PI/4"
  • r8 PI/8 rotation over Z-axis aka "Phase PI/8"
  • s PI/2 rotation over Z-axis (synonym for r2)
  • t PI/4 rotation over Z-axis (synonym for r4)
  • sdg (-PI/2) rotation over Z-axis
  • tdg (-PI/4) rotation over Z-axis
  • srn Square root of NOT
  • measure Measures qubit and stores chance (0 or 1) into classical bit

Two-qubit gates

  • swap Swap
  • srswap Square root of Swap
  • ch Controlled Hadamard gate
  • cx Controlled Pauli X (PI rotation over X-axis) aka "CNOT" gate
  • cy Controlled Pauli Y (PI rotation over Y-axis)
  • cz Controlled Pauli Z (PI rotation over Z-axis)
  • cr2 Controlled PI/2 rotation over Z-axis aka "Phase PI/2"
  • cr4 Controlled PI/4 rotation over Z-axis aka "Phase PI/4"
  • cr8 Controlled PI/8 rotation over Z-axis aka "Phase PI/8"
  • cs Controlled PI/2 rotation over Z-axis (synonym for cr2)
  • ct Controlled PI/4 rotation over Z-axis (synonym for cr4)
  • csdg Controlled (-PI/2) rotation over Z-axis
  • ctdg Controlled (-PI/4) rotation over Z-axis
  • csrn Controlled Square root of NOT

Three-qubit gates

  • ccx Toffoli aka "CCNOT" gate
  • cswap Controlled Swap aka Fredkin gate
  • csrswap Controlled Square root of Swap

Run circuit

Simply call run method.

Initial state

By default, initial state of each qubit is |0>. You can pass initial values as array of bool (true or false) or integers (0 or 1). This will set first two qubits to |1> and evaluate circuit:

Measurement

Method measure(wire) returns chance of being 1 for given qubit:

Example:

Note: method measure will return real number betwen 0 and 1.

You can store measurement into classical register. For example, to measure first qubit (wire 0) and store result into classical register named c as fourth bit (bit 3):

Also, you can add measure gate to circuit and then measurement will be done automatically and result will be stored into classical register:

Short form of writing this is addMeasure(wire, creg, cbit):

Note:

  • If specified classical register doesn't exists - it will be created automatically.

  • Equal probability (0.5) will be stored as random 0 or 1

Classical registers

Create register

Classical registers are created automatically if you add measurement gate to the circuit but you can also manually create registers by calling createCreg(name, len).

Example: create classical 5-bit register named ans:

Read register

To get register value as integer, call getCregValue(name).

Example:

Read single bit

Example: get bit 3 from register named ans:

console.log(circuit.getCregBit("ans", 3));

Returns integer: 0 or 1

Set single bit

Example: set bit 3 to 1 in register named ans:

circuit.setCregBit("ans", 3, 1);

View/print final amplitudes

You can get state as string with method stateAsString(onlyPossible):

If you want only possible values (only values with probability > 0) then pass true:

Or, you can print state to javascript console with method print(onlyPossible):

If you want to print only possible values (only values with probability > 0) then pass true:

Export/Import circuit

You can export circuit by calling save method:

And load previously saved circuit by calling load method:

Use circuit as a gate in another circuit

You can "compile" any circuit and use it as a gate in another circuit like this:

Decompose circuit

If your circuit contains custom gates (created from another circuit), you can decompose it into equivalent circuit containing only basic gates.

If you pass true as argument to function save, you'll get decomposed circuit.

Example:

Export to QASM

Circuit can be exported to OpenQASM with following limitation:

  • at the moment, gates not directly supported by QASM and qelib1.inc are exported as-is - their definition is not generated. TODO

To export circuit to OpenQASM use exportQASM(comment, decompose) method:

Example:

  • comment - comment to insert at the beginning of the file

  • decompose - if set to true and circuit contains custom gates then it will be decomposed to basic gates and then exported. If set to false then custom gates will be exported as user defined gates.

Import from QASM

Circuit can be imported from OpenQASM with following limitations:

  • import directive is ignored (but most of gates defined in qelib1.inc are supported) TODO

  • Gates with params like U(theta, phi, lambda) are ignored. TODO

  • if statement is ignored. TODO

  • barrier is ignored. TODO

  • reset is ignored. TODO

To import circuit from OpenQASM use importQASM(input) method:

Example:

  • input is string containing QASM source code.

Export to SVG

Vector .svg image of circuit can be created with exportSVG(embedded) function with following limitations:

  • Integer registers are not drawn. TODO

  • Gate symbols are non-standard. TODO (BTW, do we have standard?)

  • Not yet tested well. TODO

Example 1

Show circuit in browser:

var container = document;
containerinnerHTML = svg;

Example 2

Generate standalone SVG image at server with node.js:

Export to Quirk

Circuit can be exported to popular open-source drag-and-drop quantum circuit simulator Quirk with following limitations:

  • Quirk doesn't support more than 16 qubits.

  • Quirk can possibly incorrectly interpret circuit if we have multiple controlled gates in the same column.

  • Quirk doesn't support non-sequentially positioned multi-qubit user-defined gates (for example gate on wires [3, 0, 1]) so it's best to export decomposed circuit.

Example:

var quirkURL = "http://algassert.com/quirk#circuit=" + JSON;
var quirkLink = document;

API docs

To be written...

License

MIT