CircuitOperationList#
- class iqm.pulse.circuit_operations.CircuitOperationList(contents=(), *, qubits=None, num_qubits=0, table=None)#
Bases:
list
List of
CircuitOperation
objects representing a quantum circuit.The class is used to work with CircuitOperations directly. It is mostly meant as convenience to enable easy creation of circuits, calculations of their properties, and mapping them onto physical qubits. In addition to the circuit contents, this class has two important attributes:
qubits
andtable
.qubits
defines the list of qubits which are allowed to be in the loci of all the CircuitOperations present in the list. Think about it as Qiskit’s QuantumRegister.table
is aQuantumOpTable
, which contains all theQuantumOp
s which are allowed in the circuit. In most cases, the table is simply taken to contain all the default operations defined iniqm.pulse
. When you use this class with aScheduleBuilder
, it is good practice to settable = builder.op_table
. The QuantumOpTable is mutable, so any additional registered gates can automatically be usable in any CircuitOperationList associated with that ScheduleBuilder instance.The fundamental use of the class would be to first define a new instance:
circuit = CircuitOperationList(num_qubits=2)
The
num_qubits
parameter populates thequbits
attribute with qubits QB1-QBn, in this case['QB1', 'QB2']
.Alternatively, you can provide
qubits
directly:circuit = CircuitOperationList(qubits=['QB1', 'QB2'])
To add your own QuantumOpTable, initialize like this:
circuit = CircuitOperationList(num_qubits=2, table=my_table)
Remembering that the table is mutable.
If you already have a list of CircuitOperations, you can initialize with it:
circuit = CircuitOperationList(circuit_ops, table=my_table) circuit.find_qubits()
Calling the
find_qubits()
method populates thequbits
attribute with the qubits found in loci of the operations in the original circuit. If the list is empty, it will setqubits
to an empty list, which most of the time is not what you want to do.The class has the
__add__
,__mul__
and__getitem__
methods redefined, which meanscircuit * 3
,circuit1 + circuit2
andcircuit[0:4]
will produce a CircuitOperationList with the samequbits
andtable
attributes as the original.To add a
prx
operation to the list, call:circuit.add_op('prx', [0], angle, phase, impl_name='drag_crf')
The class also has shortcut methods defined, so the above can be shortened to
circuit.prx(angle, phase, 0, impl_name='drag_crf')
which is exactly the same syntax as in Qiskit, with the addition of the implementation name which usually does not need to be used. The names of the shortcut methods are taken from the attached
table
at init. All the operations with non-zero arity will be added as shortcuts.If all the operations in the circuit are unitary, you can calculate the unitary propagator of the entire circuit by calling:
U = circuit.get_unitary()
The dimension of the unitary will always be defined by the
qubits
attribute. In particular, if your circuit contains 3 qubits,'QB1', 'QB2', 'QB3'
, but you only add gates to the first two, the resulting unitary will still be an 8x8 matrix, corresponding to the three qubits'QB1', 'QB2', 'QB3'
, in the big endian convention. With no operations affecting'QB3'
, the action of the unitary on this qubit is identity.To map the circuit onto physical qubits, all you need to do is call:
physical_circuit = circuit.map_loci(physical_qubits)
This will create a copy of the circuit, with all the placeholder qubits replaced by the physical qubits, with the order defined by the
qubits
attribute. For example, ifqubits = ['QB1', 'Alice', 'ZZZ']
, andphysical_qubits = ['QB2', 'QB5', 'QB10']
, all occurrences of'QB1'
will be mapped to'QB2'
,'Alice'
to'QB5'
and'ZZZ'
to'QB10'
. The original circuit is not modified, so you can create many copies with different physical qubits, which is helpful when running parallel experiments on a large chip.- Parameters:
contents (Iterable[CircuitOperation]) – Circuit operations to initialize the circuit with. Can be left out.
qubits (list[str]) – Qubits allowed to be used in operation loci in the circuit.
num_qubits (int) – Number of qubits in the circuit, will initialize
qubits
with['QB1', 'QB2', ...]
. Ignored ifqubits
is given.table (QuantumOpTable | None) – Allowed quantum operations.
Module:
iqm.pulse.circuit_operations
Attributes
qubits
Methods
Adds a new
CircuitOperation
to the circuit.Adds generic placeholder qubits from 1 to n.
Add barrier to the circuit
A safer way to add circuits together, but will probably take time.
Count each type of operation in the circuit.
Set attribute qubits to qubits in the loci of operations in the list.
Calculate the overall unitary implemented by a sequence of CircuitOperations.
Creates a new list of
CircuitOperation
s with locus mapped onto physical qubits.- find_qubits()#
Set attribute qubits to qubits in the loci of operations in the list.
- Return type:
None
- get_unitary(qubit_names=None)#
Calculate the overall unitary implemented by a sequence of CircuitOperations.
- add_op(name, locus_indices, *args, impl_name=None)#
Adds a new
CircuitOperation
to the circuit.Appends a new
CircuitOperation
at the end of the list. TheCircuitOperation
is created using aQuantumOp
name from the QuantumOpTable attached to the CircuitOperationList. The locus of thatCircuitOperation
is built from the qubits stored inqubits
, by selecting the qubits at indices given bylocus_indices
. For example, ifqubits
is['QB1', 'QB2', 'QB4']
, and thelocus_indices
is[2, 1]
, the locus of the newCircuitOperation
will be('QB4', 'QB2')
. All arguments for the values of the params of the requestedQuantumOp
need to be provided.- Parameters:
name (str) – Name of the
QuantumOp
which will generate a newCircuitOperation
.locus_indices (Sequence[int]) – Indices of the qubits in the attribute .qubits which will become the locus of the operation.
args – Any arguments the CircuitOperation needs, must correspond to the params of the
QuantumOp
.impl_name (str | None) – Name of the implementation to use when converting the
CircuitOperation
into aTimebox
later.
- Return type:
None
- barrier(*locus_indices)#
Add barrier to the circuit
- Return type:
None
- compose(other, locus_indices=None)#
A safer way to add circuits together, but will probably take time.
All the
CircuitOperation
s from the'other'
list are appended to the end of this list. The wirek
of the second circuit is connected to wirelocus_indices[k]
of the first. This is achieved by mapping the locus of each operation in the second circuit onto the qubits of the first.For example, if the
qubits
of the first list are['QB1', 'QB2']
, the second list has['QB3', 'QB4']
, and the locus_indices argument is[1,0]
, all the operations in the second list will have their'QB3'
mapped to'QB2'
and'QB4'
mapped to'QB1'
.
- count_ops()#
Count each type of operation in the circuit.
- Returns:
Counter mapping operation names to numbers of times they occur in the circuit.
- Return type:
- map_loci(locus, make_circuit=True)#
Creates a new list of
CircuitOperation
s with locus mapped onto physical qubits.Creates a fresh list of fresh
CircuitOperation
s with fresh arguments. Iflocus
is provided, it needs to have the same length as the total number of qubits across the circuit, and the qubits will then be mapped onto the new locus. If it is not provided, this is identical to a deepcopy of the original list.- Parameters:
locus (list[str] | None) – List of new qubits to replace the qubits in the loci of the operations in the circuit.
make_circuit (bool) – If True, creates a
CircuitOperationList
. If False, it is just a list.
- Returns:
New CircuitOperationList with loci mapped onto new locus.
- Return type: