qat-core: Core data structures and Abstract classes
Abstract classes
Following classes may be used to define new Plugins or QPUs.
- class qat.plugins.AbstractPlugin(*args, **kwargs)
Abstract interface of a Plugin.
- abstract compile(batch, specs)
Compiles a Batch into another Batch according to some hardware specs
- do_post_processing()
Returns True iff the results need to be aggregated/post processed via the plugin.
- serve(port, host_ip='localhost', server_type=None)
Runs the plugin inside a server
- Parameters
port (int) – the port on which to listen
host_ip (str) – the url on which to publish the API. Optional. Defaults to ‘localhost’.
server_type (str, optional) –
type of server. The different types of server are:
”simple”: single-thread server, accepts one connection at a time (default server type)
”threaded”: multi-thread server, each connection starts a new thread
”pool”: multi-thread server, each connection runs in a thread, with a maximum of 10 running threads
”fork”: multi-process server, each connection runs in a new process
- wrapper_post_process(results)
Call post process function and wrap the result in a PostProcessResult object
- class qat.core.qpu.QPUHandler(plugins=None)
Abstract class describing the mininmal QPU interface.
The minimal interface is the implementation of the submit_job method:
This method should return a Result structure corresponding to the execution of a Job.
- serve(port, host_ip='localhost', server_type=None)
Runs the QPU inside a server
- Parameters
port (int) – the port on which to listen
host_ip (str) – the url on which to publish the API. Optional. Defaults to ‘localhost’.
server_type (str, optional) –
type of server. The different types of server are:
”simple”: single-thread server, accepts one connection at a time (default server type)
”threaded”: multi-thread server, each connection starts a new thread
”pool”: multi-thread server, each connection runs in a thread, with a maximum of 10 running threads
”fork”: multi-process server, each connection runs in a new process
- submit(batch: Batch, meta_data: Optional[dict] = None) BatchResult
Executes a batch of jobs and returns the corresponding list of Results.
- Parameters
batch (
Batch
) – a batch of jobs. If a single job is provided, the job is embedded into a Batch, executed, and the first result is returned.- Returns
a batch result
- Return type
- class qat.core.generator.AbstractGenerator(*args, **kwargs)
Abstract interface for a batch generator.
An abctract generator has a single abstract method ‘generate’. This generate function can return either:
a generator (use of keyword ‘yield’)
a
Batch
An optional method ‘post_process’ can be also defined to parse the result. This method is used if one of the following condition is met:
method
generate()
returned aBatch
method
generate()
returned an iterator which does not contains anyBatchResult
object
The batch generator section give some examples of dummy batch generators
- abstract generate(specs, *args, **kwargs)
Generates a batch. This function could return either an iterator or a Batch
- serve(port, host_ip='localhost', server_type=None)
Runs the batch generator inside a server
- Parameters
port (int) – the port on which to listen
host_ip (str, optional) – the hostname on which to publish the API. Defaults to ‘localhost’
server_type (str, optional) –
type of server. The different types or server are:
”simple”: single-thread server, accepts one connection at a time (default server type)
”threaded”: multi-thread server, each connection starts a new thread
”pool”: multi-thread server, each connection runs in a thread, with a maximum of 10 running threads
”fork”: multi-process server, each connection runs in a new process
- class qat.core.application.Application(generator, qpu)
An application is a class used to generate results. An application is composed of a BatchGenerator and of a QPU
Warning
This class is not designed to be instantiated by hand, but by piping a BatchGenerator to a QPU
- Parameters
generator (AbstractGenerator) – Batch generator
QPU (CommonQPU) – QPU
- execute(*args, **kwargs)
Executes the application (generates batches and executes them)
Arguments given to this method are passed to the
generate
method of the generatorNote
The first argument passed to
generate
is theHardwareSpecs
object returned by the QPU. Following arguments are those passed in argument of this function.- Returns
Any python object
- Return type
any
- serve(port, host_ip='localhost', server_type=None)
Runs the application inside a server
- Parameters
port (int) – the port on which to listen
host_ip (str) – the url on which to publish the API. Optional. Defaults to ‘localhost’.
server_type (str, optional) –
type of server. The different types of server are:
”simple”: single-thread server, accepts one connection at a time (default server type)
”threaded”: multi-thread server, each connection starts a new thread
”pool”: multi-thread server, each connection runs in a thread, with a maximum of 10 running threads
”fork”: multi-process server, each connection runs in a new process
Remote interfaces
QPUs and Plugins can be used in client/server mode. Any Plugin or QPU, once constructed, can be published via their serve method (see right above).
Once published, a client can connect and interact with them via one of the two following classes:
- class qat.core.qpu.RemoteQPU(port=None, ip=None, connection=None, plugins=None)
RemoteQPUs are built from a port and an ip address. They behave similarly to a local QPU Handler but also allow adjunction of local Plugins. The information flow inside a RemoteQPU is comparable to the information flow inside a QPUHandler:
when compiling a batch the batch goes through all the local plugins and is sent to the connection. The resulting batch is then returned to the user
when submitting, the batch is compiled using the local plugins and then forwarded through the connection. The results are then post
processed by the local Plugins.
- Parameters
port (optional, int) – the port to connect to
ip (optional, str) – the IP address to connect to
plugins (list<qat.plugins.AbstractPlugins>) – a list of plugins. Optional. Defaults to None.
connection (optional Thrift connection) – for internal use
- class qat.core.plugins.RemotePlugin(port=None, ip=None, connection=None)
RemotePlugins are built from a port and an ip address.
- Parameters
port (optional, int) – a port to connect to
ip (optional, str) – the IP address to connect to
connection (optional, Thrift connection) – for internal use
- class qat.core.generator.RemoteBatchGenerator(port: int = None, ip: unicode = None)
Remote batch generator. This batch generator creates connection between you and a remote
AbstractGenerator
started in server mode (c.f.serve()
)- Parameters
port (int, optional) – a port to connect to
ip (str, optional) – the IP address to connect to
- class qat.core.application.RemoteApplication(port: int = None, ip: unicode = None)
RemoteApplications are built from a port and an IP address
- Parameters
port (int, optional) – a port to connect to
ip (str, optional) – IP address to connect to
Batch and Job
Following are used to send a circuit or a set of circuit
to a plugin or to a QPU. The circuit is then compiled or
executed.
The Job
class is used to send
a single Circuit
to a QPU whereas
the Batch
class is used to send
several Circuit
objects at the
same time.
- class qat.core.Job(psi_0=None, parameter_map=None, **kwargs)
A high level wrapper for the Job thrift class.
- Instance attributes:
circuit (
Circuit
): circuit to executeschedule (Schedule): schedule to execute
type (
ProcessingType
): type of Jobobservable (
Observable
, optional): the observable (required if job type is of type 1, i.e observable sampling)qubits (list<int>, optional): measured qubits
nbshots (int): number of required shots: if 0, do maximal number of shots
aggregate_data (bool): contract corresponding result object
amp_threshold (float, optional): amplitude values under this threshold are considered null
parameter_map (dict[str, complex], optional): read-only attribute containing all the binded variables with their values
- differentiate(vname, method=None, **kwargs)
Generates a collection of variational jobs such that the sum of their energies equates the partial derivative of the job’s energy w.r.t to a given variable.
\[\frac{\partial }{\partial \theta_j} \langle \psi(\theta) | H | \psi(\theta) \rangle = \sum_i \langle\psi_i(\theta) | H_i |\psi_i(\theta) \rangle\]where \((|\psi\rangle, H)\) is the input job and \((|\psi_i\rangle, H_i)\) are the output jobs.
Supported methods are:
shift-rule uses the chain rule for product differentiation together with a shift rule. This method generates \(2k\) jobs where \(k\) is the number of rotations parametrized by the variable.
h-test performs a Hadamard test and a modifies the observable to sample in order to encode the appropriate post-selection induced by the Hadamard test. It generates \(k\) jobs where \(k\) is the number of rotations parametrized by the variable. These jobs have an additional qubit and have observables that are (at most) twice as large as the initial observable.
- Parameters
vname (str) – a variable name.
method (str, optional) – the method to use. Default ‘shift-rule’.
kwargs (dict) – any additional parameter that can be passed to the differentiation method.
- Returns
a list of jobs
- Return type
list<Job>
Warning
The method can be called only on a job containing an observable.
- dump(fname)
Dumps the job in a binary file.
- Parameters
fname (str) – the file name
- classmethod from_bytes(data: bytes)
Builds a job from raw bytes object
- classmethod from_thrift(tobject)
Builds a Job from a thrift job
- get_variables()
Returns the sorted list of all variables appearing in the job
- gradient(method=None, **kwargs)
Generates a collection of groups of jobs such that the sum of the energy of each group equates the partial derivative of the job’s energy w.r.t each of its variable.
The collection is returned as a map mapping variable names to job list.
This method has serveral backends, see
differentiate()
for more details.- Parameters
method (str, optional) – the differentiation method
kwargs (dict) – additional arguments passed to the differentiation method
- Returns
a map associating variable names to list of jobs
- Return type
map<str, list<Job>>
- classmethod load(fname)
Loads the Job from a binary file.
- Parameters
fname (str) – the file name
- Returns
- property parameter_map: dict
Get the parameter map stored in a thrift dictionary
- Returns
dictionary of binded variables
- Return type
dict[str, complex]
- property psi_0
Get initial state
- Returns
the initial state
- Return type
str or np.ndarray
- shift_qbits(offset)
Shifts the job’s qbits by a given offset. Consequently icreases the number of qubits of the job by the same offset.
Acts in place.
- Parameters
offset (int) – the offset
- to_thrift()
Cast the Result into a pure Thrift object
Processing types for jobs are defined via an enum class:
Different types of processings of the final quantum state.
- Possible values:
SAMPLE=0: sampling in the computational basis
OBSERVABLE=1: observable sampling
- class qat.core.Batch(jobs=None, meta_data=None)
Simple higher level wrapper for the serializable Batch class.
- Instance attributes:
jobs (list<
Job
>): jobs composing the batchmeta_data (dict<str, str>): meta data
- dump(fname)
Dumps the Batch inside a file in binary format.
- Parameters
fname (str) – the file name
- classmethod from_bytes(data: bytes)
Builds a batch from raw bytes object
- classmethod from_thrift(tobject)
Builds a Batch object from a thrift batch
- get_variables()
Returns the sorted list of all variables appearing in the Batch.
- to_thrift()
Cast the Result into a pure Thrift object
Observables
Observables are objects implementing the concept of a quantum mechanical observable \(\hat{O}\). They can be written in qubit (Pauli matrices) representation as well as others (fermions, bosons).
- class qat.core.Observable(nqbits, matrix=None, pauli_terms=None, ising=None, constant_coeff=0.0, do_clean_up=True)
Class describing an observable (i.e some Hermitian operator) over the Hilbert space spanned by a list of qbits.
The observable is stored as a sum of Terms, each composed of a Pauli operator and a real coefficient. It is also possible to store it with an Ising object which represents an Ising Hamiltonian.
The purpose of this class is mainly to efficiently automatize the sampling of this observable on the final state obtained after a computation.
Construction:
- Parameters
nqbits (int) – number of qubits
matrix (np.array, optional) – the matrix representing the observable. Defaults to None.
pauli_terms (list<Term>, optional) – the list of products of Pauli operators representing the observable. Defaults to None.
ising (
Ising
, optional) – an object representing an Ising encoded problemconstant_coeff (float) – a constant additive term
do_clean_up (boolean) – remove the constant terms Default: True
- terms
the list of products of Pauli operators representing the observable.
- Type
list<Term>
- nbqbits
the number of qbits on which the observable acts
- Type
int
- constant_coeff
a constant additive term
- Type
float
Check this notebook for an example of use and manipulation of observables.
- class qat.core.Term(coefficient, pauli_op, qbits, do_validity_check=True)
Class describing a term in the Pauli basis of an observable. It basically consists in a pair (coeffcient, pauli operator).
This class supports Thrift serialization and can be transmitted directly to the to_job method of a circuit object.
Term are constructed as follows:
from qat.core import Term term = Term(1., # Coefficient of the term "XXY", # tensor of operators X, X, and Y [1, 4, 7]) # over qubits 1, 4, and 7
- Parameters
coefficient (float) – the coefficient in front of the term
pauli_op (str) – the Pauli operator specified as a string containing I, X, Y, and Z characters.
qbits (list<int>) – the list of concerned qubits
check_validity (bool) – if True, checks that no qbit has more than one operator applied to it. default: True
- coeff
the coefficient in front of the term
- Type
float
- op
the Pauli operator specified as a string containing I, X, Y, and Z characters.
- Type
str
- qbits
the list of concerned qubits
- Type
list<int>
Schedules
On the QLM, the way to represent Hamiltonians is via the class Schedule
. The following sections present examples on how one can encode Schedules, use arithmetic operations between them and send them to a dedicated analog QPU for simulation.
- class qat.core.Schedule(drive=None, tmax=1.0, tname='t', gamma_t=None)
Schedules encode time-dependent Hamiltonians, described by the general expression:
\[H(t) = \sum_i \lambda_i(t) H_i\]with \(H_i\) a Hermitian operator. The \(H\) will be evolved for a time tmax. From a Schedule one can create a
Job
via theto_job()
method and send it to a dedicated analog QPU for execution.- Parameters
drive (
Observable
or list<tuple<Variable
(or float),Observable
>>) – the time dependent Hamiltonian to be driven.tmax (
Variable
or float) – the time at which the evolution halts. Default is 1.0.
- dump(fname)
Dumps the schedule in a binary file
- Parameters
fname (str) – the file name
- get_variables()
Returns the sorted list of all variables appearing in the schedule and its constituents
- classmethod load(fname)
Loads a schedule from a binary file.
- Parameters
fname (str) – the file name
- Returns
a schedule
- Return type
- to_job(job_type='SAMPLE', qubits=None, observable=None, psi_0=None, **kwargs)
Generates a
Job
containing the schedule and some post processing information.- Parameters
job_type (string, optional) – possible values are “SAMPLE” for computational basis sampling of all (or some) qubits, or “OBS” for returning the expectation value of an
Observable
.qubits (list<int> or list<QRegister>, optional) – the list of qubits to measure (in “SAMPLE” mode). Defaults to None, meaning all qubits are to be measured.
observable (
Observable
, optional) – an observable (in “OBS” mode) the expectation value of which will be returned.psi_0 (string or np.ndarray optional) – initial state of the processor. If of type str, it is the bitstring of length nqbits encoding a computational basis state. One can also use ‘+’ and ‘-’ to represent the respective superposition states, so a six-qubit input may look like ‘1+-01+’. If of type ndarray or list<ComplexNumber>, it is a length 2^nqbits array containing the complex amplitudes of the input state. Defaults to None (i.e |00..0>).
nbshots (int, optional) – The number of shots to perform. Defaults to zero in which case the convention is that the QPU should do its best: a quantum processor will use the largest amount of shots authorized by its configuration, while a simulator will try to output all the possible states contained in the final distribution, together with their probabilities (and possibly amplitudes) as if it did infinitely many executions.
aggregate_data (bool, optional) – if set to True, and nbshots is not zero, the samples will be aggregated and their probability field will be used to store their observed frequencies of apparition. Defaults to True.
amp_threshold (double, optional) – amplitude threshold under which states are not returned in the result structure. Useful to prune states that are unlikely to be measured out of the returned samples. Defaults to 1/2^40.
Building schedules
Schedule
objects are specified using a collection of pairs of time-dependent coefficients and Observable
objects.
Time dependent coefficients are simply arithmetic expressions (built from Variable
), with a possible open parameter representing the time dependence (usually a variable t).
This collection of pairs is called a “drive”.
Here is a simple example that constructs a schedule containing the time-dependent Hamiltonian:
from qat.core import Variable, Schedule, Observable, Term
t_variable = Variable("t")
schedule = Schedule(drive=[(1 - t_variable, Observable(1, pauli_terms=[Term(1, 'Z', [0])])),
(t_variable, Observable(1, pauli_terms=[Term(1, 'X', [0])]))],
tmax=23.0)
print(schedule)
drive:
(1 - t) * 1 * (Z|[0])
t * 1 * (X|[0])
tmax = 23.0
As you can see, one also needs to provide the time during which the schedule is defined (the tmax parameter).
All scalars (i.e coefficients, tmax, etc) can be abstract arithmetic expressions:
from qat.core import Variable, Schedule, Observable, Term
t_variable = Variable("t")
tmax_variable = 15 * Variable("tmax") - 5
schedule = Schedule(drive=[(1 - t_variable, Observable(1, pauli_terms=[Term(1, 'Z', [0])])),
(t_variable, Observable(1, pauli_terms=[Term(1, 'X', [0])]))],
tmax=tmax_variable)
print(schedule, "\n")
print(schedule(tmax=10))
drive:
(1 - t) * 1 * (Z|[0])
t * 1 * (X|[0])
tmax = ((15 * tmax) - 5)
drive:
(1 - t) * 1 * (Z|[0])
t * 1 * (X|[0])
tmax = 145
Drives can also be declared using an Observable
with time-dependent coefficients:
from qat.core import Variable, Schedule, Observable, Term
t_variable = Variable("t")
hamiltonian = (1 - t_variable) * Observable(1, pauli_terms=[Term(1, 'Z', [0])]) + \
t_variable * Observable(1, pauli_terms=[Term(1, 'X', [0])])
schedule = Schedule(drive=hamiltonian,
tmax=23.0)
print(schedule)
drive:
1 * (1 - t) * (Z|[0]) +
t * (X|[0])
tmax = 23.0
Manipulating schedules
The Schedule
class has some overloaded operators which allow you to manipulate them efficiently.
Temporal composition
Two schedules can be temporally composed using the pipe/or operator:
from qat.core import Variable, Observable, Schedule, Term
t_variable = Variable("t")
schedule1 = Schedule(drive=(1 - t_variable) * Observable(1, pauli_terms=[Term(1, 'Z', [0])]),
tmax=2.0)
schedule2 = Schedule(drive=t_variable * Observable(1, pauli_terms=[Term(1, 'X', [0])]),
tmax=3.0)
print(schedule1 | schedule2)
drive:
heaviside(t,0,2.0) * (1 - t) * (Z|[0])
heaviside(t,2.0,5.0) * (t - 2.0) * (X|[0])
tmax = 5.0
Note how the coefficients are ponderated by a heaviside signal to filter the ranges of the two schedules.
Parallel composition
Two schedules can be merged into a single schedule containing the sum of the two drives using an addition.
from qat.core import Variable, Observable, Schedule, Term
t_variable = Variable("t")
schedule1 = Schedule(drive=(1 - t_variable) * Observable(1, pauli_terms=[Term(1, 'Z', [0])]),
tmax=2.0)
schedule2 = Schedule(drive= t_variable * Observable(1, pauli_terms=[Term(1, 'X', [0])]),
tmax=3.0)
print(schedule1 + schedule2)
drive:
heaviside(t,0,2.0) * (1 - t) * (Z|[0])
heaviside(t,0,3.0) * t * (X|[0])
tmax = 3.0
Rescaling a schedule
Schedule can be rescaled by multiplication by a scalar:
from qat.core import Variable, Observable, Schedule, Term
t_variable = Variable("t")
schedule1 = Schedule(drive=(1 - t_variable) * Observable(1, pauli_terms=[Term(1, 'Z', [0])]),
tmax=2.0)
print(45 * schedule1, "\n")
print(Variable("foo") * schedule1)
Reverse mult 45 * drive:
1 * (1 - t) * (Z|[0])
tmax = 2.0
drive:
45 * (1 - t) * (Z|[0])
tmax = 2.0
Reverse mult foo * drive:
1 * (1 - t) * (Z|[0])
tmax = 2.0
drive:
foo * (1 - t) * (Z|[0])
tmax = 2.0
Time translation of a schedule
Schedule can be delayed (in the past or the future) by using the bit shift operators << and >>:
from qat.core import Variable, Observable, Schedule, Term
t_variable = Variable("t")
schedule1 = Schedule(drive=(1 - t_variable) * Observable(1, pauli_terms=[Term(1, 'Z', [0])]),
tmax=2.0)
print(schedule1 >> 3)
print(schedule1 << Variable('bar'))
drive:
heaviside(t,3,5.0) * (1 - (t - 3)) * (Z|[0])
tmax = 5.0
drive:
heaviside(t,-(bar),(2.0 + -(bar))) * (1 - (t - -(bar))) * (Z|[0])
tmax = (2.0 + -(bar))
Analog Jobs
Similarly to quantum circuits, schedules can be turned into jobs using the to_job method:
from qat.core import Variable, Observable, Schedule, Term
t_variable = Variable("t")
schedule = Schedule(drive=(1 - t_variable) * Observable(1, pauli_terms=[Term(1, 'Z', [0])]),
tmax=2.0)
# To simply sample the final state in the computational basis
job = schedule.to_job()
# To evaluate some observable at the end of the computation
job = schedule.to_job(observable=Observable(1, pauli_terms=[Term(1, 'Z', [0])]))
This method takes more or less the same arguments as the quantum circuit’s method with the same name.
One important difference to notice: it is possible to change the starting state of the computation using the psi_0 argument:
import numpy as np
from qat.core import Variable, Observable, Schedule, Term
t_variable = Variable("t")
schedule = Schedule(drive=(1 - t_variable) * (Observable(2, pauli_terms=[Term(1, 'Z', [0])]) +
Observable(2, pauli_terms=[Term(1, 'Z', [1])])),
tmax=2.0)
# Starting from |++> state
job = schedule.to_job(psi_0='++')
# Starting from |+1> state
job = schedule.to_job(psi_0='+1')
# Starting from a random initial state (simulator only)
vec = np.random.random(4)
vec /= np.linalg.norm(vec)
job = schedule.to_job(psi_0=vec)
Results
A result is composed of several samples or, if an observable is measured,
of a scalar. The result data structure is Result
- class qat.core.Result(lsb_first=False, nbqbits=None, value=None, values=None, parameter_map=None, **kwargs)
Simple higher level wrapper for the serializable Result class.
- Parameters
result (ThriftResult, optional) – result
lsb_first (bool, optional) – whether to use convention with least significant bit first. Defaults to False.
nbqbits (int, optional) – number of qubits, required if lsb_first is False. Defaults to None.
- Instance attributes:
raw_data (list<
Sample
>, optional): the raw data sampling, if requestedvalue (float, optional): scalar output
value_data (map<str, float>, optional): Information on the scalar output (deviation, etc)
meta_data (map<str, str>): any information the QPU might want to transmit to the user (logs, resource usage, etc)
has_statevector (bool): set to True iff the .statevector attribute is non-empty
statevector: a low level description of the result. Usually contains a numpy array describing either a state vector or a probability distribution. Not always set.
- add_sample(state, amplitude=None, probability=None, err=None, intermediate_measurements=None)
Add sample to result. If required, reverts bit order.
- Parameters
state (int) – the index of the state in comput. basis
amplitude (complex, optional) – a complex number for the amplitude
probability (float, optional) – the probability
err (float, optional) – the error on the probability estimate
intermediate_measurements (IntermediateMeasurements, optional) – the intermediate measures
- dump(fname)
Dumps the Result inside a file in binary format.
- Parameters
fname (str) – the file name
- classmethod from_bytes(data: bytes)
Builds a result from raw bytes object
- classmethod from_thrift(tobject)
Builds a Result object from a thrift object
- static load(fname)
Loads a Result from a file.
- Parameters
fname (str) – the file name
- Returns
- property parameter_map
complex}
- Type
Dictionnary {str
- property value
Scalar output - complex number
- property values
Scalar output - complex number
- wrap_samples(qreg_list)
Wraps the samples using a list of quantum registers Also serializes the underlying statevector object, if any
- class qat.core.wrappers.result.Sample(qregs=None, amplitude=None, state=None, **kwargs)
Simple higher-level wrapper for the serializable Sample class.
- Parameters
qregs (
QRegister
) – quantum register
- Instance attributes:
state (
State
): the measured stateprobability (float, optional): the probability of the state (infinite number of shots/samples, for simulators only), or frequency of the state (finite number of shots)
amplitude (complex, optional): optionally (for simulators), the amplitude of the state
intermediate_measurements (
IntermediateMeasurement
): list of intermediate measurementserr (float, optional): optionally, the sampling error: if the number of samples is finite, it is the estimate of the standard error on the mean \(\varepsilon_{k}\equiv\sqrt{\langle\left(p_{k}-f_{k}\right)^{2}\rangle}\), where \(p_k\) is the true probability of obtaining state \(|k\rangle\) and \(f_k\) its estimate. if the number samples is infinite (simulator only), it is the error bar on the computed probability (for approximate simulators only, like stochastic simulators)
- property amplitude
Returns the amplitude of the Sample (wrapped in a Python complex number)
- classmethod from_bytes(data: bytes)
Builds a sample from raw bytes object
- classmethod from_thrift(tobject)
Builds a Sample object from a thrift object
- set_qreg(qregs)
Sets the quantum register information for pretty display and cast of states
- property state
Gets the state of the sample. The state may be wrapped in a State object
- class qat.core.wrappers.result.State(int_msb_value, qregs=None)
Class representing a ‘classical’ state
Its __str__ method is overloaded to possibly take a QRegister type as format argument.
- Parameters
int_msb_value (int) – the state (integer, msb format)
qregs (list<QRegister>, optional) – a list of QRegister to use for formatting purposes.
- property bitstring
Returns a bitstring representation of the state
- property int
Returns a msb representation of the state
- property lsb_int
Returns a lsb reprensentation of the state
- property value
Casts the state into a classical value using the information from the quantum registers
Data structure representing outcome of an intermediate measurement.
- - cbits
list<bool> the list of classical bits resulting from the measurement
- - gate_pos
int the position of the measurement in the circuit
- - probability
optional, float, the probability of this precise measurement outcome
- class qat.core.BatchResult(**kwargs)
Simple higher level wrapper for the serializable BatchResult class.
- Parameters
kwargs (keyword arguments) – accepted arguments are results (a list of
Result
objects) and meta_data (a (str, str) map).
- Instance attributes:
results (list<
Result
>): list of resultsmeta_data (dict<str, str>): meta data
- dump(fname)
Dumps the BatchResult inside a file in binary format.
- Parameters
fname (str) – the file name
- classmethod from_thrift(tobject)
Builds a BatchResult object from a thrift object
- static load(fname)
Loads a BatchResult from a file
- Parameters
fname (str) – the file name
- Returns
Circuits
Here is the basic interface of the circuit class.
For more information on the various attributes of the circuit object, please refer to Circuits.
For instance, one can easily concatenate two Circuit
objects (as long as they have the same number of qubits):
cat_circuit = circuit1 + circuit2
Tensorial composition is implemented via an overloading of the __mult__
operator:
kron_circuit = circuit1 * circuit2
Among the other useful methods, we can find:
the direct generation of a
Job
object from a circuit using various parameters:job = circuit.to_job(job_type="SAMPLE", nbshots=1024, qubits=[0, 3, 7])
(de)serialization of a circuit:
circuit.dump("my_circuit.circ") circuit = Circuit.load("my_circuit.circ")
binding of an abstract variable:
new_circuit = circuit(theta=0.34) # or, equivalently new_circuit = circuit.bind_variables({theta: 0.34})
- class qat.core.Circuit(ops=None, name=None, gateDic=None, nbqbits=None, nbcbits=0, _gate_set=None, has_matrices=None, var_dic=None, qregs=None, ancilla_map=None, _serialized_gate_set=None)
Simple higher level wrapper for the serializable Circuit class.
This wrapper provides high-level manipulation routines such as variable binding or iteration.
Circuits are usually built using the .to_circ method of the
Program
class, and are not designed to be built by hand.The simplest way to explore the content of a circuit is to use the .iterate_simple method that provides a user friendly sequence of instructions:
from qat.lang.AQASM import * prog = Program() cbits = prog.calloc(4) qbits = prog.qalloc(4) for qb in qbits: H(qb) RX(prog.new_var(float, "a")).ctrl(3)(qbits) CNOT(qbits[0], qbits[1]) prog.measure(qbits[1]) prog.reset([qbits[3]], [cbits[2]]) circuit = prog.to_circ() for op in circuit.iterate_simple(): print(op)
('H', [], [0]) ('H', [], [1]) ('H', [], [2]) ('H', [], [3]) ('C-C-C-RX', [<qat.core.variables.Variable object at 0x151a8db9acd0>], [0, 1, 2, 3]) ('CNOT', [], [0, 1]) ('MEASURE', [1], [1]) ('RESET', [3], [2])
- bind_variable(v_name, v_value, gate_set=None)
Bind variable v_name with value v_value. Returns a fresh circuit. If binding the variable result in a gate becoming fully defined, its matrix will be regenerated using the default gate set of pyAQASM (or using a custom gate set).
Warning
For performances reasons, this method does not fully copy the circuit. In particular, modifying some portions of the resulting circuit will have a side effect of the initial circuit. To avoid this behavior, feel free to deepcopy the circuit beforehand.
- bind_variables(v_dictionary, gate_set=None, **kwargs)
Iterates
bind_variable()
over a dictionary of (names, values).- Parameters
v_dictionary (map<str,any>) – the variable names/values dictionary
gate_set (optional, GateSet) – a gate set to use to generate the matrices for freshly bound gates. If not None, the current gate set will be updated and used to fill the matrices
- Returns
a quantum circuit.
- Return type
- count(gate)
Return the number of occurences of a given gate.
- Parameters
gate (str) – a gate name
- Returns
a gate count
- Return type
int
- dag(dagger_from_params=None, **kwargs)
Computes the dagger of a circuit. This function:
crawls the gate dictionary and replace subgates by their dagger
clears the implementations of these subgates (i.e their matrices, subcircuits, etc)
reverts the list of instructions of the circuit
- Raises
TypeError if the circuit contains non-unitary instructions – (i.e classical operations, measurements, etc)
ValueError if the circuit contains a gate with no proper – syntax, or if gate name is not in the dagger_from_params map.
- Parameters
dagger_from_params (dict, optional) – a dictionary that maps gate names to callable that, given a set of parameters, returns the new parameters that implement the dagger of the gate (see example below)
kwargs – any additional keyword arguments are fed to a
Linker
in order to re-generate the various matrices and subcircuits.
ExampleBy default, the default gate set of pyAQASM is fully supported:
# Building a simple circuit from qat.lang.AQASM import * prog = Program() qbits = prog.qalloc(2) H(qbits[0]) PH(1.22)(qbits[1]) RY(3.9).ctrl()(qbits) CNOT(qbits) circuit = prog.to_circ() print("Circuit dagger:") for op in circuit.dag(): print(op)
Circuit dagger: Op(gate='CNOT', qbits=[0, 1], type=0, cbits=None, formula=None, remap=None) Op(gate='_10', qbits=[0, 1], type=0, cbits=None, formula=None, remap=None) Op(gate='_8', qbits=[1], type=0, cbits=None, formula=None, remap=None) Op(gate='H', qbits=[0], type=0, cbits=None, formula=None, remap=None)
If the circuit contains custom asbtract gate, one can specify a way to change their parameters in order to invert them. The following examples shows how to define a \(ZZ\) rotation and specify that one needs to flip the sign of its angle in order to invert it.
# Building a simple circuit from qat.lang.AQASM import * ZZ = AbstractGate("ZZ", [float], arity=2) prog = Program() qbits = prog.qalloc(2) H(qbits[0]) CNOT(qbits) ZZ(5.334)(qbits) circuit = prog.to_circ() # Default dag function print("=== Default dagger (default) ===") dagger = circuit.dag() for gate in dagger.iterate_simple(): print(gate) # Dag rule dagger_from_params = { "ZZ": lambda params: [- params[0]], } print("=== Circuit dagger (smarter) ===") smart_dagger = circuit.dag(dagger_from_params) for gate in smart_dagger.iterate_simple(): print(gate)
=== Default dagger (default) === ('D-ZZ', [5.334], [0, 1]) ('CNOT', [], [0, 1]) ('H', [], [0]) === Circuit dagger (smarter) === ('ZZ', [-5.334], [0, 1]) ('CNOT', [], [0, 1]) ('H', [], [0])
- display(**kwargs)
Displays the circuit. This method adapts to the environment (i.e. notebook or terminal).
It will call one of the following methods:
qat.core.printer.plot_in_notebook()
if inside a notebookqat.core.console.display()
if in a terminal
All kwargs are passed to these methods.
- dump(fname)
Dumps the circuit in a binary file (.circ)
- Parameters
fname (str) – the file name
- static empty(nbqbits)
Generates an empty circuit
- Parameters
nbqbits (int) – the number of qbits of the circuit
- Returns
an empty circuit
- Return type
- fill_matrices(gate_set, submatrices_only=True, include_default_gate_set=False)
Attaches matrices to gates according to the matrix generators in
gate_set
.- Parameters
gate_set (
GateSet
) – a gate set specifying the matrix generators of the gates present in the circuit. Note that if some gate has no corresponding entry in the gate set or its signature has no matrix generator, no matrix will be attached to its definition.submatrices_only (bool) –
If set to True, only subgate will receive matrices. Default to True.
include_default_gate_set (bool) – if set to True, the native gate set of pyAQASM will be included. Default to False.
- classmethod from_bytes(data: bytes)
Builds a circuit from raw bytes object
- classmethod from_thrift(tobject)
Builds a Circuit from a thrift circuit
- property gate_set
The gate set used to generate the circuit
- get_variables()
Returns the sorted list of variables appearing in the circuit
- insert_gate(position, gate, qbits, **kwargs)
Inserts a pyAQASM gate inside the circuit.
- Parameters
position (int) – index where to insert the gate
gate (
Gate
) – a pyAQASM gate (or routine)qbits (list<int>) – a list of integers
kwargs (dict) – all the keyword arguments are fed to the linker
- iterate_simple(depth=None)
Iterates over the instructions of the circuit in a user friendly way.
The iterator yields tuples with explicit content. Each tuples starts with a keyword or a gate name, followed by the arguments of the instruction.
E.g, a measure of qubits 0 and 1 stored in cbits 2 and 3 would yield:
("MEASURE", [0, 1], [2, 3])
- classmethod load(fname)
Loads a circuit from a binary (.circ) file.
- Parameters
fname (str) – the file name
- Returns
a circuit
- Return type
- remove_locks()
Remove all lock/release placeholders of the circuit.
- shift_qbits(offset)
Shift all the qbit indexes of the circuit by a given offset. Consequently increases the number of qbits by the same offset.
Acts in place.
- Parameters
offset (int) – the offset
- statistics()
Returns some statistics on the circuit.
The returned value is a dictionary with a very straightforward content.
- Returns
a dictionary
- Return type
dict
- to_job(job_type='SAMPLE', qubits=None, nbshots=0, aggregate_data=True, amp_threshold=9.094947017729282e-13, **kwargs)
Generates a Job containing the circuit and some post processing information.
- Parameters
job_type (str) – possible values are “SAMPLE” for computational basis sampling of some qubits, or “OBS” for observable evaluation (see
qat.core.Observable
for more information about this mode).qubits (optional, list<int>, list<QRegister>) – the list of qubits to measure (in “SAMPLE” mode). If some quantum register is passed instead, all the qubits of the register will be measured. Moreover, if the register was “typed” (see
qat.lang.AQASM.Program.qalloc()
for more information), the results will be cast into the register type. Defaults to None, meaning all qubits are to be measured.nbshots (optional, int) – The number of shots to perform. Defaults to zero. If set to zero, the convention is that the QPU should do its best: a quantum processor will use the largest amount of shot authorized by its configuration, while a simulator will try to output all the possible states constained in the final distribution, together with their probabilities (and possible amplitudes).
aggregate_data (optional, bool) – if set to True, and nbshots is not zero, the samples will be aggregated and their probability field will be used to store their observed frequencies of apparition. Defaults to True.
amp_threshold (optional, double) – amplitude threshold under which states are not returned in the result structure. Useful to prune states that are unlikely to be measured out of the returned samples. Defaults to 1/2^40.
- Keyword Arguments
observable (
Observable
) – seeObservable
. Used for the “OBS” mode only).
Displaying circuits
Circuits could be displayed in a notebook or inside
a terminal. To display a circuit in a terminal, you
can use the function qat.core.console.display()
- qat.core.console.display(circuit, max_depth=0, circuit_name=None, batchmode=False, renaming_dict=None)
Displays a circuit on the terminal using ASCII art. By default, calls to sub-circuits are not inlined when displaying. You can display the content of a sub-circuit by increasing the maximal depth of inlining.
from qat.core.console import display from qat.lang.AQASM import Program from qat.lang.AQASM.qftarith import add # Define circuit prog = Program() qbits = prog.qalloc(5) prog.apply(add(3, 2), qbits) circ = prog.to_circ() # Display circuit display(circ) # Display implementation of "add" display(circ, max_depth=1) # Display full circuit display(circ, max_depth=None)
- Parameters
circuit (
Circuit
) – displayed circuitmax_depth (int, optional) – maximal depth of inlining (None is maximal)
circuit_name (str, optional) – name of the circuit
batchmode (bool, optional) – if this variable is set to
True
, this function will not wait for user inputrenaming_dict (optional, dict) – an alias dictionary to display gates with custom names
Warning
Only GATETYPE operators can be visualized in a terminal. An exception is raised if your circuit contains measures, gates controlled classically, logic operators, …
Function plot_in_notebook()
can be used to display
a circuit inside a Jupyter notebook
- qat.core.printer.plot_in_notebook(circuit, fmt=None, depth=0, filename=None, hardware_model=None, renaming_dict=None)
Plots a circuit directly into a notebook cell.
- Parameters
shell – a IPython shell, as returned by get_ipython function
circuit – the circuit to plot
fmt – the format (svg or pdf)
depth – the inlining depth
filename – the filename in which to save a copy of the plot (or None)
hardware_model – some hardware model to display a timeplot of the circuit
renaming_dict – an aliases dictionary to display gates with custom names
The display()
method can be used to display a circuit in
a notebook or in an IPython terminal (this function will detect automatically
if qat.core.console.display()
or plot_in_notebook()
must be used).
Hardware specifications
Hardware specifications are described by a the class
HardwareSpecs
- class qat.core.HardwareSpecs(nbqbits=None, **kwargs)
Simple higher level wrapper for the serializable HardwareSpecs class.
- Instance attributes:
nbqbits (int): number of qubits
topology (
Topology
): topologygateset (
GateSet
): the gate set of the hardwaredescription (str): description of the hardware
processing_types (list<
ProcessingType
>): list of supported processing types
- as_graph(nqbits=None)
Returns a
networkx.Graph
object corresponding to the topology. This function calls the methodto_nx()
of the classTopology
from qat.devices import RIGETTI_ASPEN # Cast the device into a networkx graph nx_graph = RIGETTI_ASPEN.as_graph()
- Returns
graph describing the topology of this hardware specs
- Return type
networkx.Graph
- as_quameleon()
Returns a
QuameleonPlugin
instance enforcing the device’s connectivity for easy inclusion in a QLM stackfrom qat.devices import RIGETTI_ASPEN from qat.qpus import get_default_qpu # Create a stack stack = RIGETTI_ASPEN.as_quameleon() | get_default_qpu() print(stack)
QuameleonPlugin | CLinalg
- Returns
Quameleon plugin
- Return type
- dump(fname)
Dumps the HardwareSpecs inside a file in binary format.
- Parameters
fname (str) – the file name
- classmethod from_thrift(tobject)
Builds a HardwareSpecs object from a thrift object
- static load(fname)
Loads a HardwareSpecs from a file.
- Parameters
fname (str) – the file name
- Returns
- property nbqbits
Number of qubits composing the hardware
- class qat.core.Topology(is_directed=False, nbqbits=None, **kwargs)
Simple higher-level wrapper for the serializable Topology class.
- Parameters
topology (ThriftTopology, optional) – serializable object describing the topology
is_directed (bool, optional) – set the topology directed or undirected (used if your topology is Custom). If your topology is undirected, any qubit of a pair is either a control or a target qubit, otherwise, only one qubit of the pair is the control qubit. The topology is, by default, undirected
type (
TopologyType
, optional) – type of the topology (All to All, LNN or Custom)graph (dict<int, list<int>>, optional) – graph describing the topology (required iff the topology is Custom). Any key of this dictionary corresponds to the index of a control qubit and each linked value corresponds to indexes of qubits which can be target of this control qubit
nbqbits (int, optional) – number of qubits
- add_edge(qb1, qb2)
Update connectivity between qubits. The topology will be set to CUSTOM (if needed). Use this method if a two qubits gate can be applied between qubits of indexes
qb1
andqb2
Note
If your topology is directed, the first argument is the control qubit.
- Parameters
qb1 (int) – index of the first qubit
qb2 (int) – index of the second qubit
- dump(fname)
Dumps the Toplogy inside a file in binary format.
- Parameters
fname (str) – the file name
- classmethod from_bytes(data: bytes)
Builds a topology from raw bytes object
- static from_nx(nx_graph, vmap=None)
Builds a Topology object from a networkx Graph
- Parameters
nx_graph (networkx.Graph) – a networkx Graph
vmap (optional, dict) – if set, the dictionary is update with a map that maps graph nodes to qubit indexes
- classmethod from_thrift(tobject, init_none=False)
Builds a Topology object from a thrift topology object
- static load(fname)
Loads a Topology from a file.
- Parameters
fname (str) – the file name
- Returns
- property nbqbits
Getter of nbqbits of a topology object
- set_nbqbits()
A helper method that set the nbqbits of a custom topology, used by the HardwareSpecs to fix the number of qubits of a custom topology
- to_nx(nqbits=None)
Cast the Topology object into a networkx graph.
- Parameters
nqbits (optional, int) – if specified, the number of qubits
- Returns
a networkx graph
- Return type
nx.Graph
- class qat.core.TopologyType
This class is an enumeration describing the type of a
Topology
. Currently, there are three types of topologies:All To All (ALL_TO_ALL)
This type describes topologies for which two-qubit gates can be applied on any pair of qubits
from qat.core import Topology, TopologyType all_to_all = Topology(type=TopologyType.ALL_TO_ALL)
Linear (LNN)
This type describes topologies for which two-qubit gates can only be applied on qubits having consecutive indexes (i.e. a two-qubit gate can only be applied on the qubits of indexes \(i\) and \(i \pm 1\))
from qat.core import Topology, TopologyType lnn = Topology(type=TopologyType.LNN)
Custom (CUSTOM)
This type describes a custom topology. Objects of type
Topology
having a custom topology type must have a graphfrom qat.core import Topology, TopologyType # Init topology custom = Topology(type=TopologyType.CUSTOM) # Graph definition custom.add_edge(0, 1) custom.add_edge(0, 2)
Gate set and gate signatures
These classes are core classes hidden behind qat.lang.AQASM.gates.AbstractGate
class.
- class qat.core.gate_set.GateSignature(name, arg_types, arity=None, matrix_generator=None, circuit_generator=None)
GateSignatures describe gate constructors. They are defined via a constructor name and a list of argument types.
- Parameters
name (str) – the name of the gate
arg_types (list<type>) – a list of python types describing the types of the arguments. Supported types are: int, float, str, np.matrix, np.ndarray, list.
arity (int, optional) – the arity of the gate is known in advance
matrix_generator (func, optional) – a function that generates the gates matrix when provided with a set of parameters
circuit_generator (func, optional) – a function that generates a
Circuit
implementing the gate when provided with a set of parameters
- get_circuit(*args)
Generates a circuit corresponding to a set of parameters.
- Parameters
*args (list<Any>) – a list of parameters
- get_matrix(*args)
Returns the matrix corresponding to a set of parameters.
- Parameters
*args (list<Any>) – a list of parameters
- set_circuit_generator(gen)
Sets the circuit generator.
- Parameters
gen (fun) – a function that generates a
Circuit
given the appropriate parameters
- set_matrix_generator(gen)
Sets the matrix generator.
- Parameters
gen (fun) – a function that generates a numpy matrix given the appropriate parameters
- class qat.core.gate_set.GateSet(dictio=None)
Gate sets represent collections of
GateSignature
.- Parameters
dictio (dict) – a (str,
GateSignature
) dictionary
- add_signature(signa)
Adds a new
GateSignature
in theGateSet
. If the signature redefines a gate, aGateSyntaxRedefinition
is raised.- Parameters
signa (
GateSignature
) – a gate signature
- class qat.core.gate_set.GateSyntaxRedefinition(name)
Exception raised when a GateSyntax is redefined in a GateSet
Variables and expressions
When defining a variational circuit, pyAQASM provides its own way to declare variables (via the qat.lang.AQASM.Program.new_var()
method).
However, it is possible to turn most parts of a Job into a parametrized quantity using qat.core.Variable
objects.
- class qat.core.Variable(name, var_type=<class 'float'>)
Class for typed variables. Variables can be used in any python arithmetic expression to build an
ArithExpression
object.- Parameters
name (str) – the variable name
var_type (type) – the variable type. Default to float.
Note
For now, the var_type argument is simply ignored.
Warning
Variable with names that can be cast to numerical values are forbidden.
- differentiate(other_var)
Differentiate a variable w.r.t. a variable.
from qat.core import Variable a, b = Variable("a"), Variable("b") print(a.differentiate("a")) print(a.differentiate("b"))
1 0
- Parameters
other_var (str) – a variable name
- Returns
either 0 or 1
- Return type
numbers.Number
- get_variables()
Returns a single-element list containing the name of the variable .
- to_thrift()
Generates a thrift RPN version of the expression. This is used to store the expression in a serializable format.
from qat.core import Variable a, b = Variable("a"), Variable("b") expr = a + b print(expr) print(expr.to_thrift())
(a + b) + a b
- class qat.core.variables.ArithExpression(symbol, *children)
Class to represent arbitrary arithmetic expressions.
This class is not designed to be instantiated by hand. Expressions are constructed by calling a
Symbol
object on one or several variables.Additionally, expressions (and variables) have their numeric operator overloaded (i.e +, -, etc).
from qat.core.variables import cos, Variable a = Variable("a") print(type(cos)) expr = cos(a) print(type(expr))
<class 'qat.core.variables.Symbol'> <class 'qat.core.variables.ArithExpression'>
- Parameters
symbol (
Symbol
) – the symbol labeling the nodechildren (list) – the list of children of the node. These could be variables, expressions, or constants
- differentiate(other_var)
Differentiate an expression w.r.t. to a variable.
from qat.core.variables import cos, Variable a = Variable("a") expr = a * cos(a) print(expr.differentiate("a"))
((a * -(sin(a))) + cos(a))
- Parameters
other_var (str) – a variable name
- Returns
an arithmetic expression
- Return type
- static from_string(thrift_string, var_dic=None)
Builds an arithmetic expression out of a RPN string.
Effectively the inverse of the to_thrift method.
from qat.core.variables import ArithExpression expr = ArithExpression.from_string("+ a b") print(expr)
(a + b)
- Parameters
thrift_string (str) – a RPN string
var_dic (optional, dict) – a dictionary of variables as found in a
Circuit
- get_variables()
Returns the list of variables appearing inside the expression.
- Returns
a set of variable indexes
- Return type
list<int>
- pretty_print(depth=0)
Pretty prints the expression for debugging (mostly)
It is possible to extend the set of symbols supported by this abstract expression mechanics by extending the
list of a symbols with a new qat.core.variables.Symbol
. The only constraint is that the new symbols should have a fixed arity that is known in advance.
- class qat.core.variables.Symbol(token, evaluator, arity, infix=False, pretty_token=None, diff=None)
A simple structure to describe labels in the AST of an arithmetic expression.
- A symbol requires:
a token representing the symbol
a proper python function that implements the symbol for numerical values
an arity (i.e a number of arguments)
For instance, the cos symbol of this module is declared as follows, where the
diff
field can be omitted if one wouldn’t need differentiation:from qat.core.variables import Symbol import numpy as np cos = Symbol("cos", np.cos, 1, diff=lambda x, dx: -sin(x) * dx) print(cos) print(type(cos)) print(cos(np.pi))
cos <class 'qat.core.variables.Symbol'> -1.0
Its __call__ operator is overloaded in order to behave as the underlying function for numeric values and to build an
ArithExpression
when called on abstract expressions or variables.- Parameters
token (str) – the token corresponding to the symbol
evaluator (callable) – the proper function underlying the symbol (i.e the semantic)
arity (int) – the expected arguments of the symbol
infix (bool) – If set to True, the symbol will be displayed in infix format (only allowed when arity=2). Defaults to False.
pretty_token (optional, str) – If set, the pretty token will be used when displaying the symbol.
Defined symbols can be accessed via the ALL_SYMBOLS module variable:
from qat.core.variables import ALL_SYMBOLS
for token, symbol in ALL_SYMBOLS.items():
print(token, symbol.evaluator, symbol.arity)
+ <cyfunction <lambda> at 0x152b42cd1860> 2
* <cyfunction <lambda> at 0x152b42cd1a00> 2
- <cyfunction <lambda> at 0x152b42cd1ba0> 2
UMINUS <cyfunction <lambda> at 0x152b42cd1d40> 1
/ <cyfunction <lambda> at 0x152b42cd1ee0> 2
** <cyfunction <lambda> at 0x152b42cc5110> 2
cos <ufunc 'cos'> 1
sin <ufunc 'sin'> 1
exp <ufunc 'exp'> 1
sqrt <ufunc 'sqrt'> 1
heaviside <cyfunction <lambda> at 0x152b42cc55f0> 3
max <built-in function max> 2
ln <ufunc 'log'> 1
real <function real at 0x152b44981e50> 1
imag <function imag at 0x152b44984040> 1
angle <function angle at 0x152b444ba700> 1
abs <ufunc 'absolute'> 1
atan2 <built-in function atan2> 2
For instance, the following piece of code adds a new symbol that represents the min between two numeric values.
from qat.core.variables import Variable, Symbol, ALL_SYMBOLS
# token will "min"
# underlying function is the standard `min`
# it has arity 2
# it is not an infix operator
my_min = Symbol("min", min, 2, infix=False)
# This step is necessary if we want to be able to bind variables in expressions containing min
ALL_SYMBOLS["min"] = my_min
a, b = Variable("a"), Variable("b")
expr = my_min(a, b)
print(expr)
print(expr(a=3))
print(expr(a=3, b=4))
min(a,b)
min(3,b)
3
Warning
If you create a Symbol in your environment, but choose to send it for evaluation to a remote QLM, the remote environment will not be aware of this new Symbol and the expression evaluation might fail.
Various methods and tools
- qat.core.formula_eval.evaluate(formula_str, tab)
Evaluate a logical formula stored in prefix format inside a string.
- Parameters
formula_str (str) – a string
tab (list<bool>) – a list of values of the cbits
- Returns
bool
- qat.core.circuit_builder.matrix_util.get_predef_generator()
Returns a map of all the matrices of constant gates in the default gate set
- qat.core.circuit_builder.matrix_util.get_param_generator()
Returns a map containing the 4 default parametrized gates generators
- qat.core.circuit_builder.matrix_util.default_gate_set()
Generates a fresh default gate set
Plugins & Junctions
- class qat.plugins.Junction(collective=False)
Junctions are elements of a stack that allow for iterations inside a QLM stack. They implement the standard QPU interface (submit, submit_job, etc), but can be seen as some kind of iterative plugin that delegates execution of jobs to the lower part of the stack.
Junctions can be composed via the pipe operator to be included saemlessly into QLM stacks.
- Parameters
collective (bool, optional) – if set to True, the .run method will receive the full batch. If set to False, .run will receive the jobs one by one. Defaults to False.
- execute(qlm_object)
Executes a new Job or Batch back using the lower part of the stack.
- abstract run(initial_object, meta_data)
This function will be called upon the reception of a fresh abstract QLM object (e.g a Job or a Batch depending the Junction configuration)
Warning
This is an abstract method with no default implementation. You should build your own junction by inheriting from this class and implementing your own .run method.
- Parameters
qlm_object (
Job
orBatch
) – the initial abstract QLM object. The type depends on the constructor parameter collective: if set to True, a Batch will be provided. If False, a Job.meta_data (dict) – the meta data of the initial batch. This is helpful to transmit arguments from the client to the Junction
- Returns
this method should return a Result or BatchResult depending on the collective constructor parameter
- Return type
- class qat.plugins.Optimizer(collective=False, **binding_kwargs)
A particulized Junction that provides a higher level, QLM independant interface to perform variational optimization.
Instead of providing a .execute method, it provides a .evaluate method that takes a map of <variablename, value> and returns a float.
The administrative burden on binding the variables of the abstract job and fetching the result’s value is done by the class itself, under the hood.
- Parameters
collective (optional, bool) – see
Junction
class constructor arguments**binding_kwargs (dict) – a collection of arguments passed to the variable binding method of the job. E.g gate set, matrix generation parameters, etc.
- evaluate(values)
Bind variables into the abstract job/batch, execute the job/batch and return the “value” field of the returned Result object or the sum of the result values if a BatchResult is returned.
- Parameters
values (dict) – a dictionary of values (str, float)
- Returns
a floating point number or a complex, depending on the structure of the Job
- Return type
float or complex
- abstract optimize(var_names)
This function will be called upon the reception of a fresh abstract QLM object (e.g a Job or a Batch depending the Junction configuration).
Upon reception of a fresh QLM object (Job, Batch), the object is stored in the self.job attribute, and this method is called with as single argument the list of all the variables appearing inside the object.
Warning
This is an abstract method with no default implementation. You should build your own optimizer by inheriting from this class and implementing your own .optimize method.
- Parameters
var_names (list) – the list of variables to optimize upon
- Returns
this method should return a triplet of values with:
the optimal value found (this will correspond to the .value field in the final result object)
the optimal values of the parameters (these will be stored in the meta_data using the “parameters” key in the final result object)
any additional information (these will be also stored in the meta_data field using the “optimizer_data” key)
- Return type
(float, list/np.ndarray, object)