Circuits¶
TrueQ™ uses the following highlevel classes to define circuits, their components, and their results:
Operation
: a quantum operationCycle
: a clock cycle of a quantum circuitCircuit
: a list of cycles and other metadataResults
: stores the results when a quantum circuit is run on hardware
Operations¶
The Operation
object is an abstract base class for
general quantum operations. TrueQ™ uses five different types of primitive
quantum operations:
Gate
: a generic unitary operation applied during a quantum circuit.NativeGate
: a unitary operation that is native to a given hardware platform.Prep
: an input to a quantum circuit.Meas
: generates the output of a quantum circuit.Block
: used to prevent compiler optimizations.
Gates¶
The Gate
class is a thin wrapper over a NumPy matrix. The primary
difference is that the equality for TrueQ™ gates ignores an overall phase (that is, a
complex number with unit modulus), which has no physical meaning in quantum information.
Any userspecified gate should be a unitary matrix. Although this is not checked upon
construction for performance reasons, it can be checked as shown below. Common gates for
qubits are aliased for convenience. All instances of Gate
are
immutable.
import trueq as tq
# view the list of aliased gates
print(tq.Gate.ALIASES)
# construct a Pauli X gate and visualize it using visualization.plot_mat()
gate = tq.Gate.x
tq.visualization.plot_mat(gate.mat)
# construct a gate from a matrix
phased_gate = tq.Gate([[0, 1j], [1j, 0]])
tq.visualization.plot_mat(phased_gate.mat)
# check that equality holds up to an overall phase
print("Equality holds up to a phase:", phased_gate == gate)
# check that equality holds up to an overall phase
scaled_gate = tq.Gate([[0, 2], [2, 0]])
print("scaled_gate is unitary:", scaled_gate.is_unitary)
print("Equality holds for arbitary constants:", phased_gate == scaled_gate)
{'id': Gate.id, 'x': Gate.x, 'y': Gate.y, 'z': Gate.z, 'cx': Gate.cx, 'cy': Gate.cy, 'cz': Gate.cz, 'swap': Gate.swap, 'iswap': Gate.iswap, 'h': Gate.h, 's': Gate.s, 'sx': Gate.sx, 't': Gate.t, 'ch': Gate.ch, 'i': Gate.id, 'cnot': Gate.cx, 'cliff0': Gate.id, 'cliff1': Gate.x, 'cliff2': Gate.y, 'cliff3': Gate.z, 'cliff4': Gate.cliff4, 'cliff5': Gate.sx, 'cliff6': Gate.cliff6, 'cliff7': Gate.cliff7, 'cliff8': Gate.cliff8, 'cliff9': Gate.s, 'cliff10': Gate.cliff10, 'cliff11': Gate.cliff11, 'cliff12': Gate.h, 'cliff13': Gate.cliff13, 'cliff14': Gate.cliff14, 'cliff15': Gate.cliff15, 'cliff16': Gate.cliff16, 'cliff17': Gate.cliff17, 'cliff18': Gate.cliff18, 'cliff19': Gate.cliff19, 'cliff20': Gate.cliff20, 'cliff21': Gate.cliff21, 'cliff22': Gate.cliff22, 'cliff23': Gate.cliff23}
Equality holds up to a phase: True
scaled_gate is unitary: False
Equality holds for arbitary constants: False
We provide additional methods to determine the generators of a gate and to construct gates via generators.
import trueq as tq
# generators can be obtained for generic gates
print(tq.Gate.h.generators)
# gates can be constructed from generators
gate = tq.Gate.from_generators("XX", 90, "YZ", 180)
tq.visualization.plot_mat(gate.mat)
{'X': (127.27922061357853+0j), 'Z': (127.27922061357856+0j)}
NativeGate¶
NativeGate
is a subclass of Gate
which introduces
three new attributes:
name
is a string giving a name to the gate.(optional)
parameters
is a dictionary that maps parameter names to parameter values.
These attributes are entirely metadata; there are no automatic consistency checks between them and the corresponding matrix. The purpose of these attributes is to provide tools such as transpilers to thirdparty circuit formats with a fast method of identifying gates without introspecting matrices. This requires trust that these metadata were generated correctly and in good faith.
Instances of NativeGate
are often constructed by
GateFactory
s, which are in turn are typically constructed by
Config
objects (see Configuration for more
details).
import trueq as tq
# make a factory that produces z rotations
rz = tq.config.GateFactory(name="Rz", matrix=[[1, 0], [0, "exp(1j*pi*theta/180)"]])
# construct a native gate
rz(90)
 Name:

 Rz(theta)
 Aliases:

 Gate.s
 Gate.cliff9
 Parameters:

 theta = 90
 Generators:

 'Z': 90.0
 Matrix:

Measurements¶
Meas
is a singleton class that records where measurements occur
in a Circuit
. This is always a singlequbit operation. The
results
attribute of a circuit must be consistent with the
total number of Meas
that are present.
Typically, all measurement objects are found in the last cycle in a circuit, however the API does not enforce this. Presently, the simulator fails if this is not the case.
Preparations¶
Prep
is a singleton class that records where state preparations
occur in a circuit. This is always a singlequbit operation. These objects are optional
in circuits and exist mainly:
To allow conversion to and from thirdparty circuit formats that use them.
To allow the simulator to add state preparation noise.
Block¶
Block
is a singleton class used to trivially occupy some set of
qubits in a circuit cycle; it is similar to the barrier
statement in Open QASM.
Its main purpose is to prevent compilation tools from merging adjacent gates, commuting
gates past each other from different cycles. However, the use of a unique
marker
on cycles is preferred, if possible.
Cycles¶
A Cycle
is a round of operations on a quantum device, which is
essentially a wrapper for a dictionary from tuples of qubit labels to instances of
operations discussed in the previous section. Any system that does not have a specified
gate is implicitly acted upon by an identity gate.
import trueq as tq
tq.Cycle({(0, 2): tq.Gate.cz, (1, ): tq.Gate.x})

(0, 2):
Gate.cz

(1):
Gate.x

Note
The order of system labels in the keys is important. For example, the
controlledX gate is controlled by the first qubit, so that
Cycle({(0, 1): tq.Gate.cx})
flips the state of qubit 1
based
on the state of qubit 0
, whereas Cycle({(1, 0): tq.Gate.cx})
flips the state of 0
based on the state of qubit 1
.
Results¶
Experimental results can be stored in a Results
object. This results
object is a thin wrapper for a dictionary class whose keys are concatenated strings
of outcomes (e.g., "001"
) to either relative frequencies or to the number of
observations. A results object also contains attributes such as the timestamp from when
the results were last modified, the dimension of the system, the total number of
measurements (i.e. the length of the strings), and the total number of shots.
Outcomes that were not observed should not be entered.
For instance, the results of flipping two coins 20 times and recording the results might be stored as follows:
import trueq as tq
tq.Results({"10": 5, "00": 2, "01": "10", "11": 3})
Results({'10': 5, '00': 2, '01': '10', '11': 3})
Circuits¶
Circuit
objects hold three pieces of information.
A circuit is intended to be constructed from a list of cycles and a key containing any relevant information. Results can be passed into the constructor, but would typically be added after the circuit is executed on hardware or a simulator as in the following example.
import trueq as tq
circuit = tq.Circuit(cycles=[
tq.Cycle({(0, ): tq.Gate.x, (1, ): tq.Gate.y}),
tq.Cycle({(0, 2): tq.Gate.cz, (1, ): tq.Gate.x}),
tq.Cycle({(3, ): tq.Gate.h, (5, ): tq.Gate.s})
], key=tq.Key(foo="bar"))
circuit.measure_all()
circuit.results = {"01000": 3, "01001": 2, "01010": 1, "01011": 4}
circuit.draw()