qat.pylinalg: Python Linear-algebra simulator

myQLM comes with one simulator which performs a unitary evolution of the initial quantum state using the operations described in a quantum circuit. This simulator is called “Python Linear-Algebra”.

It is entirely written in Python, and is based in particular on the Numpy library.

The quantum state is stored as an ndarray of shape \(\left(2,...,2\right)\), with one 2-valued index per qubit:

\[\vert\psi\rangle = \sum_{i_{1},\dots,i_{N}\in \{0,1\}^{N}} a_{i_{1} \dots i_{N}}|i_{1} \dots i_{N}\rangle\]

where \(|i_{1}\dots i_{N}\rangle\) represents a computational basis state.

Quantum gates are also manipulated as ndarrays, with \(2\times \text{arity}\) 2-valued indices. Half of the indices are input indices and the other half output indices.

Applying a gate consists in contracting the input indices with the indices corresponding to the qubits on which the gate is applied:

../images/tensordot.png

The main point of using ndarrays is that this operation can be easily written with the np.tensordot function. See source code documentation for more details.

miscellanous remarks about the simulator:

  • it accepts any gate, of any arity.

  • it works with the entire amplitude vector. Any information is available.

  • it is memory and run-time exponential in the number of qubits. This implies a hard simulation limit at around 20-30 qubits, depending on your RAM.

Within myQLM, this simulator is contained in the qat.pylinalg module.

Note

The full QLM appliance also includes ‘’noisy’’ simulators, which take into account realistic, parametrable physical noise into the execution of a quantum circuit.

The qat.pylinalg module contains a linear-algebra-based quantum simulator. This simulator encodes the quantum amplitudes in a vector with \(2^n\) complex numbers, where \(n\) is the number of qubits. This vector is modified by the application of quantum gates.

Quantum Processing Unit

This is the high-level class wrapping the simulator. It follows the convention of the qat.core.qpu.QPUHandler structure.

class qat.pylinalg.PyLinalg

Simple linalg simulator plugin.

Inherits serve() and submit() method from qat.core.qpu.QPUHandler Only the submit_job() method is simulator-specific and defined here.

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: qat.core.wrappers.batch.Batch, meta_data: Optional[dict] = None)qat.core.wrappers.result.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)

submit_job(job)

Returns a Result structure corresponding to the execution of a Job

Parameters

job (Job) – the job to execute

Returns

the result

Return type

Result

Note

The submit_job() method above basically consists of two imbricated if statements.

The first one looks at the type attribute of the job, which can take two values:

  • OBSERVABLE (cf. ProcessingType): the job consists in evaluating an observable at the end of the circuit. Currently, the attribute nbshots has no effect if the type is OBSERVABLE

  • SAMPLING (cf. ProcessingType): the job consists in sampling the output probability distribution of the quantum circuit. This is where the second if loop comes in, depending on the number of shots which is asked (job.nbshots):

    • if nbshots=0 then the simulator/quantum-processor returns the best it can do. In our case, of a linear-algebra-based simulator, this is the entire probability distribution.

    • else, the simulator samples the output probability distribution nbshots times.

Simulator Internal Functions

The actual numpy-based simulation code can be accessed in the qat.pylinalg.simulator module, whose source code is documented here.

qat.pylinalg.simulator.simulate(circuit)

Computes state vector at the output of provided circuit.

State vector is stored as a numpy.ndarray It is initialized at \(|0^n\rangle\). Then, loop over gates, updating the state vector using np.tensordot

Parameters

circuit (Circuit) – Input circuit. The circuit to simulate.

Returns

a tuple composed of a state vector and intermediate measurements:

  • state vector: numpy.ndarray containing the final state vector. It has one 2-valued index per qubits.

  • intermediate measurements: list of qat.comm.shared.ttypes.IntermediateMeasurement. List containing descriptors of the intermediate measurements that occurred within the circuit, so that the classical branching is known to the user.

Return type

tuple

Note

qat.pylinalg.simulator.simulate() mainly consists in a for loop over the ops attribute of the input Circuit, i.e the gates of the circuit. They are successively applied onto the quantum state, which starts in \(|0...0\rangle\). A gate can be of several types:

  • GATETYPE (default)

  • MEASURE (measure some qubits, store the result)

  • RESET (measure a qubit, if result is 1, apply X onto it)

  • CLASSIC (perform classical logical computation on classical bits)

  • CLASSICCTRL (perform classically controlled quantum gate)

  • BREAK (raise break exception if a clasical formula is evaluated to True)

circuit.ops is a list of qat.comm.datamodel.ttypes.Op.

qat.pylinalg.simulator.measure(state_vec, qubits, nb_samples=1)

Samples measurement results on the specified qubits.

No projection is carried out ! See “project” function. Thanks to the absence of projection, several samples can be asked.

Parameters
  • state_vec (numpy.ndarray) – the numpy.ndarray containing full state vector.

  • qubits (list) – list of integers specifying the subset of qubits to measure.

  • nb_samples (int, optional) – the number of samples to return. Set to 1 by default.

Returns

intprob_list, a list (of length nb_samples) containing tuples of the form (integer, probability). The integer is the result of the measurement on the subset of qubits (when converted to binary representation, it needs to have a width of len(qubits)). The probability is the probability the measurement had to occur. It is useful for renormalizing afterwards. In short: it is a list of samples. One sample is a (int, prob) tuple.

Return type

list

qat.pylinalg.simulator.project(state_vec, qubits, intprob)

Projects the state by assigning qubits to specified values.

The “measure” function does not project. This is nice when asking for several samples. But the full behavior of a quantum state when undergoing measurement includes a projection onto the result state. This is what this function does. In practice, it is used for intermediary measurements. (i.e within measure and reset gates)

Parameters
  • state_vec (numpy.ndarray) – The state vector to project, i.e the one from which the results were sampled.

  • qubits (list) – The qubits that were measured, presented as a list of integers. Without this info, we don’t know to what axes the result corresponds.

  • intprob (tuple) – a tuple of the form (integer, probability). The integer codes for the value that was measured on the qubits in the list “qubits”. The probability that the measurement had to occur. It is useful for renormalizing without having to recompute a norm.

Returns

The projected state vector. The values of the qubits in the “qubits” list have been assigned to the measured values.

Return type

numpy.ndarray

qat.pylinalg.simulator.reset(state_vec, qubits)

Resets the value of the specified qubits to 0.

It works by measuring each qubit, and then applying an X gate if the result is 1.

for one qubit, entirely equivalent to, in AQASM:

MEAS q[k] c[k]
?c[k] : X q[k]
Parameters
  • state_vec (numpy.ndarray) – nd-array containing the full state vector.

  • qubits (list) – list of integers, containing the qubits to reset.

Returns

a tuple composed of:

  • state_vec(numpy.ndarray) the full state vector. the specified qubits have been reset.

  • an integer: result of the measurement on the subset of qubits (when converted to binary representation, it needs to have a width of len(qubits)).

  • a float: probability the measurement had to occur.

Return type

(state_vec, int, prob)

qat.pylinalg.simulator.raise_break(op, op_pos, cbits)

Raises break exception, as a result of a boolean classical formula being evaluated to True.

qat.pylinalg.simulator.mat2nparray(matrix)

Converts serialized matrix format into numpy array.

When extracted from the quantum circuit, gate matrices are not directly numpy arrays. They are instances of Matrix, an internally-defined structure.

Parameters

matrix (qat.comm.datamodel.ttypes.Matrix) – The matrix, as extracted from circuit operation, to convert to numpy.ndarray

Returns

a numpy.ndarray of shape (2*arity,2*arity) containing the matrix data.

Return type

numpy.ndarray