qat.lang : Programming tools
The Program class
- class qat.lang.AQASM.Program(default_gate_set=True)
Class for quantum programs.
- Parameters
default_gate_set (bool, optional) – If set to False, the Program is initialized with an empty gate set. The gate set is updated dynamically when one applies gates to the Program via the apply method. Default to True.
- calloc(size=1)
Allocates a new classical bit register of the given size. Returns the register.
- Parameters
size (int) – The number of classical bits to allocate.
- Returns
A fresh classical bit register of size size
- Return type
- cbreak(formula)
Conditional break. The program execution will stop if and only if the formula given as argument evaluates to 1.
- Parameters
formula (
BoolFormula
) – a boolean formula
- cc_apply(cbit, gate, *args)
Conditional application of a quantum gate. The quantum gate will be applied if and only if the control classical bit given as first argument evaluates to 1.
- comment(message)
Add a comment inside the circuit. The comment will appear in the AQASM export of the circuit.
- Parameters
message (str) – a string containing the comment
- conjugate(gate1, gate2, *args)
Apply a sequence of 3 gates. gate2 will be conjugated by gate1. The two gates are required to have the same arity.
- display(**kwargs)
Transforms the program into a circuit and displays it.
- Parameters
kwargs – key word arguments passed to the display method of the circuit
- export(fname)
Exports the Program in AQASM text format.
- Parameters
fname (str) – A file name
- free_ancillae(args)
Releases a set of ancillae
- get_free_ancillae(size=1, class_name=None)
Returns a collection of free ancillae
- logic(cbit, formula)
Performs a classical logic operation using classical bits and stores the result in a classical bit.
- Parameters
cbit (
Cbit
) – the cbit that will store the resultformula (
BoolFormula
) – A boolean formula.
- measure(qbits, cbits=None)
Applies a measurement operator. All measurements are \(Z\) measurements (i.e in the computational basis). Qbits are simply collapsed and not destroyed (i.e they are still usable afterward).
- Parameters
qbits (list<Qbit>) – The qubits to measure specified as a list/register/array
cbits – (list<Cbit>, optional): The classical bits in which to store the result. Default to None. If defaulted/set to None, the results will be stored in classical bits with matching indices.
- new_var(var_type, var_name)
Constructs a new variable bound to the circuit.
- Parameters
var_type (type) – The type of the variable.
var_name (str) – The name of the variable. Raises a
VariableNameNotAvailable
if a variable with the same name is already bound to the circuit.
- Returns
A variable
- Return type
- qalloc(size=1, class_type=None, **kwargs)
Allocates a new qbit register of the given size. Returns the register.
If no constructor is provided, the register will simply behave as an array of qubits.
The two implemented quantum types are
QInt
andQBoolArray
.from qat.lang.AQASM import Program, QInt, QBoolArray prog = Program() qbits = prog.qalloc(5) print(qbits) qint = prog.qalloc(5, QInt) print(qint) qbools = prog.qalloc(5, QBoolArray) print(qbools)
QReg(q[0]..q[4]) QInt(q[5]..q[9]) QBoolArray(q[10]..q[14])
The main purpose of this wrapping process is to provide a higher level interface for some quantum register, depending on the application. This allows to generate complicated quantum circuits without having to deal with low level administrative constraints.
- Parameters
size (int) – the number of qbits to allocate.
class_type (type) – the quantum type of the register
kwargs – any keyword argument is passed to the constructor of the quantum type
- Returns
A fresh qbit register of size size
- Return type
- reset(qblist, cblist=None)
Resets the value of a set of qbits to \(|0\rangle\). and/or a set of cbits to 0.
- Parameters
qblist (list<Qbit>) – The list of qbits
cblist (list<Cbit>, optional) – The list of cbits. Default to [].
- to_circ(include_matrices=True, include_locks=False, inline=False, do_link=True, comparison_eps=1e-20, box_routines=False, **kwargs)
Return a
Circuit
implementing the program.The circuit extraction consists of three stages:
Circuit generation: a simple circuit is extracted from the Program object. Any call to an abstract gate will be left untouched. All circuit implementation attached to abstract gates definition will be ignored at this stage.
Linking: this simple circuit is then linked against:
the gate set aggregated by the program during its construction
any other gate set/abstract gate/python module passed as argument.
The linking process simply attach to each abstract gate a circuit implementation using the function specified at definition of the abstract gate.
Inlining: these implementations are then inlined in place, turning the circuit into a (potentially) very long sequence of gates/operations. By default this step is skipped, as it is very demanding.
All these steps can be controlled using various parameters detailed below.
- Parameters
include_matrices (bool, optional) – if set to True, matrices will be generated and included in the circuit. Defaults to True.
include_locks (bool) – If set to True, additional gates will be added on the ancilla wire to signify lock and release of the ancilla. Defaults to False. This can be used to debug ancilla usage or further optimize the circuit later on. Lock/release operations can be removed from the circuit via the .remove_locks() method.
do_link (bool) – if set to False, skips the linking step. Default to True.
inline (bool) – if set to False, no inlining/linking will happen. Default to False.
comparison_eps (float) – Optional argument to set the matrix/gate parameters comparison threshold. If two parameters are comparison_eps close, they will be considered as identical, resulting in the indentification of their correspondig gates (thus saving space and time). Default value is 1e-20.
box_routines (bool) – If set to True, routines will be systematically boxed before being included inside the initial circuit (the one generated during the Circuit generation step). Setting this option to True can save up a lot of time for repetitive circuits.
**kwargs – other arguments passed to the linker. See below.
- Keyword Arguments
submatrices_only (bool) –
if set to True, only submatrices will be generated and included in the circuit. Default to False.
Warning
Setting this argument to True will decrease (by a lot) the circuit generation time. For instance, using the default settings, a RZ rotation controlled 10 times will end up generating a matrix of size (\(2^{11} \times 2^{11}\)). By setting this argument to True, only a \(2\times 2\) matrix will be generated. However, not all simulators are able to infer a matrix from the structure of the gate. In particular, this means that you should set this option to True to simulate circuits on
PyLinalg
.keep (list<str>) – if set to a list of gate names, these gates won’t be inlined by the linker. Default to None.
link (list) – a list of
AbstractGate
,GateSet
, or python packages to pass to the linker.
- Returns
A circuit implementing the program
- Return type
Gate structures
All classes describing unitary operators inherit from the Gate
class.
This parent class contains very little information. Its primary function is to store information about control, dagger and other high-level gate operations. Since this class is not made to be used directly, we skip some part of its documentation.
- class qat.lang.AQASM.gates.Gate(arity, matrix=None, name=None)
Mother class for all quantum gates/unitary operators. This class should never be instantiated directly.
- conj()
Builds a new gate that is the complex conjugate of the initial gate.
- Returns
The conjugate of the gate.
- Return type
- ctrl(nbctrls=1)
Builds a new gate that is a controlled version of the initial gate.
- Returns
The same gate, but controlled.
- Return type
- dag()
Builds a new gate that is the dagger of the initial gate.
- Returns
The dagger of the gate.
- Return type
Another important class is the AbstractGate
class. This class provides a way to define new parametrized gates. Basically, an abstract gate describes a family of gates parametrized by a list of values of various types. The current list of admissible types contains: int
, float
, str
For instance, to declare a new AbstractGate
that describes a \(R_z\) rotation, we would write:
my_rz = AbstractGate("RZ", [float], arity=1)
and use this new abstract gate as follows:
my_rz(0.4)(qbits_reg[0])
It is possible to attach a matrix generator to the abstract gate in order to generate and include a matrix in the final circuit (if you want to be able to simulate the circuit for instance).
def matrix_gen(theta):
return np.array([[1, 0], [0, np.exp(1j * theta)])
my_rz = AbstractGate("RZ", [float], arity=1,
matrix_generator=matrix_gen)
- class qat.lang.AQASM.gates.AbstractGate(*args, **kwargs)
Warning
This class is for advanced usage.
Abstract gate class. Abstract gates are used to define gate constructors such as Rx, Ry, Rz, etc.
They behave as quantum gate constructing functions.
This class extends
qat.core.gate_set.GateSignature
.- set_dag(func)
Attaches a dagger recipe to the abstract gate. The function passed as argument should:
take as argument a sequence of parameters
return a new sequence of parameters corresponding to the dagger of the gate.
my_rz = AbstractGate("MY_RZ", [float]) my_rz.set_dag(lambda theta: [-theta])
This function will be called when a .dag() method is called on some
ParamGate
.If the dag is not set, the abstract gate will make no assumption on the structure of the dagger, and the standard recursive structure from
Gate
will be used.
ParamGate
is generated through AbstractGate
. An abstract gate instantiated by a set of parameters (such as the my_rz(0.4) from above) returns a ParamGate
carrying all the necessary information to build the correct abstract syntax tree of the gate.
- class qat.lang.AQASM.gates.ParamGate(abstract_gate, arity, *parameters)
Class for parametrized gates instantiated by some set of parameters. Members of this class should be automatically instantiated by the AbstractGate structure. Only instantiate if you know what you are doing.
- Parameters
abstract_gate (
AbstractGate
) – the mother abstract gatearity (int) – the arity of the gate (can be set to None)
*parameters (list<any>) – the list of parameters of the gate
- dag()
Builds a new gate that is the dagger of the initial gate.
- Returns
The dagger of the gate.
- Return type
- display(**kwargs)
Displays the param gate (if the gate has a circuit generator).
- Parameters
kwargs – key word arguments passed to the display method of the circuit
Finally, the notion of gate is also extended to subcircuits via the QRoutine
class.
- class qat.lang.AQASM.routines.QRoutine(arity=0, routop_l=None, max_wire=None, ancillae=None)
QRoutine
represent subcircuits that behave as a Gate object.The constructor takes no required arguments.
Applying a Gate or a
QRoutine
inside another routine is done through theapply()
method. Since aQRoutine
can exist independently from any Program there are is no notion of qbit allocation in aQRoutine
. Gates are thus applied on indices referring to the inbound wires of the routine.Example
The following example shows how to create a QRoutine containing two gates and use it repeatedly within a quantum circuit
from qat.lang.AQASM import Program, QRoutine, H, CNOT routine = QRoutine() routine.apply(H, 0) routine.apply(CNOT, 0, 1) prog = Program() qbits = prog.qalloc(4) for _ in range(3): for bl in range(2): prog.apply(routine, qbits[2*bl:2*bl+2]) circ = prog.to_circ() print("total number of gates: ", len(circ.ops))
total number of gates: 12
- apply(gate, *args)
Apply a quantum gate on a set of wires. Wires are refered using integers.
- Parameters
gate (
Gate
) – the quantum gate to apply*args (any iterator over int) – the wires to apply the gate on
- compute()
Opens a computation scope and returns a reference to this fresh scope.
Warning
All scopes are lost when controlling/daggering a QRoutine.
Note
This method is designed to be called inside a with statement in order for the scope to be cleanly closed.
with rout.compute(): rout.apply(CNOT, 0, 1) # here the scope is closed rout.uncompute()
Directly calling the method will result in the scope never being closed.
- free_ancillae(args)
Return the ancillae qubits to an unused pool so that they can be reused again in the routine. It is important that the ancilla qubits are in product \(|0\rangle\) state when they are freed.
- Parameters
args (int/list<int>/quantum register) – the set of ancillae to release
- get_free_ancillae(length=1, class_name=None)
Returns a list of ancillae qubits of length length. If there are currently unused ancillae qubits in the list freed_ancillae, they will be returned first. Then, allocate new wires if needed and set them as ancillae.
- Parameters
length (optional, int) – the number of ancillae to allocate (default to 1)
class_name (optional, type) – optionally, a quantum type to wrap the allocated ancillae (e.g QInt, QBool, etc)
- new_wires(length, class_name=None, **kwargs)
Returns a list of fresh wires of length length.
- Parameters
length (int) – the number of fresh wires to return
class_name (type) – a quantum type (optional)
kwargs – additinal arguments are passed to the quantum type
- Returns
a quantum register
- Return type
- set_ancillae(*args)
Tags some wires as ancillae. This has two effects:
the public arity of the routine will be reduced to only consider the non-tagged wires as input/output wires
ancillae will be allocated at circuit generation (in consequence, the final number of qubits of the circuit might be larger than the number of qubits allocated inside the program)
For example, in the following code:
rout = QRoutine() w1, w2 = rout.new_wires() rout.set_ancillae(w1)
rout is a routine of arity 1 (since w1 has been tagged as ancilla). In particular the following code will be a valid program:
prog = Program() qbits = prog.qalloc(1) prog.apply(rout, qbits) circuit = prog.to_circ()
Moreover, the resulting circuit will be a circuit over 2 qubits (the ancilla will be allocated during the contruction of the circuit).
- uncompute()
Uncomputes the first computation scope on the scope stack and pops it from the scope stack.
Quantum Types
Quantum boolean and expressions
- class qat.lang.AQASM.qbool.QBool(index, scope=None)
Class for quantum boolean type.
This class is not designed to be instantiated by hand, but rather via the .qalloc method of the
Program
class or the .new_wires method of theQRoutine
class.Moreover, since allocation happens at the register level, the
QBoolArray
class should be used.from qat.lang.AQASM.qbool import QBoolArray from qat.lang.AQASM import QRoutine rout = QRoutine() qbool_array = rout.new_wires(2, QBoolArray) print(type(qbool_array)) print(type(qbool_array[0]))
<class 'qat.lang.AQASM.qbool.QBoolArray'> <class 'qat.lang.AQASM.qbool.QBool'>
See documentation of the
QClause
class for more information.- Parameters
Instance attributes:
- cast(val)
Cast an integer value as a boolean.
This method is used when casting execution samples to proper python values.
- Parameters
val (int) – some integer
- Returns
a boolean value
- Return type
bool
- cast_to(cls, **kwargs)
Cast the register to another type.
- Parameters
cls (type) – another quantum type class
- Returns
some object constructed via cls
- Return type
object
For instance, the following piece of code casts a quantum boolean to a quantum integer:
from qat.lang.AQASM.qbool import QBoolArray from qat.lang.AQASM.qint import QInt from qat.lang.AQASM import QRoutine rout = QRoutine() array = rout.new_wires(2, QBoolArray) qbool = array[0] qint = qbool.cast_to(QInt)
- evaluate(output=None)
Evaluate the QBool as an expression. Since QBools are trivial expression, this simply returns self and the output argument is ignored (it is here simply for compatibility with more complicated expressions).
- phase()
Flips the phase of the state if and only if the QBool is set to True. Effectively applies a \(Z\) gate on self.
- qbits_list()
Returns the list of underlying qubits (i.e [self])
- class qat.lang.AQASM.qbool.QBoolArray(start=0, length=1, scope=None, qbits_list=None)
Class describing an array of
QBool
.This class is not designed to be instantiated by hand, but rather via the .qalloc method of the
Program
class or the .new_wires method of theQRoutine
class.See documentation of the
QClause
class for more information.from qat.lang.AQASM.qbool import QBoolArray from qat.lang.AQASM import QRoutine rout = QRoutine() qbool_array = rout.new_wires(2, QBoolArray) print(type(qbool_array))
<class 'qat.lang.AQASM.qbool.QBoolArray'>
- Parameters
- Instance attributes:
index (int): the index of the underlying qbit
qbits (list of
QBool
): a list of quantum booleans
- cast(val)
Cast some integer value as a list of bools.
This method is used when casting execution samples to proper python values.
- Parameters
val (int) – some integer
- Returns
a list of boolean values
- Return type
list of bools
- class qat.lang.AQASM.qbool.QClause(scope, operator, operand=None)
Class describing boolean formulae to manipulate qubits.
This class is not designed to be instantiated by hand.
Clauses are created by using the boolean operators (and, or, xor, neg) applied on (an)other formula(e) or qubits. Qubits are implicitly cast toward boolean formulae.
Example:
from qat.lang.AQASM import QRoutine from qat.lang.AQASM.qbool import QBoolArray rout = QRoutine() wires = rout.new_wires(2, QBoolArray) and_formula = wires[0] & wires[1] print(type(and_formula))
<class 'qat.lang.AQASM.qbool.QClause'>
A formula should be manipulated via:
the .evaluate method, in order to compute its value in superposition
the .phase method in order to perform a phase flip of all the basis states that evaluate the formula to true
a with statement (see the example below)
Example of evaluate:
from qat.lang.AQASM import QRoutine from qat.lang.AQASM.qbool import QBoolArray rout = QRoutine() wires = rout.new_wires(2, QBoolArray) and_formula = wires[0] & wires[1] # By directly allocating an ancilla result = and_formula.evaluate() # Or by prior allocation of a result qubit result = rout.new_wires(1) and_formula.evaluate(output=result) # At this stage, `result` carries the logical AND between our two inputs
Example of phase:
from qat.lang.AQASM import QRoutine from qat.lang.AQASM.qbool import QBoolArray rout = QRoutine() wires = rout.new_wires(2, QBoolArray) and_formula = wires[0] & wires[1] and_formula.phase() # At this stage, all classical states such that the first two qubits # are set to 1 have their phase flipped
Example of with statement conditional:
from qat.lang.AQASM import QRoutine, CNOT from qat.lang.AQASM.qbool import QBoolArray rout = QRoutine() wires = rout.new_wires(2, QBoolArray) and_formula = wires[0] & wires[1] output = rout.new_wires(1) with and_formula as condition: CNOT(condition, output) # We evaluated the expression, got a qbit `condition` carrying the result # and store the result in `output`
- evaluate(output=None)
Builds a circuit evaluating the expression.
- phase()
Flips the phase if and only if the expression/qbits evaluates to True.
- qbits_list()
Returns a list of underlying qbits.
- Returns
a list of qbits
- Return type
list of Qbits
Quantum integers
- class qat.lang.AQASM.qint.QInt(start=0, length=1, scope=None, reverse_bit_order=False, qbits_list=None)
Class for quantum integer type. The qubit list in a QInt is by default encoded such that the most significant bit is on the left. The order can be reversed using the reverse_bit_order argument.
As other quantum types, this class is not designed to be constructed by hand, but rather via the .qalloc method of the
Program
class or the .new_wires method of theQRoutine
class.Example:
from qat.lang.AQASM.qint import QInt from qat.lang.AQASM import QRoutine rout = QRoutine() qint = rout.new_wires(10, QInt) print(qint, type(qint)) # or, with reversed bit order: qint = rout.new_wires(10, QInt, reverse_bit_order=True)
QInt(q[0]..q[9]) <class 'qat.lang.AQASM.qint.QInt'>
Quantum integers have two sets of overloaded operators:
arithmetic operators (+ and *): combine
QInt
and/orQArithExp
to produce aQArithExp
. See documentation ofQArithExp
for more details.comparison operators (<,>,<=,>=,==,!=): combine a
QInt
/QArithExp
and or a classicalint
in order to produce a comparison expression,QCompExp
, that behave similarly to aQClause
and can be thus used to construct oracles or conditionals.
Moreover, the += and -= operators are also overloaded and triggers a circuit evaluating the right-hand term and adding it to the quantum integer.
from qat.lang.AQASM.qint import QInt from qat.lang.AQASM import QRoutine rout = QRoutine() qint1 = rout.new_wires(10, QInt) qint2 = rout.new_wires(10, QInt) qint3 = rout.new_wires(10, QInt) # This does nothing qint1 + qint2 # This generates a circuit that adds qint1 in qint3 and qint2 in qint3 qint3 += qint1 + qint2 # This generates a circuit that adds -qint1 in qint3 and -qint2 in qint3 qint3 -= qint1 + qint2
Comparisons results can be used exactly as
QClause
:from qat.lang.AQASM.qint import QInt from qat.lang.AQASM import QRoutine rout = QRoutine() qint1 = rout.new_wires(10, QInt) qint2 = rout.new_wires(10, QInt) # Flips the phase of states such that qint1 is smaller than 3 (qint1 < 3).phase() # Flips the phase of states such that qint1 + qint2 is smaller than 14 (qint1 + qint2 < 14).phase()
- Parameters
Instance attributes:
- cast(val)
Casts some integer value into some other integer value with the correct bit-order. This is used when casting execution samples.
- Parameters
val (int) – some integer
- evaluate(nbqbit=None)
Evaluate the QInt as a formula. Effectively does nothing and returns a copy of self.
- qbits_list()
Returns the underlying list of qubits.
- Returns
a list of quantum booleans
- Return type
list of
Qbit
- set_value(val)
Sets the QInt to some classical value.
Note
This method assumes that the QInt is unitialized (i.e is still in state \(|0\rangle\)). Effectively, the register is xored with the classical value.
- Parameters
val (int) – some integer value
- class qat.lang.AQASM.qint.QArithExp(scope, operator, operands=None, reverse_bit_order=True)
Class describing arithmetic expressions whose operands contain quantum integers.
This class is not designed to be instantiated by hand, but rather built via the addition, substraction, and multiplication of quantum integers with other quantum integers or python integers.
from qat.lang.AQASM import QRoutine, QInt rout = QRoutine() qint = rout.new_wires(10, QInt) print(type(qint + 33))
<class 'qat.lang.AQASM.qint.QArithExp'>
- evaluate(nbqbit=None)
Builds a piece of circuit evaluating the arithmetic expression.
- Parameters
nbqbits (optional, int) – the number of qbits of the output register.
- class qat.lang.AQASM.qint.QCompExp(scope, operator, left, right)
Class describing quantum comparison expressions to manipulate quantum integers.
This class is not designed to be instantiated by hand, but rather built via comparison of quantum integers (with other quantum integers or classical values) using the standard comparison operators.
from qat.lang.AQASM import QRoutine, QInt rout = QRoutine() qint = rout.new_wires(10, QInt) print(type(qint < 33))
<class 'qat.lang.AQASM.qint.QCompExp'>
- evaluate(output=None)
Evaluates the comparison operator and stores the result in a temporary Qbit.
- phase()
Flips the phase if and only if the expression evaluates to True.
- qbits_list()
Returns the underlying list of qubits
Other structures
- class qat.lang.AQASM.bits.Qbit(index, scope=None)
Class for qbits.
- class qat.lang.AQASM.bits.QRegister(offset, length=1, scope=None, qbits_list=None)
Class for registers of qbits. They should only be declared through the qalloc method of the Program class. Qbits inside a register can be accessed similarly to elements of an array (i.e using brackets and slices).
- class qat.lang.AQASM.bits.CRegister(offset, length=1)
Class for registers of classical bits. They should only be declared through the calloc method of the AQASM class Cbits inside a register can be accessed similarly to elements of an array (i.e using brackets and slices).
Classical bits and Boolean formulae
- class qat.lang.AQASM.bits.Cbit(index)
Class for cbits.
Cbits can be composed via logical operator to form Boolean formulae.
See
BoolFormula
for examples.- Parameters
index (int) – the index of the classical bit
- class qat.lang.AQASM.bits.BoolFormula(operator, left, right=None)
Class describing boolean formulae to manipulate classical bits.
This class is not designed to be instantiated by hand, but rather via composition of
Cbit
via logical operators.Formulae are created by using the classical boolean operators (and, or, xor, neg) applied on (an)other formula(e) or classical bits.
from qat.lang.AQASM import Program prog = Program() cbits = prog.calloc(2) and_expr = cbits[0] & cbits[1] print(type(and_expr)) print(and_expr)
<class 'qat.lang.AQASM.bits.BoolFormula'> (c[0] & c[1])
These objects are used in various pyAQASM instructions.
- cbits_list()
Returns the list of cbits appearing in the formula.
- Returns
a list of
Cbit
- Return type
list
- to_thrift()
Returns a thrift compatible string representation of the formula.
This method is called in order to produce a seralizable representation of the expression.
- Returns
a string representation of the formula
- Return type
str
Exceptions
This exception is defined in qat.core but related to qat.lang:
- class qat.core.circuit_builder.builder.VariableNameNotAvailable(name)
Exception raised when a variable is redefined using the same name.
Utilities
This module contains a few utilitaries
- qat.lang.AQASM.util.suppr_ctrl(rout, depth=1)
Builds a functionally equivalent quantum routine such that no gate in this routine has more that “depth” control qbits. It uses an ancillary register to Toffoli-fold the control qbits. The number of ancillas to add is (in the worst case) equal to (maximum number of ctrl to remove) -1.
- qat.lang.AQASM.util.toffoli_fold(nb_qbits)
Returns a quantum routine that, given a data register of length \(n\) and an ancilla register, applies Toffoli gates on the data register in order to compute a logical AND of all its qbits inside a single ancillary qbit. May use up to \(n-1\) ancillary qbits (worst case scenario). The resulting qbits is always the last qbit (hence of index arity - 1).
- Parameters
nb_qbits (int) – the size of the register to fold
- Returns
a quantum routine performing the fold
- Return type
Gate set management and generation
This module contains various tools and decorator to turn functions into gates
- class qat.lang.AQASM.misc.build_gate(name, signature, arity=None)
A wrapper class lifting functions into AbstractGates with a circuit implementation.
- Parameters
func (function) – an annotated function returning a QRoutine
Note
Calling the decorated function will return an AbstractGate.
The underlying QRoutine can be referred to by using a tilde before the function:
@build_gate def my_gate(a : int) -> 'FOO': rout = QRoutine() return rout my_gate(10) # will return an AbstractGate object, instantiated # with parameter 10 (~my_gate)(10) # will return a QRoutine object
- qat.lang.AQASM.misc.generate_gate_set(*args, safe=False)
Generates a gate set from a list of various arguments. Arguments can be:
GateSignature/AbstractGates
build_gates wrappers
python modules
In this case it iterates through its scope and gathers all the build_gate objects (i.e all the decorated functions).
Quantum libraries
Linker and low level circuit manipulation
In pyAQASM, gates can be specified using various representations. They can be purely abstract (a name and a set of parameter values, such as RZ, [PI/2]). To this abstract syntax, one can attach a matrix, or even a subcircuit that implements this gate (this is the case of adders of the arithmetic library for instance).
Circuits are generated in two steps: - first a skeleton of the circuit is generated (for instance adders will be gates called ADD in this skeleton) - then a subcircuit is generated and attached to this abtract gate.
This second step is handled by a class called Linker. It can be useful in some settings to manipulate this Linker object in order to replace gates by subcircuits, or to generate matrices in order to simulate the circuit.
- class qat.lang.linking.linker.Linker(gate_set=None, include_matrices=True, keep=None, link=None, submatrices_only=False, inline=False, include_locks=False)
The purpose of a linker is to crawl a circuit and attach implementations to gates. These implementations are specified via a
GateSet
object (more or less a collection ofAbstractGate
).The main method of this class is the .link method.
The following example uses a linker to replace CNOT gates by H and CZ gates:
from qat.lang.AQASM import * from qat.core.gate_set import GateSet from qat.lang.linking.linker import Linker prog = Program() qbits = prog.qalloc(2) CNOT(qbits) circuit = prog.to_circ() @build_gate('CNOT', [], arity=2) def my_cnot_implementation(): rout = QRoutine() wires = rout.new_wires(2) with rout.compute(): H(wires[1]) CSIGN(wires) rout.uncompute() return rout # We initialize a Linker with an empty gate set since we want # to override the default CNOT implementation linker = Linker(gate_set=GateSet()) linker.add_signature(my_cnot_implementation) print("Before linking:") for op in circuit.iterate_simple(): print(op) linker.link(circuit) print("After linking:") for op in circuit.iterate_simple(): print(op)
Before linking: ('CNOT', [], [0, 1]) After linking: ('H', [], [1]) ('CSIGN', [], [0, 1]) ('H', [], [1])
- Parameters
gate_set (
GateSet
) – A gate set to start the linking with. Optional. Defaults to the default pyAQASM gate set.include_matrices (bool, optional) – if set to True, matrices will be generated and included in the circuit. Defaults to True.
keep (list<str>) – if set to a list of gate names, these gates will be skipped by the linker. Default to None.
link (list) – a list of
AbstractGate
,GateSet
, or python modules to pass to the linker.submatrices_only (bool) – if set to True, only submatrices will be generated and included in the circuit. For instance a CTRL(Y) will only generate the matrix for Y, thus saving memory. Default to True.
inline (bool, optional) – if set to True, subcircuit implementations will be inlined in the main body of the circuit. This might increase the memory footprint of the circuit and linking cost. Default to False.
include_locks (bool, optional) – if set to False, removes all lock/release operators after the linking process. If set to True, these operators will remain. These operators are here for debug purpose only. Defaults to False.
- add_signature(gate_signature)
Adds a gate signature to the current gate set of the Linker. This gate should not be present in the current gate set.
- Parameters
gate_signature (
GateSignature
) – a gate signature
- clear_gate_set(default_gates=True)
Resets the content of the internal gate set of the Linker.
- Parameters
default_gates (bool) – If set to True, the default gate set of pyAQASM will be included in the fresh gate set. Optional. Defaults to True.
- compile(batch, _specs)
Compiles a Batch into another Batch according to some hardware specs
- link(circuit)
Uses the internal gate set of the Linker to link gate implementations.
Warning
The linking happens in place (thus modifies the input circuit).
- Parameters
circuit (
qat.core.Circuit
) – a quantum circuit
- link_gates(circuit)
Uses the internal gate set of the Linker to instantiate the gates of a circuit.
Warning
The linking happens in place (thus modifies the input circuit).
- Parameters
circuit (
qat.core.Circuit
) – a quantum circuit
- link_matrices(circuit)
Link the matrix implementations to the gate definitions of a circuit.
Warning
The linking happens in place (thus modifies the input circuit).
- Parameters
circuit (
qat.core.Circuit
) – a quantum circuit
Basic algorithms
There are a few basic algorithms present in the qat.lang.algorithms module:
- qat.lang.algorithms.amplification_step(oracle, state_prep=None)
Builds a routine that performs a single amplification step. By default the diffusion operator is Grover’s diffusion.
If the state_prep argument is specified, it will be used to perform the following Householder transform instead:
\[2\left(U|0\rangle\langle 0|U^\dagger\right) - I\]where \(U\) is the unitary operator implemented by state_prep.
This method can be used to easily build a Grover search program:
from qat.lang.AQASM import Program, QInt, QRoutine, H from qat.lang.algorithms import amplification_step nbits = 2 oracle = QRoutine() reg1 = oracle.new_wires(nbits, QInt) reg2 = oracle.new_wires(nbits, QInt) (reg1 == reg2).phase() step = amplification_step(oracle) grover = Program() reg1 = grover.qalloc(nbits, QInt) reg2 = grover.qalloc(nbits, QInt) for qbit in reg1: H(qbit) for qbit in reg2: H(qbit) for _ in range(1): step(reg1, reg2) job = grover.to_circ().to_job(qubits=[reg1, reg2]) from qat.qpus import get_default_qpu qpu = get_default_qpu() result = qpu.submit(job) for sample in result: print(sample.state.value, sample.probability)
(0, 0) 0.24999999999999956 (1, 1) 0.24999999999999956 (2, 2) 0.24999999999999956 (3, 3) 0.24999999999999956
- qat.lang.algorithms.phase_estimation(operator, nbits)
Generates a routine that performs phase estimation of some unitary operator.
The number of qubits of the resulting routine is \(n+k\) where \(n\) is the arity of operator and \(k\) is nbits. The routine assumes that the first \(n\) qubits are already prepared and contain the states on which the phase estimation should be performed. The result will be stored in the last \(k\) qubits.
- qat.lang.algorithms.quantum_counting(oracle, nbits, prepare=True)
Performs a quantum counting on some oracle.
The number of qubits of the resulting routine is \(n+k\) where \(n\) is the arity of oracle and \(k\) is nbits. The routine assumes that the first \(n\) qubits are already prepared and contain the states on which the counting should be performed. The result will be stored in the last \(k\) qubits.
Additionally, the submodule qat.lang.algorithms.amplification contains some functions that might be useful:
- qat.lang.algorithms.amplification.uniform_distribution(nbits)
Prepares a uniform distribution from state \(|0\rangle\)
- Parameters
nbits (int) – the number of qbits
- Returns
a routine
- Return type
- qat.lang.algorithms.amplification.householder(state_prep)
Given some state preparation circuit implementing a unitary operator \(U\), builds a Householder transform of the form:
\[2\left(U|0\rangle\langle 0|U^\dagger\right) - I\]
- qat.lang.algorithms.amplification.grover_diffusion(nbits)
An implementation of Grover’s diffusion.
The returned routine implements the following Householder transform:
\[2|s\rangle\langle s| - I\]where \(|s\rangle\) is the uniform distribution.
- Parameters
nbits (int) – the arity of the diffusion
- Returns
a routine
- Return type