Measurements
from mpqp.measures import *
A measurement can be used to retrieve information about your quantum state. In
mpqp
, you can add it to a circuit as any other instruction (either when
initializing the circuit, or using the
add
circuit method). All kind of
measurements are listed bellow, check them out for usage example.
However, if no measurement is added to the circuit before running it, the user
will retrieve the state as a
StateVector
in the computational
basis (see section Results).
Note
In the current version, we only allow one measurement per circuit.
The measurement
Measure
is the
base class for measurements. It regroups all the attributes and methods
common to all types of measurements we support.
A measurement can be of two types:
BasisMeasure
or
ExpectationMeasure
,
described in details bellow.
- class Measure(targets, shots=0, label=None)[source]
Bases:
Instruction
,ABC
Abstract class representing the measurement of the quantum state generated by a quantum circuit.
This class is used to regroup attributes and methods shared by all different types of measures.
We distinguish two types of measures:
Basis measurement (measure some qubits in a specific basis, sample mode, or retrieve the StateVector when shots is equal to zero)
Expectation value (use of an observable, exact or sample mode)
- Parameters
targets (list[int]) – List of indices referring to the qubits on which the measure will be applied.
shots (int) – Number of times the circuit should be run, each of these times is called a shot.
label (Optional[str]) – Label used to identify the measure.
- label
See parameter description.
- shots
See parameter description.
Measuring in a basis
Choice of the basis
When we measure a quantum state, we project it in an orthonormal basis of the
associated Hilbert space. By default,
BasisMeasure
operates in the computational basis, but you may want to measure the state in a
custom basis, like it can be the case in the Bell game. For this purpose, you
can use the Basis
class.
On the other hand, some common basis are available for you to use:
ComputationalBasis
and HadamardBasis
.
- class Basis(basis_vectors, nb_qubits=None)[source]
Bases:
object
Represents a basis of the Hilbert space used for measuring a qubit.
- Parameters
basis_vectors (list[npt.NDArray[np.complex64]]) – List of vector composing the basis.
nb_qubits (Optional[int]) – Number of qubits associated with this basis. If not specified, it will be automatically inferred from
basis_vectors
’s dimensions.
Example
>>> Basis([np.array([1,0]), np.array([0,-1])]).pretty_print() Basis: [ [1, 0], [0, -1] ]
- pretty_print()[source]
Nicer print for the basis, with human readable formatting.
Example
>>> Basis([np.array([1,0]), np.array([0,-1])]).pretty_print() Basis: [ [1, 0], [0, -1] ]
- basis_vectors
See parameter description.
- nb_qubits
See parameter description.
- class ComputationalBasis(nb_qubits=None)[source]
Bases:
VariableSizeBasis
Basis representing the computational basis, also called Z-basis or canonical basis.
- Parameters
nb_qubits (Optional[int]) – number of qubits of the space, if not given as input (for example if unknown at the moment of creation)
set_size
will have to be executed before the basis is used (in a measure for example).
Examples
>>> ComputationalBasis(3).pretty_print() Basis: [ [1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1] ] >>> b = ComputationalBasis() >>> b.set_size(2) >>> b.pretty_print() Basis: [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ]
- set_size(nb_qubits)[source]
To allow the user to use a basis without having to specify the size (because implicitly the size should be the number of qubits of the circuit), we use this method, that will only be called once the circuit’s size is definitive (i.e. at the last moment before the circuit is ran)
- Parameters
nb_qubits (int) – number of qubits in the basis
- class HadamardBasis(nb_qubits=None)[source]
Bases:
VariableSizeBasis
Basis representing the Hadamard basis, also called X-basis or +/- basis.
- Parameters
nb_qubits (Optional[int]) – number of qubits in the basis
Example
>>> HadamardBasis(2).pretty_print() Basis: [ [0.5, 0.5, 0.5, 0.5], [0.5, -0.5, 0.5, -0.5], [0.5, 0.5, -0.5, -0.5], [0.5, -0.5, -0.5, 0.5] ]
- set_size(nb_qubits)[source]
To allow the user to use a basis without having to specify the size (because implicitly the size should be the number of qubits of the circuit), we use this method, that will only be called once the circuit’s size is definitive (i.e. at the last moment before the circuit is ran)
- Parameters
nb_qubits (int) – number of qubits in the basis
The BasisMeasure
The BasisMeasure
is used to project the state on a statevector of a
given Basis
and returns the corresponding eigenvalue.
- class BasisMeasure(targets, c_targets=None, shots=0, basis=None, label=None)[source]
Bases:
Measure
Class representing a measure of one or several qubits in a specific basis.
By default, the computational basis will be used. The user can also precise a specific basis using the class Basis.
The number of shots indicates the number of time the measure is repeated. When shots is equal to 0 (by default), the simulator is used to produce exact value of the amplitudes/probabilities.
- Parameters
targets (list[int]) – List of indices referring to the qubits on which the measure will be applied.
c_targets (Optional[list[int]]) – List of indices referring to the classical bits on which the measure will be applied.
shots (int) – Number of shots to be performed basis: basis in which the qubits should be measured.
basis (Optional[Basis]) – Basis in which the measure is performed. Defaults to
ComputationalBasis()
label (Optional[str]) – Label used to identify the measure.
Examples
>>> c1 = QCircuit([H(0), H(1), CNOT(0,1)]) >>> c1.add(BasisMeasure([0, 1], shots=1024)) >>> c2 = QCircuit([H(0), H(1), CNOT(0,1)]) >>> c2.add(BasisMeasure([0, 1], shots=1024, basis=HadamardBasis()))
- to_other_language(language=Language.QISKIT, qiskit_parameters=None)[source]
Transforms this instruction into the corresponding object in the language specified in the
language
arg.By default, the instruction is translated to the corresponding one in Qiskit, since it is the interface we use to generate the OpenQASM code.
In the future, we will generate the OpenQASM code on our own, and this method will be used only for complex objects that are not tractable by OpenQASM (like hybrid structures).
- Parameters
language (Language) – Enum representing the target language.
qiskit_parameters (Optional[set['Parameter']]) – We need to keep track of the parameters passed to qiskit in order not to define twice the same parameter. Defaults to
set()
.
- Returns
The corresponding instruction (gate or measure) in the target language.
- basis
See parameter description.
- c_targets
See parameter description.
Measuring using an observable
Information about the state can be retrieved using the expectation value of
this state measured by an observable. This is done using the Observable
class to define your observable, and a ExpectationMeasure
to perform
the measure.
- class ExpectationMeasure(targets, observable, shots=0, label=None)[source]
Bases:
Measure
This measure evaluates the expectation value of the output of the circuit measured by the observable given as input.
If the
targets
are not sorted and contiguous, some additional swaps will be needed. This will affect the performance of your circuit if run on noisy hardware. The swaps added can be checked out in thepre_measure
attribute of theExpectationMeasure
.- Parameters
targets (list[int]) – List of indices referring to the qubits on which the measure will be applied.
observable (Observable) – Observable used for the measure.
shots (int) – Number of shots to be performed.
label (Optional[str]) – Label used to identify the measure.
Example
>>> obs = Observable(np.diag([0.7, -1, 1, 1])) >>> c = QCircuit([H(0), CNOT(0,1), ExpectationMeasure([0,1], observable=obs, shots=10000)]) >>> run(c, ATOSDevice.MYQLM_PYLINALG).expectation_value 0.85918
- Warns
UserWarning – If the
targets
are not sorted and contiguous, some additional swaps will be needed. This will change the performance of your circuit is run on noisy hardware.- Parameters
targets (list[int]) –
observable (Observable) –
shots (int) –
label (Optional[str]) –
- to_other_language(language=Language.QISKIT, qiskit_parameters=None)[source]
Transforms this instruction into the corresponding object in the language specified in the
language
arg.By default, the instruction is translated to the corresponding one in Qiskit, since it is the interface we use to generate the OpenQASM code.
In the future, we will generate the OpenQASM code on our own, and this method will be used only for complex objects that are not tractable by OpenQASM (like hybrid structures).
- Parameters
language (Language) – Enum representing the target language.
qiskit_parameters (Optional[set['Parameter']]) – We need to keep track of the parameters passed to qiskit in order not to define twice the same parameter. Defaults to
set()
.
- Returns
The corresponding instruction (gate or measure) in the target language.
- Return type
None
- observable
See parameter description.
- pre_measure
Circuit added before the expectation measurement to correctly swap target qubits when their are note ordered or contiguous.
- rearranged_targets
Adjusted list of target qubits when they are not initially sorted and contiguous.
- class Observable(observable)[source]
Bases:
object
Class defining an observable, used for evaluating expectation values.
An observable can be defined by using a Hermitian matrix, or using a combination of operators in a specific basis Pauli.
- Parameters
observable (Matrix | PauliString) – can be either a Hermitian matrix representing the observable or PauliString representing the observable.
- Raises
ValueError – If the input matrix is not Hermitian or does not have a square shape.
NumberQubitsError – If the number of qubits in the input observable does not match the number of target qubits.
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> matrix = np.array([[1, 0], [0, -1]]) >>> ps = 3 * I @ Z + 4 * X @ Y >>> obs = Observable(matrix) >>> obs2 = Observable(ps)
- to_other_language(language, circuit=None)[source]
Converts the observable to the representation of another quantum programming language.
- Parameters
language (Language) – The target programming language.
circuit (Optional[Cirq_Circuit]) – The Cirq circuit associated with the observable (required for
cirq
).
- Returns
Depends on the target language.
- Return type
Operator | QLMObservable | Hermitian | CirqPauliSum | CirqPauliString
Example
>>> obs = Observable(np.diag([0.7, -1, 1, 1])) >>> obs_qiskit = obs.to_other_language(Language.QISKIT) >>> print(obs_qiskit) Operator([[ 0.69999999+0.j, 0. +0.j, 0. +0.j, 0. +0.j], [ 0. +0.j, -1. +0.j, 0. +0.j, 0. +0.j], [ 0. +0.j, 0. +0.j, 1. +0.j, 0. +0.j], [ 0. +0.j, 0. +0.j, 0. +0.j, 1. +0.j]], input_dims=(2, 2), output_dims=(2, 2))
- property matrix: Matrix
The matrix representation of the observable.
- nb_qubits
Number of qubits of this observable.
- property pauli_string: PauliString
The PauliString representation of the observable.
Pauli String
Represents Pauli strings, which is linear combinations of
PauliMonomial
which is a combination of PauliAtom
.
PauliString
objects can be added, subtracted, and multiplied by
scalars. They also support matrix multiplication with other PauliString
objects.
- class PauliString(monomials=None)[source]
Bases:
object
Represents a Pauli string, a linear combination of Pauli monomials.
- Parameters
monomials (Optional[list['PauliStringMonomial']]) – List of Pauli monomials.
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> I @ Z + 2 * Y @ I + X @ Z 1*I@Z + 2*Y@I + 1*X@Z
Note
Pauli atoms are named
I
,X
,Y
, andZ
. If you have conflicts with the gates of the same name, you could:Rename the Pauli atoms:
from mpqp.measures import X as Pauli_X, Y as Pauli_Y ps = Pauli_X + Pauli_Y/2
Import the Pauli atoms directly from the module:
from mpqp.measures import pauli_string ps = pauli_string.X + pauli_string.Y/2
- classmethod from_matrix(matrix)[source]
Constructs a PauliString from a matrix.
- Parameters
matrix (Matrix) – Matrix from which the PauliString is generated
- Returns
Pauli string decomposition of the matrix in parameter.
- Return type
- Raises
ValueError – If the input matrix is not square or its dimensions are not a power of 2.
Example
>>> ps = PauliString.from_matrix(np.array([[0, 1], [1, 2]])) >>> print(ps) 1*I + 1*X + -1*Z
- round(round_off_till=4)[source]
Rounds the coefficients of the PauliString to a specified number of decimal places.
- Parameters
round_off_till (int) – Number of decimal places to round the coefficients to. Defaults to 4.
- Returns
- A PauliString with coefficients rounded to the specified number
of decimal places.
- Return type
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> ps = 0.6875*I@I + 0.1275*I@Z >>> rounded_ps = ps.round(1) >>> print(rounded_ps) 0.7*I@I + 0.1*I@Z
- simplify(inplace=False)[source]
Simplifies the PauliString by combining identical terms and removing terms with null coefficients.
- Parameters
inplace (bool) – Indicates if
simplify
should update self.- Returns
A simplified version of the PauliString.
- Return type
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> ps = I@I - 2 *I@I + Z@I - Z@I >>> simplified_ps = ps.simplify() >>> print(simplified_ps) -1*I@I
- sort_monomials()[source]
Sorts the monomials of the PauliString based on their coefficients.
- Returns
- A new PauliString object with monomials sorted based on their coefficients,
in descending order.
- Return type
- to_dict()[source]
Converts the PauliString object to a dictionary representation.
- Returns
Dictionary representation of the PauliString object.
- Return type
dict[str, float]
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> ps = 1 * I@Z + 2 * I@I >>> print(ps.to_dict()) {'II': 2, 'IZ': 1}
- to_matrix()[source]
Converts the PauliString to a matrix representation.
- Returns
Matrix representation of the PauliString.
- Return type
Matrix
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> ps = I + Z >>> matrix_representation = ps.to_matrix() >>> print(matrix_representation) [[2.+0.j 0.+0.j] [0.+0.j 0.+0.j]]
- property monomials: list[mpqp.core.instruction.measurement.pauli_string.PauliStringMonomial]
Gets the monomials of the PauliString.
- Returns
The list of monomials in the PauliString.
- property nb_qubits: int
Gets the number of qubits associated with the PauliString.
- Returns
The number of qubits associated with the PauliString.
- class PauliStringAtom(label, matrix)[source]
Bases:
PauliStringMonomial
Represents a single Pauli operator acting on a qubit in a Pauli string.
- Parameters
label (str) – The label representing the Pauli operator.
matrix (npt.NDArray[np.complex64]) – The matrix representation of the Pauli operator.
- Raises
RuntimeError – New atoms cannot be created, you should use the available ones.
Note
All the atoms are already initialized. Available atoms are (
I
,X
,Y
,Z
).- to_matrix()[source]
Converts the PauliString to a matrix representation.
- Returns
Matrix representation of the PauliString.
- Return type
ndarray[Any, dtype[complex64]]
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> ps = I + Z >>> matrix_representation = ps.to_matrix() >>> print(matrix_representation) [[2.+0.j 0.+0.j] [0.+0.j 0.+0.j]]
- property atoms
- property coef
- property monomials
Gets the monomials of the PauliString.
- Returns
The list of monomials in the PauliString.
- property nb_qubits: int
Gets the number of qubits associated with the PauliString.
- Returns
The number of qubits associated with the PauliString.
- class PauliStringMonomial(coef=1, atoms=None)[source]
Bases:
PauliString
Represents a monomial in a Pauli string, consisting of a coefficient and a list of PauliStringAtom objects.
- Parameters
coef (Real | float) – The coefficient of the monomial.
atoms (Optional[list['PauliStringAtom']]) – The list of PauliStringAtom objects forming the monomial.
- simplify(inplace=False)[source]
Simplifies the PauliString by combining identical terms and removing terms with null coefficients.
- Parameters
inplace (bool) – Indicates if
simplify
should update self.- Returns
A simplified version of the PauliString.
- Return type
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> ps = I@I - 2 *I@I + Z@I - Z@I >>> simplified_ps = ps.simplify() >>> print(simplified_ps) -1*I@I
- to_matrix()[source]
Converts the PauliString to a matrix representation.
- Returns
Matrix representation of the PauliString.
- Return type
Matrix
Example
>>> from mpqp.core.instruction.measurement.pauli_string import I, X, Y, Z >>> ps = I + Z >>> matrix_representation = ps.to_matrix() >>> print(matrix_representation) [[2.+0.j 0.+0.j] [0.+0.j 0.+0.j]]
- property monomials: list['PauliStringMonomial']
Gets the monomials of the PauliString.
- Returns
The list of monomials in the PauliString.
- property nb_qubits: int
Gets the number of qubits associated with the PauliString.
- Returns
The number of qubits associated with the PauliString.
- I = I
Pauli-I atom representing the identity operator in a Pauli monomial or string. Matrix representation: \(\begin{pmatrix}1&0\\0&1\end{pmatrix}\)
- X = X
Pauli-X atom representing the X operator in a Pauli monomial or string. Matrix representation: \(\begin{pmatrix}0&1\\1&0\end{pmatrix}\)
- Y = Y
Pauli-Y atom representing the Y operator in a Pauli monomial or string. Matrix representation: \(\begin{pmatrix}0&-i\\i&0\end{pmatrix}\)
- Z = Z
Pauli-Z atom representing the Z operator in a Pauli monomial or string. Matrix representation: \(\begin{pmatrix}1&0\\0&-1\end{pmatrix}\)