-
Notifications
You must be signed in to change notification settings - Fork 28
The staq tool suite
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.
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.
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.
- Desugarer
- Inliner
- Oracle synthesizer
- Simplifier
- Rotation optimizer
- Mapper
- Resource estimator
- Quil compiler
- ProjectQ compiler
- Q# compiler
- Cirq compiler
Desugars the openQASM source. Typically this means expanding out gates applied to registers.
Usage:
./desugarer < FILE.qasm
Example:
foo@bar$ cat desugar_example.qasm
OPENQASM 2.0;
qreg a[2];
qreg b[2];
CX a,b;
foo@bar$ ./desugar < desugar_example.qasm
OPENQASM 2.0;
qreg a[2];
qreg b[2];
CX a[0],b[0];
CX a[1],b[1];
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:
./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$ ./inline < 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];
Synthesizes oracles declared by verilog files. If successful, all oracle declarations in the input file are replaced with regular gate declarations.
Usage:
./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$ ./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;
}
Applies basic circuit simplifications. In particular, the simplifier cancels adjacent inverse gates.
Usage:
./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$ ./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];
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:
./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$ ./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];
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: At present, devices are hard-coded into the executable. New devices can be added by creatingmapping::Device
objects and recompiling
Usage:
./mapper [OPTIONS] < FILE.qasm
Option | Description |
---|---|
-d,--device (tokyo⎮agave⎮aspen4⎮singapore⎮square⎮fullycon) |
Device to map the circuit onto (default=tokyo ) |
-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$ ./mapper -d square9 -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[3];
cx q[4],q[5];
cx q[6],q[7];
cx q[8],q[3];
cx q[3],q[8];
cx q[8],q[3];
cx q[3],q[2];
cx q[2],q[3];
cx q[3],q[2];
cx q[2],q[1];
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:
./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$ ./resource_estimator < resource_example.qasm
Resources used:
cx: 1
depth: 3
ccx: 1
qubits: 3
t: 2
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 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:
./quil_compiler < 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$ ./quil_compiler < 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 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:
./quil_compiler < 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$ ./projectq_compiler < 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# 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.SynthewareQ
.
Usage:
./quil_compiler < 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$ ./qsharp_compiler < teleport.qasm
namespace Quantum.SynthewareQ {
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 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:
./quil_compiler < 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$ ./cirq_compiler < 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]"),
])
Copyright (c) 2019 - 2024 softwareQ Inc. All rights reserved.