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

(BatchResult)

abstract submit_job(job: Job) Result

Returns a Result structure corresponding to the execution of a Job.

Note

Notice that the job can be assumed to be already compiled/optimized.

This is the end of the pipe!

Parameters

job (Job) – the job to execute

Returns

a result

Return type

(Result)

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:

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
execute(*args, **kwargs)

Executes the application (generates batches and executes them)

Arguments given to this method are passed to the generate method of the generator

Note

The first argument passed to generate is the HardwareSpecs 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 execute

  • schedule (Schedule): schedule to execute

  • type (ProcessingType): type of Job

  • observable (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

Job

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:

class qat.comm.shared.ttypes.ProcessingType

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 batch

  • meta_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.

static load(fname)

Loads a Batch from a file.

Parameters

fname (str) – the file name

Returns

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:

An Observable object can be constructed in various ways:

using a matrix representation:

using a list of Term:

or using an Ising object from the Ising:

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 problem

  • constant_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>

ising

represents an Ising encoded problem (present if terms is None)

Type

Ising, optional

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.

  1. Building schedules

  2. Manipulating schedules

  3. Analog Jobs

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 the to_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

Schedule

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:

\[H(t) = (1 - t) \sigma_z^{(0)} + t \sigma_x^{(0)}\]
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 requested

  • value (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

Result

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 state

  • probability (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 measurements

  • err (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

class qat.comm.shared.ttypes.IntermediateMeasurement(cbits=None, gate_pos=None, probability=None)

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 results

  • meta_data (dict<str, str>): meta data

append(value)

Add a result in the list of results

Parameters

value (Result) –

dump(fname)

Dumps the BatchResult inside a file in binary format.

Parameters

fname (str) – the file name

extend(values)

Add several results in the list of results

Parameters

values (list<Result>) –

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

BatchResult

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.

Parameters
  • v_name (str) – the variable name

  • v_value (any) – the value

  • gate_set (optional, GateSet) – a gate set to use to generate the matrices for freshly bound gates

Returns

a quantum circuit

Return type

Circuit

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

Circuit

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.

Example

By 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:

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

Circuit

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

Circuit

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) – see Observable. 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 circuit

  • max_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 input

  • renaming_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): topology

  • gateset (GateSet): the gate set of the hardware

  • description (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 method to_nx() of the class Topology

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 stack

from 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

QuameleonPlugin

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

HardwareSpecs

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 and qb2

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

Topology

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 graph

from 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 the GateSet. If the signature redefines a gate, a GateSyntaxRedefinition is raised.

Parameters

signa (GateSignature) – a gate signature

union(new_gate_set)

Compute the union of the gate set with another gate set

Parameters

new_gate_set (GateSet) – another gate set

Returns

the union of both gate sets

Return type

GateSet

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 node

  • children (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

ArithExpression

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)

to_thrift()

Generates a thrift RPN version of the expression.

See Variable method with the same name.

Returns

a string representation

Return type

str

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.

Parameters

qlm_object (Job or Batch) – the qlm object to transmit down the stack

Returns

a Result structure corresponding to the transmitted Job or Batch

Return type

BatchResult or Result

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 or Batch) – 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

Result, BatchResult

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)