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=None, 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 (Optional[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, symbols=None, basis_vectors_labels=None)[source]

Bases: object

Represents a basis of the Hilbert space used for measuring a qubit.

Parameters
  • basis_vectors (list[npt.NDArray[np.complex128]]) – 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.

  • symbols (Optional[tuple[str, str]]) –

  • basis_vectors_labels (Optional[list[str]]) –

Example

>>> custom_basis = Basis([np.array([1,0]), np.array([0,-1])], symbols=("↑", "↓"))
>>> custom_basis.pretty_print()
Basis: [
    [1, 0],
    [0, -1]
]
>>> circ = QCircuit([X(0), H(0)])
>>> circ.add(BasisMeasure([0], basis=custom_basis, shots=10000))
>>> print(run(circ, IBMDevice.AER_SIMULATOR)) 
Result: IBMDevice, AER_SIMULATOR
 Counts: [5035, 4965]
 Probabilities: [0.5035, 0.4965]
 Samples:
  State: ↑, Index: 0, Count: 5035, Probability: 0.5035
  State: ↓, Index: 1, Count: 4965, Probability: 0.4965
 Error: None
binary_to_custom(state)[source]

Converts a binary string to a custom string representation. By default, it uses “0” and “1” but can be customized based on the provided \(symbols\).

Parameters

state (str) – The binary string (e.g., “01”) to be converted.

Returns

The custom string representation of the binary state.

Return type

str

Example

>>> basis = Basis([np.array([1,0]), np.array([0,-1])], symbols=("+", "-"))
>>> custom_state = basis.binary_to_custom("01")
>>> custom_state
'+-'
pretty_print()[source]

Nicer print for the basis, with human readable formatting.

to_computational()[source]

Converts the custom basis to the computational basis.

This method creates a quantum circuit with a custom gate represented by a unitary transformation and applies it to all qubits before measurement.

Returns

A quantum circuit representing the basis change circuit.

Return type

QCircuit

Example

>>> basis = Basis([np.array([1, 0]), np.array([0, -1])])
>>> circuit = basis.to_computational()
>>> print(circuit)
   ┌─────────┐
q: ┤ Unitary ├
   └─────────┘
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 to measure, if not specified, the size will be dynamic and automatically span across the whole circuit, even through dimension change of the circuit.

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

to_computational()[source]

Converts the custom basis to the computational basis.

This method creates a quantum circuit with a custom gate represented by a unitary transformation and applies it to all qubits before measurement.

Returns

A quantum circuit representing the basis change circuit.

Return type

QCircuit

Example

>>> basis = Basis([np.array([1, 0]), np.array([0, -1])])
>>> circuit = basis.to_computational()
>>> print(circuit)
   ┌─────────┐
q: ┤ Unitary ├
   └─────────┘
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 to measure, if not specified, the size will be dynamic and automatically span across the whole circuit, even through dimension change of the circuit.

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]
]
>>> circ = QCircuit([X(0), H(1), CNOT(1, 2), Y(2)])
>>> circ.add(BasisMeasure(basis=HadamardBasis()))
>>> print(run(circ, IBMDevice.AER_SIMULATOR)) 
Result: None, IBMDevice, AER_SIMULATOR
 Counts: [0, 261, 253, 0, 0, 244, 266, 0]
 Probabilities: [0, 0.25488, 0.24707, 0, 0, 0.23828, 0.25977, 0]
 Samples:
  State: ++-, Index: 1, Count: 261, Probability: 0.2548828
  State: +-+, Index: 2, Count: 253, Probability: 0.2470703
  State: -+-, Index: 5, Count: 244, Probability: 0.2382812
  State: --+, Index: 6, Count: 266, Probability: 0.2597656
 Error: None
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

to_computational()[source]

Converts the custom basis to the computational basis.

This method creates a quantum circuit with a custom gate represented by a unitary transformation and applies it to all qubits before measurement.

Returns

A quantum circuit representing the basis change circuit.

Return type

QCircuit

Example

>>> basis = Basis([np.array([1, 0]), np.array([0, -1])])
>>> circuit = basis.to_computational()
>>> print(circuit)
   ┌─────────┐
q: ┤ Unitary ├
   └─────────┘
class VariableSizeBasis(basis_vectors, nb_qubits=None, symbols=None)[source]

Bases: Basis

A variable-size basis with a dynamically adjustable size to different qubit numbers during circuit execution.

Parameters
  • nb_qubits (Optional[int]) – number of qubits in the basis. If not provided, the basis can be dynamically sized later using the \(set_size\) method.

  • symbols (Optional[tuple[str, str]]) – custom symbols for representing basis states, defaults to (“0”, “1”).

  • basis_vectors (list[npt.NDArray[np.complex128]]) –

Example

>>> custom_basis = VariableSizeBasis([np.array([1,0]), np.array([0,-1])], symbols=("↑", "↓"))
>>> custom_basis.pretty_print()
Basis: [
    [1, 0],
    [0, -1]
]
>>> circ = QCircuit([X(0), H(1), CNOT(1, 2), Y(2)])
>>> circ.add(BasisMeasure(basis=custom_basis, shots=10000))
>>> print(run(circ, IBMDevice.AER_SIMULATOR)) 
Result: None, IBMDevice, AER_SIMULATOR
 Counts: [0, 0, 0, 0, 0, 4936, 5064, 0]
 Probabilities: [0, 0, 0, 0, 0, 0.4936, 0.5064, 0]
 Samples:
  State: ↓↑↓, Index: 5, Count: 4936, Probability: 0.4936
  State: ↓↓↑, Index: 6, Count: 5064, Probability: 0.5064
 Error: None
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=None, c_targets=None, shots=1024, 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. If you don’t specify a target, the operation will apply to all qubits.

Parameters
  • targets (Optional[list[int]]) – List of indices referring to the qubits on which the measure will be applied. Defaults to the entire circuit for VariableSizeBasis and the first qubits matching the size of the basis for other basis.

  • 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), BasisMeasure()])
>>> c2 = QCircuit([
...     H(0),
...     H(2),
...     CNOT(0,1),
...     BasisMeasure([0, 1], shots=512, basis=HadamardBasis())
... ])
to_dict()[source]
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.

property pre_measure: QCircuit

Measuring using an observable

Information about the state can be retrieved using the expectation value of this state measured by one or several observables. This is done using the Observable class to define your observable, and a ExpectationMeasure to perform the measure.

class ExpectationMeasure(observable, targets=None, shots=0, commuting_type=CommutingTypes.QUBITWISE, grouping_method=GroupingMethods.GREEDY, label=None, optimize_measurement=True, optim_diagonal=False)[source]

Bases: Measure

This measure evaluates the expectation value of the output of the circuit measured by the observable(s) 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 the pre_measure attribute.

Parameters
  • targets (Optional[list[int]]) – List of indices referring to the qubits on which the measure will be applied.

  • observable (Union[Observable, list[Observable]]) – Observable used for the measure.

  • shots (int) – Number of shots to be performed.

  • commuting_type (CommutingTypes) – Type of commutation (grouped in an Enum) used for Pauli grouping when optimizing the observables measurement.

  • grouping_method (GroupingMethods) – Enum describing the method used for Pauli grouping.

  • label (Optional[str]) – Label used to identify the measure.

  • optimize_measurement (Optional[bool]) – Indicates if the Pauli grouping must be done to optimize the number of measurements. Default to True.

  • optim_diagonal (Optional[bool]) – Indicates if the computation of expectation value for diagonal observables is optimized. Default to False.

Warns

UserWarning – If the targets are not sorted and contiguous, some additional swaps will be needed. This will change the performance if your circuit is run on noisy hardware.

Examples

>>> obs = Observable(np.diag([0.7, -1, 1, 1]))
>>> c = QCircuit([H(0), CNOT(0,1), ExpectationMeasure(obs, shots=10000)])
>>> run(c, ATOSDevice.MYQLM_PYLINALG).expectation_values 
0.85918
>>> from mpqp.measures import X as pX, Y as pY
>>> obs2 = Observable( pX @ pY - pY @ pY)
>>> c = QCircuit([H(0), CNOT(0,1), ExpectationMeasure([obs, obs2], shots=10000)])
>>> run(c, IBMDevice.AER_SIMULATOR).expectation_values 
{'observable_0': 0.8514399940967561, 'observable_1': 0.9876}
get_pauli_grouping()[source]

Return the grouped monomials of the Pauli string of the observable. The grouping is done according to the grouping method of the expectation measure and the chosen commutativity type.

Return type

list[list[PauliStringMonomial]]

only_diagonal_observables()[source]

Returns True if all the observables in the ExpectationMeasure are diagonal.

Return type

bool

to_dict()[source]

Serialize the Expectation Measure to a dictionary. :returns: A dictionary representation of the Expectation Measure. :rtype: dict

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 | str

commuting_type

See parameter description.

grouping_method

See parameter description.

property nb_observables: int
observables: list[mpqp.core.instruction.measurement.expectation_value.Observable]

See parameter description.

property observables_labels: list[str]
optim_diagonal

See parameter description.

optimize_measurement

See parameter description.

class Observable(observable, label=None)[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 | list[float]) – can be either a Hermitian matrix representing the observable, or PauliString representing the observable, or a list of diagonal elements of the matrix when the observable is diagonal.

  • label (Optional[str]) – Label used to identify 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.

Examples

>>> Observable(np.array([[1, 0], [0, -1]]))
Observable(array([[ 1.+0.j, 0.+0.j], [ 0.+0.j, -1.+0.j]]))
>>> from mpqp.measures import I, X, Y, Z
>>> Observable(3 * I @ Z + 4 * X @ Y)
Observable(3*I@Z + 4*X@Y)
>>> Observable([1, -2, 3, -4])  
Observable([ 1., -2., 3., -4.])
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[CirqCircuit]) – The Cirq circuit associated with the observable (required if language == Language.CIRQ).

Returns

Depends on the target language.

Return type

Union[SparsePauliOp, QLMObservable, Hermitian, CirqPauliSum, CirqPauliString]

Example

>>> obs = Observable([0.7, -1, 1, 1])
>>> obs_qiskit = obs.to_other_language(Language.QISKIT)
>>> obs_qiskit.to_list()  
[('II', (0.425+0j)), ('IZ', (0.425+0j)), ('ZI', (-0.575+0j)), ('ZZ', (0.425+0j))]
property diagonal_elements: npt.NDArray[np.float64]

The diagonal elements of the matrix representing the observable (diagonal or not).

property is_diagonal: bool

Returns True if this observable is diagonal.

Examples

>>> Observable(np.array([[3, 0], [0, 8]])).is_diagonal
True
>>> Observable(np.array([[3, -1], [-1, 8]])).is_diagonal
False
>>> Observable(np.diag([-1,8,4,5])).is_diagonal
True
>>> Observable([0, 5, 6, 9, 7, 4, 3, 6]).is_diagonal
True
>>> Observable(np.array([7, 4, 3, 6])).is_diagonal
True
>>> from mpqp.measures import I, X, Y, Z
>>> Observable(I @ Z - 3 * Z @ Z + 2* Z @ I).is_diagonal
True
>>> Observable(I @ X - 3* Z @ Z + 2 * Y @ I).is_diagonal
False
label

See parameter description.

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

Observables can be defined using linear combinations of Pauli operators, these are called “Pauli strings”. In mpqp, a PauliString is a linear combination of PauliStringMonomial which are themselves combinations (tensor products) of PauliStringAtom. PauliString can be added, subtracted and tensored together, as well as multiplied by scalars.

class CommutingTypes(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: Enum

Enum regrouping the types of commutation supported by the PauliStrings.

FULL = 1
QUBITWISE = 2
class GroupingMethods(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: Enum

Enum regrouping the different Pauli grouping algorithms.

CLIQUE_REMOVING = 8
COLORING_DB = 6
COLORING_DSATUR = 7
COLORING_GREEDY = 2
COLORING_LF = 4
COLORING_RECURSIVE_LF = 5
COLORING_SF = 3
GREEDY = 1
class PauliString(monomials=None)[source]

Bases: object

Represents a Pauli string, a linear combination of Pauli monomials.

Note that as a user of our library, you would most likely never need to directly call this class. Instead, we advise you to build the Pauli strings you need from the atom we provide (see the example bellow).

Parameters

monomials (Optional[list['PauliStringMonomial']]) – List of Pauli monomials defining the PauliString.

Example

>>> from mpqp.measures import I, X, Y, Z
>>> I @ Z + 2 * Y @ I + X @ Z
I@Z + 2*Y@I + X@Z

Note

Pauli atoms are named I, X, Y, and Z. 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
static from_diagonal_elements(diagonal_elements, method='walsh')[source]

Create a PauliString from the diagonal elements of a diagonal observable, by using decomposition algorithms in the Pauli basis.

Currently, two different methods are available: “walsh” and “ptdr”. The first one is based on the computation of a walsh-hadamard matrix to retrieve the coefficients in front of the monomials that are combinations of I and Z. The second is an adaptation of the PTDR algorithm (Pauli Tree Decomposition Routine), see decompose_diagonal_observable_ptdr().

Parameters
  • diagonal_elements (list[float] | npt.NDArray[np.float64]) – List of Real coefficients

  • method (Literal['walsh', 'ptdr']) – String used to specify the decomposition method. “walsh” or “ptdr”.

Returns

The pauli decomposition of the given diagonal observable elements.

Return type

PauliString

Example

>>> PauliString.from_diagonal_elements([1, -1, 4, 2])
1.5*I@I + I@Z - 1.5*Z@I
static from_matrix(matrix, method='ptdr')[source]

Constructs a PauliString from a matrix.

Parameters
  • matrix (Matrix) – Matrix from which the PauliString is generated.

  • method (Literal['ptdr', 'trace']) – String indicating which Pauli decomposition method is used. “ptdr” refers to the tree-based decomposition algorithm (see decompose_hermitian_matrix_ptdr().), and “trace” to the one computing the trace of the observable with each possible monomial (naive method).

Returns

Pauli string corresponding to the Pauli decomposition of the matrix in parameter.

Raises

ValueError – If the input matrix is not square or its dimensions are not a power of 2.

Return type

PauliString

Example

>>> PauliString.from_matrix(np.array([[0, 1], [1, 2]]))
I + X - Z
static from_other_language(pauli, min_dimension=1)[source]

Convert pauli objects from other quantum SDKs to PauliString.

Parameters
  • pauli (Union[SparsePauliOp, BraketObservable, TensorProduct, list[Term], Term, CirqPauliSum, CirqPauliString, list[CirqPauliString], CirqGateOperation]) – The pauli object(s) to be converted.

  • min_dimension (int) – Minimal dimension of the resulting Pauli string.

Returns

The converted PauliString. If the input is a list, the output will be a list of PauliString.

Return type

PauliString | list[PauliString]

Examples

>>> from cirq import LineQubit, PauliSum, X as Cirq_X, Y as Cirq_Y, Z as Cirq_Z
>>> a, b, c = LineQubit.range(3)
>>> cirq_ps = 0.5 * Cirq_Z(a) * 0.5 * Cirq_Y(b) + 2 * Cirq_X(c)
>>> PauliString.from_other_language(cirq_ps)
0.25*Z@Y@I + 2.0*I@I@X
>>> from braket.circuits.observables import (
...     Sum as BraketSum,
...     I as Braket_I,
...     X as Braket_X,
...     Y as Braket_Y,
...     Z as Braket_Z,
... )
>>> braket_ps = 0.25 * Braket_Z() @ Braket_Y() @ Braket_I() + 2 * Braket_I() @ Braket_I() @ Braket_X()
>>> PauliString.from_other_language(braket_ps)
0.25*Z@Y@I + 2*I@I@X
>>> from qiskit.quantum_info import SparsePauliOp
>>> qiskit_ps = SparsePauliOp(["IYZ", "XII"], coeffs=[0.25 + 0.0j, 2.0 + 0.0j])
>>> PauliString.from_other_language(qiskit_ps)
0.25*Z@Y@I + 2.0*I@I@X
>>> from qat.core.wrappers.observable import Term
>>> my_qml_ps = [Term(0.25, "ZY", [0, 1]), Term(2, "X", [2])]
>>> PauliString.from_other_language(my_qml_ps)
0.25*Z@Y@I + 2*I@I@X
is_diagonal()[source]

Checks whether this pauli string has a diagonal representation, by checking if only I and Z Pauli operators appears in the monomials of the string.

Returns

True if the observable represented by pauli string is diagonal.

Return type

bool

Examples

>>> from mpqp.measures import I, X, Y, Z
>>> (I @ X + Z @ Y - Y @ X).is_diagonal()
False
>>> (I @ Z @ I - 2* Z @ Z @ Z + I @ I @ I).is_diagonal()
True
round(max_digits=4)[source]

Rounds the coefficients of the PauliString to a specified number of decimals.

Parameters

max_digits (int) – Number of decimal places to round the coefficients to.

Returns

The Pauli string with coefficients rounded to the specified number of decimals.

Return type

PauliString

Example

>>> from mpqp.measures import I, X, Y, Z
>>> ps = 0.6875 * I @ I + 0.1275 * I @ Z
>>> ps.round(1)
0.7*I@I + 0.1*I@Z
simplify(inplace=False)[source]

Simplifies the Pauli string by combining identical terms and removing terms with null coefficients. When all terms annihilate themselves, we return an empty PauliString with a number of qubits corresponding to the initial one.

Parameters

inplace (bool) – Indicates if self should be updated in addition of a new Pauli string being returned.

Returns

The simplified version of the Pauli string.

Return type

PauliString

Example

>>> from mpqp.measures import I, X, Y, Z
>>> (I @ I - 2 *I @ I + Z @ I - Z @ I).simplify()
-1*I@I
sorted_monomials()[source]

Creates a new Pauli string with the same monomials but sorted in monomial alphabetical ascending order (and the coefficients are not taken into account).

Returns

The Pauli string with its monomials sorted.

Return type

PauliString

Example

>>> from mpqp.measures import I, X, Y, Z
>>> (2*I@Z + .5*I@X + X@Y).sorted_monomials()
0.5*I@X + 2*I@Z + X@Y
subs(values, remove_symbolic=True)[source]

Substitute the coef of the pauli sting with values for each of the specified coef. Optionally also remove all symbolic variables such as \(\pi\) (needed for example for circuit execution).

Since we use sympy for the gate parameters, the values can in fact be anything the subs method from sympy would accept.

Parameters
  • values (dict[sympy.core.expr.Expr | str, numbers.Real]) – Mapping between the variables and the replacing values.

  • remove_symbolic (bool) – Whether symbolic values should be replaced by their numeric counterparts.

Returns

The pauli sting with the replaced parameters.

Return type

PauliString

Examples

>>> from mpqp.measures import I, X, Y, Z
>>> theta, k = symbols("θ k")
>>> ps = theta * I @ X + k * Z @ Y
>>> print(ps)
(θ)*I@X + (k)*Z@Y
>>> print(ps.subs({theta: np.pi, k: 1}))
3.1416*I@X + Z@Y
to_dict()[source]

Converts the Pauli string to a dictionary representation with the keys being the Pauli monomials and the values the corresponding coefficients.

Returns

Mapping representing the Pauli string.

Return type

dict[str, str]

Example

>>> from mpqp.measures import I, X, Y, Z
>>> (1 * I @ Z + 2 * I @ I).to_dict()
{'II': '2', 'IZ': '1'}
to_matrix()[source]

Converts the PauliString to a matrix representation.

Returns

Matrix representation of the Pauli string.

Return type

Matrix

Example

>>> from mpqp.measures import I, X, Y, Z
>>> pprint((I + Z).to_matrix())
[[2, 0],
 [0, 0]]
to_other_language(language, circuit=None)[source]

Converts the pauli string to pauli string of another quantum programming language.

Parameters
  • language (Language) – The target programming language.

  • circuit (Optional[CirqCircuit]) – The Cirq circuit associated with the pauli string (required for cirq).

Returns

Depends on the target language.

Return type

Union[SparsePauliOp, BraketSum, list[Term], Term, CirqPauliSum, CirqPauliString, list[CirqPauliString]]

Example

>>> from mpqp.measures import I, X, Y, Z
>>> ps = X @ X @ I + I @ Y @ I + I @ I @ Z
>>> print(ps.to_other_language(Language.CIRQ))
1.000*X(q(0))*X(q(1))+1.000*Y(q(1))+1.000*Z(q(2))
>>> for term in ps.to_other_language(Language.MY_QLM):
...     print(term.op, term.qbits)
XX [0, 1]
Y [1]
Z [2]
>>> print(ps.to_other_language(Language.QISKIT))
SparsePauliOp(['IXX', 'IYI', 'ZII'],
              coeffs=[1.+0.j, 1.+0.j, 1.+0.j])
>>> for tensor in ps.to_other_language(Language.BRAKET).summands:
...     print(tensor.coefficient, "".join(a.name for a in tensor.factors))
1 XXI
1 IYI
1 IIZ
property monomials: list[PauliStringMonomial]

Monomials of the PauliString.

property nb_qubits: int

Number of qubits associated with the PauliString.

class PauliStringAtom(label, matrix, eig_values, eig_vectors, basis_change)[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.complex128]) – The matrix representation of the Pauli operator.

  • eig_values (list[int]) – The eigenvalues associated with the Pauli operator.

  • eig_vectors (npt.NDArray[np.complex64]) – A list of eigenvectors associated with the Pauli operator.

  • basis_change (list[type[SingleQubitGate]]) – Basis change of the measurement associated with 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).

commutes_with(other, method=CommutingTypes.FULL)[source]

Determines whether this single-qubit Pauli operator commutes with the one in parameter. In this case, this can be done by simply checking if one of the atoms are identity, or if they are the same.

Parameters
  • other (PauliString) – The single-qubit Pauli operator with which want to check the commutativity

  • method (CommutingTypes) – The type of commutation being checked, Only full commutativity is implemented for PauliStringAtom.

Returns

True if the atoms commute, False otherwise.

Return type

bool

Examples

>>> from mpqp.measures import I, X, Y, Z
>>> X.commutes_with(X)
True
>>> X.commutes_with(Y)
False
>>> X.commutes_with(I)
True
>>> Y.commutes_with(Z)
False
>>> I.commutes_with(Z)
True
get_basis_change()[source]
Return type

list[type[mpqp.core.instruction.gates.gate.SingleQubitGate]]

to_matrix()[source]

Converts the PauliString to a matrix representation.

Returns

Matrix representation of the Pauli string.

Return type

npt.NDArray[np.complex128]

Example

>>> from mpqp.measures import I, X, Y, Z
>>> pprint((I + Z).to_matrix())
[[2, 0],
 [0, 0]]
to_other_language(language, circuit=None, target=None)[source]

Converts the pauli string to pauli string of another quantum programming language.

Parameters
  • language (Language) – The target programming language.

  • circuit (Optional[CirqCircuit]) – The Cirq circuit associated with the pauli string (required for cirq).

  • target (Optional[Qid]) –

Returns

Depends on the target language.

Example

>>> from mpqp.measures import I, X, Y, Z
>>> ps = X @ X @ I + I @ Y @ I + I @ I @ Z
>>> print(ps.to_other_language(Language.CIRQ))
1.000*X(q(0))*X(q(1))+1.000*Y(q(1))+1.000*Z(q(2))
>>> for term in ps.to_other_language(Language.MY_QLM):
...     print(term.op, term.qbits)
XX [0, 1]
Y [1]
Z [2]
>>> print(ps.to_other_language(Language.QISKIT))
SparsePauliOp(['IXX', 'IYI', 'ZII'],
              coeffs=[1.+0.j, 1.+0.j, 1.+0.j])
>>> for tensor in ps.to_other_language(Language.BRAKET).summands:
...     print(tensor.coefficient, "".join(a.name for a in tensor.factors))
1 XXI
1 IYI
1 IIZ
property atoms: list[mpqp.core.instruction.measurement.pauli_string.PauliStringAtom]

Atoms present. (needed for upward compatibility with PauliStringMonomial)

property coef: Union[Real, float, Expr]

Coefficient of the monomial.

property eigen_values: npt.NDArray[np.bool_]
property monomials

Monomials of the PauliString.

property nb_qubits: int

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 (Coef) – The coefficient of the monomial.

  • atoms (Optional[list['PauliStringAtom']]) – The list of PauliStringAtom objects forming the monomial.

commutes_with(other, method=CommutingTypes.FULL)[source]

Checks wether this Pauli monomial commutes (full commutativity) with the Pauli monomial in parameter. This is done by checking that the number of anti-commuting atoms is even.

Parameters
  • other (PauliString) – The Pauli monomial for which we want to check commutativity with this monomial.

  • method (CommutingTypes) – The type of commutation to be verified.

Returns

True if this Pauli monomial commutes with the one in parameter.

Return type

bool

Examples

>>> from mpqp.measures import I, X, Y, Z
>>> (I @ X @ Y).commutes_with(Z @ Y @ X)
True
>>> (X @ Z @ Z).commutes_with(Y @ Z @ I)
False
>>> (X @ Z @ Z).commutes_with(X @ Z @ Z)
True
>>> (X @ X).commutes_with(Y @ Y, CommutingTypes.FULL)
True
>>> (X @ X).commutes_with(Y @ Y, CommutingTypes.QUBITWISE)
False
simplify(inplace=False)[source]

Simplifies the Pauli string by combining identical terms and removing terms with null coefficients. When all terms annihilate themselves, we return an empty PauliString with a number of qubits corresponding to the initial one.

Parameters

inplace (bool) – Indicates if self should be updated in addition of a new Pauli string being returned.

Returns

The simplified version of the Pauli string.

Example

>>> from mpqp.measures import I, X, Y, Z
>>> (I @ I - 2 *I @ I + Z @ I - Z @ I).simplify()
-1*I@I
subs(values, remove_symbolic=True)[source]

Substitutes the parameters of the instruction with complex values. Optionally, also removes all symbolic variables such as \(\pi\) (needed for circuit execution, for example).

Since we use sympy for gate parameters, values can in fact be anything the subs method from sympy would accept.

Parameters
  • values (dict[sympy.core.expr.Expr | str, numbers.Real]) – Mapping between the variables and the replacing values.

  • remove_symbolic (bool) – Whether symbolic values should be replaced by their numeric counterparts.

Returns

The circuit with the replaced parameters.

Return type

PauliStringMonomial

Example

>>> from mpqp.measures import I, X, Y, Z
>>> theta = symbols("θ")
>>> print((theta * I @ X).subs({theta: np.pi}))
3.141592653589793*I@X
to_matrix()[source]

Converts the PauliString to a matrix representation.

Returns

Matrix representation of the Pauli string.

Return type

Matrix

Example

>>> from mpqp.measures import I, X, Y, Z
>>> pprint((I + Z).to_matrix())
[[2, 0],
 [0, 0]]
to_other_language(language, circuit=None)[source]

Converts the pauli string to pauli string of another quantum programming language.

Parameters
  • language (Language) – The target programming language.

  • circuit (Optional[CirqCircuit]) – The Cirq circuit associated with the pauli string (required for cirq).

Returns

Depends on the target language.

Example

>>> from mpqp.measures import I, X, Y, Z
>>> ps = X @ X @ I + I @ Y @ I + I @ I @ Z
>>> print(ps.to_other_language(Language.CIRQ))
1.000*X(q(0))*X(q(1))+1.000*Y(q(1))+1.000*Z(q(2))
>>> for term in ps.to_other_language(Language.MY_QLM):
...     print(term.op, term.qbits)
XX [0, 1]
Y [1]
Z [2]
>>> print(ps.to_other_language(Language.QISKIT))
SparsePauliOp(['IXX', 'IYI', 'ZII'],
              coeffs=[1.+0.j, 1.+0.j, 1.+0.j])
>>> for tensor in ps.to_other_language(Language.BRAKET).summands:
...     print(tensor.coefficient, "".join(a.name for a in tensor.factors))
1 XXI
1 IYI
1 IIZ
property atoms: list[mpqp.core.instruction.measurement.pauli_string.PauliStringAtom]

The list of atoms in the monomial.

coef

Coefficient of the monomial.

property monomials: list[PauliStringMonomial]

Monomials of the PauliString.

property name: str
property nb_qubits: int

Number of qubits associated with the PauliString.

property positive_eigen_values: npt.NDArray[np.bool_]

Return the eigen values associated with this Pauli monomial, stored as booleans for efficiency. True refers to +1 and False to -1.

Example

>>> from mpqp.measures import I, X, Y
>>> (X @ Y @ I).positive_eigen_values  
array([ True, True, False, False, False, False, True, True])
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}\)