Skip to content

The staq tool suite

Scott Xu edited this page Oct 1, 2021 · 12 revisions

Along with a monolithic command line tool for performing passes on QASM code, the build targets of staq also includes a suite of individual, light-weight command line tools which can be chained together using unix-style piping to perform a range of compilation tasks. This page details the included tools and their usage.

Overview

The basic workflow with the staq tool suite is via I/O redirection:

cat circuit.qasm | tool1 | tool2 > circuit_out.qasm

Each tool accepts OpenQASM (with extensions) source code via standard input and outputs OpenQASM on standard output. The exceptions are the compiler tools, which output source code in the relevant language on standard output, as well as the resource estimator, and the device generator.

The tool suite is most useful for composing staq circuit transformations with external OpenQASM-based tools in simple shell scripts. Using the tool suite also allows developers to pick-and-choose the particular transformations needed, and by extension the parts of the library to be compiled.

List of tools

Tool descriptions & usage

Desugaring

Desugars the OpenQASM source. Typically this means expanding out gates applied to registers.


Usage:

staq_desugarer < FILE.qasm

Example:

foo@bar$ cat desugar_example.qasm
OPENQASM 2.0;

qreg a[2];
qreg b[2];
CX a,b;

foo@bar$ staq_desugar < desugar_example.qasm
OPENQASM 2.0;

qreg a[2];
qreg b[2];
CX a[0],b[0];
CX a[1],b[1];

Inlining

Inlines the OpenQASM source code. By default only non-standard gate declarations are inlined -- that is, declarations from qelib1.inc, including standard Pauli, Clifford, and single qubit gates, are not inlined.


Usage:

staq_inliner [OPTIONS] < FILE.qasm
Option Description
--clear-decls Setting this flag will cause the inliner to remove the inlined gate declarations in the source
--inline-stdlib Setting this flag will inline all gate declarations, including those in qelib1.inc
--ancilla-name STRING                 This option sets the name of the ancilla register to hold all local ancillas

Example:

foo@bar$ cat inline_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

gate swap x,y {
  cx x,y;
  cx y,x;
  cx x,y;
}

qreg q[2];
swap q[0],q[1];


foo@bar$ staq_inliner < inline_example.qasm
include "qelib1.inc";

gate swap x,y {
  cx x,y;
  cx y,x;
  cx x,y;
}

qreg q[2];
cx q[0],q[1];
cx q[1],q[0];
cx q[0],q[1];

Oracle synthesis

Synthesizes oracles declared by verilog files. If successful, all oracle declarations in the input file are replaced with regular gate declarations.


Usage:

staq_oracle_synthesizer < FILE.qasm

Example:

foo@bar$ cat or.v
module top ( a, b, c );
  input a,b;
  output c;
  assign c = a | b;
endmodule

foo@bar$ cat oracle_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

oracle OR x,y,z { "or.v" }

foo@bar$ staq_oracle_synthesizer < oracle_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

gate OR x,y,z { 
  h z;
  cx x,z;
  t z;
  cx y,z;
  t z;
  cx x,z;
  tdg z;
  cx y,z;
  tdg z;
  cx y,x;
  tdg x;
  cx y,x;
  t y;
  tdg x;
  h z;
  cx y,z;
}

Circuit simplification

Applies basic circuit simplifications. In particular, the simplifier cancels adjacent inverse gates.


Usage:

staq_simplifier [OPTIONS] < FILE.qasm
Option Description
--no-fixpoint Prevents the simplifier from iterating until changes are no longer made

Example:

foo@bar$ cat simplification_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];

h q[0];
h q[0];

cx q[0],q[1];
barrier q[1];
cx q[0],q[1];

foo@bar$ staq_simplifier < simplification_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];

cx q[0],q[1];
barrier q[1];
cx q[0],q[1];

Rotation optimization

Applies an optimization to reduce the number of small-angle rotation gates in all Pauli bases. The optimization in based on Zhang, F. and Chen, J. Optimizing T gates in Clifford+T circuit as π/4 rotations around Paulis, with extensions to allow arbitrary gates as well as to optimize rotation gates of arbitrary angles.

Note: the rotation optimizer currently replaces symbolic angle expressions with floating point numbers in merged gates.


Usage:

staq_rotation_optimizer [OPTIONS] < FILE.qasm
Option Description
--no-phase-correction                 Disables the phase correction for the global phase added in some cases by the algorithm

Example:

foo@bar$ cat rotation_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

qreg q[3];

t q[0];
t q[0];

rx(pi/4) q[1];
h q[1];
rz(pi/4) q[1];
h q[1];

t q[2];
x q[2];
t q[2];
x q[2];

foo@bar$ staq_rotation_optimizer < rotation_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];

s q[0];

h q[1];
rz(1.5708) q[1];
h q[1];

x q[2];
h q[2];
s q[2];
h q[2];
s q[2];
h q[2];
s q[2];
x q[2];

Physical device mapping

The mapper rewrites the input circuit to fit the constraints of a particular physical device. Physical devices are defined by a number of qubits and a connectivity map giving the available qubit-qubit couplings. Optionally, devices may give average fidelities of single qubit gates on each qubit, and an average fidelity of each coupling.

Given a particular device, the mapper generates an initial layout of the circuit onto the physical hardware by selecting which physical qubit each logical qubit is mapped to. Then the circuit is rewritten so that all CX gates are between coupled qubits (with the correct direction).

Note: The mapper also fully inlines circuits to the builtin U, CX gate set

Hint: Device topologies are encoded in a particular JSON format. Example devices are in the qpus folder. New devices can be created using the device generator.


Usage:

staq_mapper [OPTIONS] < FILE.qasm
Option Description
-d,--device Device to map the circuit onto
-l (linear⎮eager⎮bestfit) Algorithm used to generate an initial layout (default=linear)
-m (swap⎮steiner) Algorithm used to perform CX mapping (default=swap)

Example:

foo@bar$ cat mapping_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

qreg x[5];
qreg y[4];

cx x[0],y[0];
cx x[1],y[1];
cx x[2],y[2];
cx x[3],y[3];
cx x[4],y[0];

foo@bar$ staq_mapper -d square_9q.json -l eager -m swap < mapper_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

qreg q[9];

CX q[0],q[1];
CX q[2],q[5];
CX q[3],q[4];
CX q[6],q[7];
CX q[8],q[5];
CX q[5],q[8];
CX q[8],q[5];
CX q[5],q[2];
CX q[2],q[5];
CX q[5],q[2];
CX q[2],q[1];

Device Generation

Given a device topology, the device generator creates a JSON file for it.

Hint: Example devices, with scripts used to generate them, are in the qpus folder.


Usage:

staq_device_generator [OPTION]
Option Description
-r INT [INT] Qubits are arranged in a rectangular lattice
-c INT Qubits are arranged in a circle
-l INT Qubits are arranged linearly

For an exact device specification, use the graph subcommand:

staq_device_generator graph [OPTIONS]
Option Description
-n INT Number of qubits (required)
--name TEXT Device name
-f INT DOUBLE Specify the fidelity for a single qubit
-d INT INT Specify a directed qubit-qubit coupling
-D INT INT DOUBLE Specify a directed qubit-qubit coupling with a fidelity
-u INT INT Specify an undirected qubit-qubit coupling
-U INT INT DOUBLE Specify an undirected qubit-qubit coupling with a fidelity

Example:

foo@bar$ staq_device_generator graph -n 3 -d 0 1 -u 1 2
{
  "couplings": [
    {
      "control": 0,
      "target": 1
    },
    {
      "control": 1,
      "target": 2
    },
    {
      "control": 2,
      "target": 1
    }
  ],
  "name": "Custom device",
  "qubits": [
    {
      "id": 0
    },
    {
      "id": 1
    },
    {
      "id": 2
    }
  ]
}

Resource estimation

The resource estimator takes a QASM circuit and prints out circuit statistics, notably the number of bits/qubits, the number of each type of gate and the circuit depth. By default, all gates declared in qelib1.inc are considered atomic and are not decomposed into lower-level gates (i.e., U and CX) to assess gate counts.


Usage:

staq_resource_estimator [OPTIONS] < FILE.qasm
Option Description
--box-gates Counts all gate applications as atomic. That is, does not count the resources associated with the decomposition of a gate
--unbox-qelib When --box-gates is not turn on, also unboxes gates from the standard library
--no-merge-dagger                             Counts gate and their inverses separately

Example:

foo@bar$ cat resource_example.qasm
OPENQASM 2.0;
include "qelib1.inc";

gate foo x,y,z {
  ccx x,y,z;
  cx x,y;
}

qreg q[3];

t q[0];
tdg q[2];
foo q[0],q[1],q[2];

foo@bar$ staq_resource_estimator < resource_example.qasm
Resources used:
  cx: 1
  depth: 3
  ccx: 1
  qubits: 3
  t: 2

Compilation

The staq library includes a number of compilers or transpilers for translating OpenQASM to other common circuit description languages. The compilers are designed to keep the original structure of the QASM source as much as possible, using comparable idioms in the target language -- e.g., the ProjectQ transpiler translates gate declarations into ProjectQ gate classes rather than simple Python functions. Additionally, when possible built in gates are used to implement gates from qelib1.inc, rather than by the qelib1.inc gate decompositions.

Individual compilers are described in more detail below.

Quil

Quil is a quantum instruction set similar to QASM developed by Rigetti. Quil code can be simulat with the quilc compiler and executed on the Rigetti QVM. As both languages are designed as primitive instruction sets, the translation is largely one-to-one.


Usage:

staq_quil < FILE.qasm

Example:

Click to expand

foo@bar$ cat teleport.qasm
OPENQASM 2.0;
include "qelib1.inc";
 
gate bell x,y {
  h x;
  cx x,y;
}

qreg q[1];
qreg anc[2];
creg c0[1];
creg c1[1];

bell anc[0],anc[1];
cx q,anc[0];
h q;
measure q -> c0[0];
measure anc[0] -> c1[0];
if(c0==1) z anc[1];
if(c1==1) x anc[1];

foo@bar$ staq_quil < teleport.qasm
DEFGATE X:
    0, 1
    1, 0

DEFGATE CNOT:
    1, 0, 0, 0
    0, 1, 0, 0
    0, 0, 0, 1
    0, 0, 1, 0

DEFGATE U(%theta, %phi, %lambda):
    EXP(-i*(%phi+%lambda)/2)*COS(%theta/2), -EXP(-i*(%phi-%lambda)/2)*SIN(%theta/2)
    EXP(i*(%phi-%lambda)/2)*SIN(%theta/2), EXP(i*(%phi+%lambda)/2)*COS(%theta/2)

DEFCIRCUIT CLEAR q scratch_bit:
    MEASURE q scratch_bit
    JUMP-UNLESS @end scratch_bit
    X q
    LABEL @end

DEFCIRCUIT u3(%theta, %phi, %lambda) q:
    U(%theta, %phi, %lambda) q

DEFCIRCUIT u2(%phi, %lambda) q:
    U(pi/2, %phi, %lambda) q

DEFCIRCUIT u1(%lambda) q:
    U(0, 0, %lambda) q

DEFCIRCUIT cx c t:
    CNOT c t

DEFCIRCUIT id a:
    U(0, 0, 0) a

DEFCIRCUIT u0(%gamma) q:
    U(0, 0, 0) q

DEFCIRCUIT x a:
    u3(pi, 0, pi) a

DEFCIRCUIT y a:
    u3(pi, pi/2, pi/2) a

DEFCIRCUIT z a:
    RZ(pi) a

DEFCIRCUIT h a:
    u2(0, pi) a

DEFCIRCUIT s a:
    RZ(pi/2) a

DEFCIRCUIT sdg a:
    RZ(-(pi/2)) a

DEFCIRCUIT t a:
    RZ(pi/4) a

DEFCIRCUIT tdg a:
    RZ(-(pi/4)) a

DEFCIRCUIT rx(%theta) a:
    u3(%theta, -(pi/2), pi/2) a

DEFCIRCUIT ry(%theta) a:
    u3(%theta, 0, 0) a

DEFCIRCUIT rz(%phi) a:
    RZ(%phi) a

DEFCIRCUIT cz a b:
    H b
    CNOT a b
    H b

DEFCIRCUIT cy a b:
    DAGGER S b
    CNOT a b
    S b

DEFCIRCUIT swap a b:
    CNOT a b
    CNOT b a
    CNOT a b

DEFCIRCUIT ch a b:
    H b
    DAGGER S b
    CNOT a b
    H b
    T b
    CNOT a b
    T b
    H b
    S b
    X b
    S a

DEFCIRCUIT ccx a b c:
    H c
    CNOT b c
    DAGGER T c
    CNOT a c
    T c
    CNOT b c
    DAGGER T c
    CNOT a c
    T b
    T c
    H c
    CNOT a b
    T a
    DAGGER T b
    CNOT a b

DEFCIRCUIT crz(%lambda) a b:
    RZ(%lambda/2) b
    CNOT a b
    RZ(-(%lambda/2)) b
    CNOT a b

DEFCIRCUIT cu1(%lambda) a b:
    RZ(%lambda/2) a
    CNOT a b
    RZ(-(%lambda/2)) b
    CNOT a b
    RZ(%lambda/2) b

DEFCIRCUIT cu3(%theta, %phi, %lambda) c t:
    RZ((%lambda-%phi)/2) t
    CNOT c t
    u3(-(%theta/2), 0, -((%phi+%lambda)/2)) t
    CNOT c t
    u3(%theta/2, %phi, 0) t

DEFCIRCUIT bell x y:
    H x
    CNOT x y

bell 1 2
CNOT 0 1
H 0
MEASURE 0 0
MEASURE 1 1
JUMP-UNLESS @end430 [0]
Z 2
LABEL @end430
JUMP-UNLESS @end434 [1]
X 2
LABEL @end434

ProjectQ

ProjectQ is a Python library for quantum computing. It provides an API for writing, transforming and simulating quantum circuits. The compiler currently compiles to a standalone Python/ProjectQ program.


Usage:

staq_projectq < FILE.qasm

Example:

Click to expand

foo@bar$ cat teleport.qasm
OPENQASM 2.0;
include "qelib1.inc";
 
gate bell x,y {
  h x;
  cx x,y;
}

qreg q[1];
qreg anc[2];
creg c0[1];
creg c1[1];

bell anc[0],anc[1];
cx q,anc[0];
h q;
measure q -> c0[0];
measure anc[0] -> c1[0];
if(c0==1) z anc[1];
if(c1==1) x anc[1];

foo@bar$ staq_projectq < teleport.qasm
from projectq import MainEngine, ops
from cmath import pi,exp,sin,cos,tan,log as ln,sqrt
import numpy as np

def SdagGate(): return ops.Sdag
def TdagGate(): return ops.Tdag
def CNOTGate(): return ops.CNOT
def CZGate(): return ops.CZ
def CCXGate(): return ops.Toffoli

class UGate(ops.BasicGate):
    def __init__(self, theta, phi, lambd):
        ops.BasicGate.__init__(self)
        self.theta = theta
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," \
               + str(self.phi) + "," + str(self.lambd) + ")"

    def tex_str(self):
        return str(self.__class__.__name__) + "$(" + str(self.theta) + "," \
               + str(self.phi) + "," + str(self.lambd) + ")$"

    def get_inverse(self):
        tmp = 2 * pi
        return self.__class__(-self.theta + tmp, -self.lambd + tmp, -self.phi + tmp)

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.theta == other.theta \
                   & self.phi == other.phi \
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    @property
    def matrix(self):
        return np.matrix([[exp(-1j*(self.phi+self.lambd)/2)*cos(self.theta/2),
                           -exp(-1j*(self.phi-self.lambd)/2)*sin(self.theta/2)],
                          [exp(1j*(self.phi-self.lambd)/2)*sin(self.theta/2),
                           exp(1j*(self.phi+self.lambd)/2)*cos(self.theta/2)]])

class ResetGate(ops.FastForwardingGate):
    def __str__(self):
        return "Reset"

    def __or__(self, qubit):
        ops.Measure | qubit
        if int(qubit):
            ops.X | qubit
Reset = ResetGate()

class u3(ops.BasicGate):
    def __init__(self, theta, phi, lambd):
        ops.BasicGate.__init__(self)
        self.theta = theta
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.theta == other.theta\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        UGate(theta, phi, lambd) | q

class u2(ops.BasicGate):
    def __init__(self, phi, lambd):
        ops.BasicGate.__init__(self)
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        UGate(pi/2, phi, lambd) | q

class u0(ops.BasicGate):
    def __init__(self, gamma):
        ops.BasicGate.__init__(self)
        self.gamma = gamma

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.gamma) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.gamma == other.gamma
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        UGate(0, 0, 0) | q

class cy(ops.BasicGate):
    def __init__(self, ):
        ops.BasicGate.__init__(self)

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        SdagGate() | (b)
        CNOTGate() | (a, b)
        ops.SGate() | (b)

class swap(ops.BasicGate):
    def __init__(self, ):
        ops.BasicGate.__init__(self)

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        CNOTGate() | (a, b)
        CNOTGate() | (b, a)
        CNOTGate() | (a, b)

class ch(ops.BasicGate):
    def __init__(self, ):
        ops.BasicGate.__init__(self)

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        ops.HGate() | (b)
        SdagGate() | (b)
        CNOTGate() | (a, b)
        ops.HGate() | (b)
        ops.TGate() | (b)
        CNOTGate() | (a, b)
        ops.TGate() | (b)
        ops.HGate() | (b)
        ops.SGate() | (b)
        ops.XGate() | (b)
        ops.SGate() | (a)

class cu3(ops.BasicGate):
    def __init__(self, theta, phi, lambd):
        ops.BasicGate.__init__(self)
        self.theta = theta
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.theta == other.theta\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        c = qubits[0]
        t = qubits[1]

        ops.Rz((lambd-phi)/2) | (t)
        CNOTGate() | (c, t)
        u3(-(theta/2), 0, -((phi+lambd)/2)) | (t)
        CNOTGate() | (c, t)
        u3(theta/2, phi, 0) | (t)

class bell(ops.BasicGate):
    def __init__(self, ):
        ops.BasicGate.__init__(self)

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        x = qubits[0]
        y = qubits[1]

        ops.HGate() | (x)
        CNOTGate() | (x, y)

if __name__ == "__main__":
    eng = MainEngine()
    q = eng.allocate_qureg(1)
    anc = eng.allocate_qureg(2)
    c0 = [None] * 1
    c1 = [None] * 1
    bell() | (anc[0], anc[1])
    CNOTGate() | (q[0], anc[0])
    ops.HGate() | (q[0])
    ops.Measure | q[0]
    c0[0] = int(q[0])
    ops.Measure | anc[0]
    c1[0] = int(anc[0])
    if sum(v<<i for i, v in enumerate(c0[::-1])) == 1:
        ops.ZGate() | (anc[1])
    if sum(v<<i for i, v in enumerate(c1[::-1])) == 1:
        ops.XGate() | (anc[1])

Q#

Q# is a quantum programming language developed by Microsoft. The QDK can be used to compile and simulate Q# code using the .NET runtime. Compiled circuits can be directly imported into Q# projects and accessed via the default namespace Quantum.staq.


Usage:

staq_qsharp < FILE.qasm

Example:

Click to expand

foo@bar$ cat teleport.qasm
OPENQASM 2.0;
include "qelib1.inc";
 
gate bell x,y {
  h x;
  cx x,y;
}

qreg q[1];
qreg anc[2];
creg c0[1];
creg c1[1];

bell anc[0],anc[1];
cx q,anc[0];
h q;
measure q -> c0[0];
measure anc[0] -> c1[0];
if(c0==1) z anc[1];
if(c1==1) x anc[1];

foo@bar$ staq_qsharp < teleport.qasm
namespace Quantum.staq {
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Math;

    operation U(theta : Double, phi : Double, lambda : Double, q : Qubit) : Unit {
        Rz(lambda, q);
        Ry(theta, q);
        Rz(phi, q);
    }

    operation u3(theta : Double, phi : Double, lambda : Double, q : Qubit) : Unit {
        U(theta, phi, lambda, q);
    }

    operation u2(phi : Double, lambda : Double, q : Qubit) : Unit {
        U(PI()/2.0, phi, lambda, q);
    }

    operation u0(gamma : Double, q : Qubit) : Unit {
        U(0.0, 0.0, 0.0, q);
    }

    operation cy(a : Qubit, b : Qubit) : Unit {
        (Adjoint S)(b);
        CNOT(a, b);
        S(b);
    }

    operation swap(a : Qubit, b : Qubit) : Unit {
        CNOT(a, b);
        CNOT(b, a);
        CNOT(a, b);
    }

    operation cu3(theta : Double, phi : Double, lambda : Double, c : Qubit, t : Qubit) : Unit {
        Rz((lambda-phi)/2.0, t);
        CNOT(c, t);
        u3(-(theta/2.0), 0.0, -((phi+lambda)/2.0), t);
        CNOT(c, t);
        u3(theta/2.0, phi, 0.0, t);
    }

    operation bell(x : Qubit, y : Qubit) : Unit {
        H(x);
        CNOT(x, y);
    }

    operation Circuit() : Unit {
        using (q = Qubit[1]) {
            using (anc = Qubit[2]) {
                mutable c0 = new Result[1];
                mutable c1 = new Result[1];
                bell(anc[0], anc[1]);
                CNOT(q[0], anc[0]);
                H(q[0]);
                set c0 w/= 0 <- M(q[0]);
                set c1 w/= 0 <- M(anc[0]);
                if (ResultArrayAsInt(c0) == 1) {// if (c0==1) z anc[1];
                    Z(anc[1]);
                }
                if (ResultArrayAsInt(c1) == 1) {// if (c1==1) x anc[1];
                    X(anc[1]);
                }

                ResetAll(anc);
            }
            ResetAll(q);
        }
    }
}

Cirq

Cirq is a Python library for quantum computing. Like ProjectQ, Cirq supports the writing, transforming and simulation of quantum circuits. It is newer and less developed than the other languages described in this document, and in particular as of 10/29/2019 does not support circuits with classical control.


Usage:

staq_cirq < FILE.qasm

Example:

Click to expand

foo@bar$ cat teleport.qasm
OPENQASM 2.0;
include "qelib1.inc";
 
gate bell x,y {
  h x;
  cx x,y;
}

qreg q[1];
qreg anc[2];
creg c0[1];
creg c1[1];

bell anc[0],anc[1];
cx q,anc[0];
h q;
measure q -> c0[0];
measure anc[0] -> c1[0];
//if(c0==1) z anc[1];
//if(c1==1) x anc[1];

foo@bar$ staq_cirq < teleport.qasm
import cirq
import numpy as np
from cmath import pi,exp,sin,cos,tan,log as ln,sqrt

class UGate(cirq.SingleQubitMatrixGate):
    def __init__(self, theta, phi, lambd):
        mat = np.matrix([[exp(-1j*(phi+lambd)/2)*cos(theta/2),
                          -exp(-1j*(phi-lambd)/2)*sin(theta/2)],
                         [exp(1j*(phi-lambd)/2)*sin(theta/2),
                          exp(1j*(phi+lambd)/2)*cos(theta/2)]])
        cirq.SingleQubitMatrixGate.__init__(self, mat)
        self.theta = theta
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," \
               + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.theta == other.theta \
                   & self.phi == other.phi \
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

class u3(cirq.Gate):
    def __init__(self, theta, phi, lambd):
        self.theta = theta
        self.phi = phi
        self.lambd = lambd
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.theta == other.theta\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        return [
            UGate(theta, phi, lambd)(q),
        ]
    def num_qubits(self):
        return 1

class u2(cirq.Gate):
    def __init__(self, phi, lambd):
        self.phi = phi
        self.lambd = lambd
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        return [
            UGate(pi/2, phi, lambd)(q),
        ]
    def num_qubits(self):
        return 1

class u0(cirq.Gate):
    def __init__(self, gamma):
        self.gamma = gamma
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.gamma) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.gamma == other.gamma
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        return [
            UGate(0, 0, 0)(q),
        ]
    def num_qubits(self):
        return 1

class cyInit(cirq.Gate):
    def __init__(self):
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        return [
            (cirq.S**(-1))(b),
            cirq.CNOT(a, b),
            cirq.S(b),
        ]
    def num_qubits(self):
        return 2
cy = cyInit()

class swapInit(cirq.Gate):
    def __init__(self):
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        return [
            cirq.CNOT(a, b),
            cirq.CNOT(b, a),
            cirq.CNOT(a, b),
        ]
    def num_qubits(self):
        return 2
swap = swapInit()

class chInit(cirq.Gate):
    def __init__(self):
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        return [
            cirq.H(b),
            (cirq.S**(-1))(b),
            cirq.CNOT(a, b),
            cirq.H(b),
            cirq.T(b),
            cirq.CNOT(a, b),
            cirq.T(b),
            cirq.H(b),
            cirq.S(b),
            cirq.X(b),
            cirq.S(a),
        ]
    def num_qubits(self):
        return 2
ch = chInit()

class crz(cirq.Gate):
    def __init__(self, lambd):
        self.lambd = lambd
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        return [
            cirq.Rz(lambd/2)(b),
            cirq.CNOT(a, b),
            cirq.Rz(-(lambd/2))(b),
            cirq.CNOT(a, b),
        ]
    def num_qubits(self):
        return 2

class cu1(cirq.Gate):
    def __init__(self, lambd):
        self.lambd = lambd
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        return [
            cirq.Rz(lambd/2)(a),
            cirq.CNOT(a, b),
            cirq.Rz(-(lambd/2))(b),
            cirq.CNOT(a, b),
            cirq.Rz(lambd/2)(b),
        ]
    def num_qubits(self):
        return 2

class cu3(cirq.Gate):
    def __init__(self, theta, phi, lambd):
        self.theta = theta
        self.phi = phi
        self.lambd = lambd
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.theta == other.theta\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        c = qubits[0]
        t = qubits[1]

        return [
            cirq.Rz((lambd-phi)/2)(t),
            cirq.CNOT(c, t),
            u3(-(theta/2), 0, -((phi+lambd)/2))(t),
            cirq.CNOT(c, t),
            u3(theta/2, phi, 0)(t),
        ]
    def num_qubits(self):
        return 2

class bellInit(cirq.Gate):
    def __init__(self):
        return

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def _decompose_(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        x = qubits[0]
        y = qubits[1]

        return [
            cirq.H(x),
            cirq.CNOT(x, y),
        ]
    def num_qubits(self):
        return 2
bell = bellInit()

q = [cirq.NamedQubit("q[i]") for i in range(1)]
anc = [cirq.NamedQubit("anc[i]") for i in range(2)]
circuit = cirq.Circuit()
circuit.append([
    bell(anc[0], anc[1]),
    cirq.CNOT(q[0], anc[0]),
    cirq.H(q[0]),
    cirq.measure(q[0], key="c0[0]"),
    cirq.measure(anc[0], key="c1[0]"),
])