Tools

Some additional tools are provided with our library. Even though most of them are geared at internal usage, they are all presented here. Amongst them, the ones most probable of being of use for you are in the Useful Maths Operations section, presenting mathematical tools for linear algebra, functions generalized to more data types, etc…

Useful Maths Operations

from mpqp.tools.maths import *

Mathematical tools for linear algebra, functions generalized to more data types, etc…

closest_unitary(matrix)[source]

Calculate the unitary matrix that is closest with respect to the operator norm distance to the general matrix in parameter.

Parameters

matrix (Matrix) – Matrix for which we want to determine the closest unitary matrix.

Returns

The closest unitary matrix.

Example

>>> is_unitary(np.array([[1, 2], [3, 4]]))
False
>>> u = closest_unitary(np.array([[1, 2], [3, 4]]))
>>> u
array([[-0.51449576,  0.85749293],
       [ 0.85749293,  0.51449576]])
>>> is_unitary(u)
True
cos(angle)[source]

Generalization of the cosine function, to take as input either sympy’s expressions or floating numbers.

Parameters

angle (Expr | Real) – The angle considered.

Returns

Cosine of the given angle.

Return type

sp.Expr | float

exp(angle)[source]

Generalization of the exponential function, to take as input either sympy’s expressions or floating numbers.

Parameters

angle (Expr | Complex) – The angle considered.

Returns

Exponential of the given angle.

Return type

sp.Expr | complex

is_hermitian(matrix)[source]

Checks whether the matrix in parameter is hermitian.

Parameters

matrix (Matrix) – matrix for which we want to know if it is hermitian.

Returns

True if the matrix in parameter is Hermitian.

Return type

bool

Examples

>>> is_hermitian(np.array([[1,2j,3j],[-2j,4,5j],[-3j,-5j,6]]))
True
>>> is_hermitian(np.diag([1,2,3,4]))
True
>>> m3 = np.array([[1,2,3],[2,4,5],[3,5,6]])
>>> is_hermitian(np.array([[1,2,3],[2,4,5],[3,5,6]]))
True
>>> m4 = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> is_hermitian(np.array([[1,2,3],[4,5,6],[7,8,9]]))
False
>>> x = symbols("x", real=True)
>>> is_hermitian(np.diag([1,x]))
True
>>> is_hermitian(np.array([[1,x],[-x,2]]))
False
is_power_of_two(n)[source]

Checks if the integer in parameter is a (positive) power of two.

Parameters

n (int) – Integer for which we want to check if it is a power of two.

Returns

True if the integer in parameter is a power of two.

Example

>>> is_power_of_two(4)
True
>>> is_power_of_two(6)
False
is_unitary(matrix)[source]

Checks whether the matrix in parameter is unitary.

Parameters

matrix (Matrix) – Matrix for which we want to know if it is unitary.

Returns

True if the matrix in parameter is Unitary.

Return type

bool

Example

>>> is_unitary(np.array([[1,1],[1,-1]]))
False
>>> is_unitary(np.array([[1,1],[1,-1]])/np.sqrt(2))
True
matrix_eq(lhs, rhs, atol=1e-08, rtol=1e-05)[source]

Checks whether two matrix (including vectors) are element-wise equal, within a tolerance.

For respectively each elements \(a\) and \(b\) of both inputs, we check this specific condition: \(|a - b| \leq (atol + rtol * |b|)\).

Parameters
  • lhs (Matrix) – Left-hand side matrix of the equality.

  • rhs (Matrix) – Right-hand side matrix of the equality.

  • atol (float) –

  • rtol (float) –

Returns

True if the two matrix are equal (according to the definition above).

Return type

bool

normalize(v)[source]

Normalizes an array representing the amplitudes of the state.

Parameters

v (npt.NDArray[np.complex64]) – The vector to be normalized.

Returns

The normalized vector.

Return type

npt.NDArray[np.complex64]

Examples

>>> vector = np.array([1,0,0,1])
>>> normalize(vector)
array([0.70710678, 0.        , 0.        , 0.70710678])
>>> vector = np.array([0,0,0,0])
>>> normalize(vector)
array([0, 0, 0, 0])
rand_clifford_matrix(nb_qubits, seed=None)[source]

Generate a random Clifford matrix.

Parameters
  • size – Size (number of columns) of the square matrix to generate.

  • seed (Optional[int]) – Seed used to initialize the random number generation.

  • nb_qubits (int) –

Returns

A random Clifford matrix.

Return type

npt.NDArray[np.complex64]

Examples

>>> pprint(rand_clifford_matrix(2))
[[-0.5 , 0.5  , 0.5j , -0.5j],
 [-0.5j, -0.5j, 0.5  , 0.5  ],
 [-0.5j, -0.5j, -0.5 , -0.5 ],
 [-0.5 , 0.5  , -0.5j, 0.5j ]]
>>> pprint(rand_clifford_matrix(2, seed=123))
[[0.70711j, 0        , -0.70711j, 0        ],
 [0       , -0.70711j, 0        , -0.70711j],
 [0       , 0.70711j , 0        , -0.70711j],
 [0.70711j, 0        , 0.70711j , 0        ]]
rand_hermitian_matrix(size, seed=None)[source]

Generate a random Hermitian matrix.

Parameters
  • size (int) – Size (number of columns) of the square matrix to generate.

  • seed (Optional[int]) – Seed used to initialize the random number generation.

Returns

A random Hermitian Matrix.

Return type

npt.NDArray[np.complex64]

Example

>>> pprint(rand_hermitian_matrix(2))
[[1.2917 , 0.64402],
 [0.64402, 1.10203]]
>>> pprint(rand_hermitian_matrix(2, seed=123))
[[1.3647 , 0.27418],
 [0.27418, 0.36874]]
rand_orthogonal_matrix(size, seed=None)[source]

Generate a random orthogonal matrix optionally with a given seed.

Parameters
  • size (int) – Size (number of columns) of the square matrix to generate.

  • seed (Optional[int]) – Seed used to initialize the random number generation.

Returns

A random orthogonal matrix.

Return type

npt.NDArray[np.complex64]

Examples

>>> pprint(rand_orthogonal_matrix(3))
[[0.70957 , 0.13959, 0.69067],
 [0.61432 , 0.35754, -0.7034],
 [-0.34513, 0.92341, 0.16795]]
>>> pprint(rand_orthogonal_matrix(3, seed=123))
[[0.75286 , -0.65782, 0.02175],
 [-0.22778, -0.22939, 0.94631],
 [0.61751 , 0.71739 , 0.32254]]
rand_product_local_unitaries(nb_qubits, seed=None)[source]

Generate a pseudo random matrix, resulting from a tensor product of random unitary matrices.

Parameters
  • nb_qubits (int) – Number of qubits on which the product of unitaries will act.

  • seed (Optional[int]) – Seed used to initialize the random number generation.

Returns

A tensor product of random unitary matrices.

Return type

npt.NDArray[np.complex64]

Example

>>> pprint(rand_product_local_unitaries(2))
[[0.07059          , 0.43591+0.02564j , 0.09107+0.1104j  , 0.52233+0.71486j ],
 [-0.43591-0.02564j, -0.06-0.03719j   , -0.52233-0.71486j, -0.01924-0.14182j],
 [-0.09107-0.1104j , -0.52233-0.71486j, -0.04361-0.05551j, -0.24913-0.35863j],
 [0.52233+0.71486j , 0.01924+0.14182j , 0.24913+0.35863j , 0.00782+0.07015j ]]
>>> pprint(rand_product_local_unitaries(2, seed=123))
[[-0.45364         , 0.11284-0.27441j , -0.13022-0.69112j, 0.45045+0.09315j ],
 [-0.11284+0.27441j, -0.45235+0.03417j, -0.45045-0.09315j, -0.18191-0.67934j],
 [0.13022+0.69112j , -0.45045-0.09315j, 0.06866-0.44841j , 0.25417+0.15308j ],
 [0.45045+0.09315j , 0.18191+0.67934j , -0.25417-0.15308j, 0.03469-0.45231j ]]
rand_unitary_2x2_matrix(seed=None)[source]

Generate a random one-qubit unitary matrix.

Parameters
  • size – Size (number of columns) of the square matrix to generate.

  • seed (Optional[Union[int, np.random.Generator]]) – Used for the random number generation. If unspecified, a new generator will be used. If a Generator is provided, it will be used to generate any random number needed. Finally if an int is provided, it will be used to initialize a new generator.

Returns

A random Clifford matrix.

Return type

npt.NDArray[np.complex64]

Examples

>>> pprint(rand_unitary_2x2_matrix())
[[-0.44234        , -0.57071-0.69183j],
 [0.57071+0.69183j, 0.27325+0.34784j ]]
>>> pprint(rand_unitary_2x2_matrix(seed=123))
[[-0.54205       , -0.1556-0.82582j],
 [0.1556+0.82582j, 0.08204-0.53581j]]
sin(angle)[source]

Generalization of the sine function, to take as input either sympy’s expressions or floating numbers.

Parameters

angle (Expr | Real) – The angle considered.

Returns

Sine of the given angle.

Return type

sp.Expr | float

atol = 1e-08

The absolute tolerance parameter.

rtol = 1e-05

The relative tolerance parameter.

Generics

from mpqp.tools.generics import *

This module contains a collection of generic types and functions needed across the library.

Type aliases Matrix, OneOrMany and ArbitraryNestedSequence are used across the library. In particular, ArbitraryNestedSequence is used in cases the nesting of the sequence is unknown; and in these cases you might want to flatten the list using flatten().

On occasion, there is also a need to “flatten” the string representation of an object i.e. to display it on one line. In this case one_line_repr() is your friend.

Lastly, we find the default list search mechanism in python a bit too restrictive. find() allow us a much more versatile search using an oracle.

class MessageEnum(value=<no_arg>, names=None, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: Enum

Enum subclass allowing you to access the docstring of the members of your enum through the message property.

Example

>>> class A(MessageEnum):  
...     '''an enum'''
...     VALUE1 = auto()
...     '''member VALUE1'''
...     VALUE2 = auto()
...     '''member VALUE2'''
>>> A.VALUE1.message  
'member VALUE1'

Warning

This implementation is not very robust, in particular, in case some members are not documented, it will mess things up. In addition, this can only work for code in file, and will not work in the interpreter.

message: str

This is an attribute for each member of the enum, giving more information about it.

class SimpleClassReprABC[source]

Bases: object

This class is the equivalent of ABC (it signifies that it’s subclass isn’t meant to be instantiated directly), but it adds the small feature of setting the repr to be the class name, which is for instance useful for gates.

class SimpleClassReprABCMeta(name, bases, namespace, **kwargs)[source]

Bases: SimpleClassReprMeta, ABCMeta

class SimpleClassReprMeta[source]

Bases: type

Metaclass used to change the repr of the class (not the instances) to display the name of the class only (instead of the usual <class mpqp.path.ClassName>)

class classproperty(func)[source]

Bases: object

Decorator yo unite the classmethod and property decorators.

Parameters

func (Callable[..., Any]) –

find(sequence, oracle)[source]

Finds the first element in the sequence that satisfies the given oracle.

Parameters
  • sequence (Sequence[T]) – The sequence to search for the element.

  • oracle (Callable[[T], bool]) – A callable function that takes an element and returns True if the element satisfies the condition.

Returns

The first element in the sequence that satisfies the oracle.

Raises

ValueError – If no element in the sequence satisfies the given oracle.

Return type

T

Example

>>> numbers = [1, 2, 3, 4, 5]
>>> is_even = lambda x: x % 2 == 0
>>> find(numbers, is_even)
2
find_index(iterable, oracle)[source]

Finds the index of the first element in the iterable that satisfies the given oracle.

Parameters
  • iterable (Iterable[T]) – The iterable to search for the element.

  • oracle (Callable[[T], bool]) – A callable function that takes an element and returns True if the element satisfies the condition.

Returns

The index of the first element in the iterable that satisfies the oracle.

Raises

ValueError – If no element in the iterable satisfies the given oracle.

Return type

int

Example

>>> numbers = [1, 2, 3, 4, 5]
>>> is_even = lambda x: x % 2 == 0
>>> find_index(numbers, is_even)
1
flatten(lst)[source]

Flattens an arbitrarily nested Sequence.

This is a wrapper around flatten_generator().

Parameters

lst (Union[Sequence[ArbitraryNestedSequence[T]], T]) – The nested sequence, to be flattened.

Returns

A flattened list containing all elements from the input list.

Return type

list[~T]

Example

>>> nested_list = [[1, 2, [3, 4]], [5, [6, 7]], 8]
>>> flatten(nested_list)
[1, 2, 3, 4, 5, 6, 7, 8]
flatten_generator(lst)[source]

Helper generator function for flattening an arbitrarily nested list.

Parameters

lst (Union[Sequence[ArbitraryNestedSequence[T]], T]) – The list, or nested list, to be flattened.

Yields

Elements from the input list in a flattened order.

Return type

Iterator[T]

ArbitraryNestedSequence

This type alias allows us to define heterogeneously nested Sequences of T.

Examples

>>> l: ArbitraryNestedSequence[int]
>>> l = [[0,1],0,[[0,2],3]]
>>> l = 1
>>> l = [2,1,3]

alias of Union[Sequence[ArbitraryNestedSequence[T]], T]

Matrix

Type alias denoting all the matrices we consider (either matrices of complex or of sympy expressions, given to numpy as objects)

alias of Union[ndarray[Any, dtype[complex64]], ndarray[Any, dtype[object_]]]

OneOrMany

Type alias for single elements of type T, or sequences of such elements.

alias of Union[T, Sequence[T]]

T

A generic type.

alias of TypeVar(‘T’)

Display helpers

from mpqp.tools.display import *
clean_1D_array(array, round=5)[source]

Cleans and formats elements of a one dimensional array. This function rounds the parts of the numbers in the array and formats them as integers if appropriate. It returns a string representation of the cleaned array.

Parameters
  • array (list[complex] | npt.NDArray[np.complex64 | np.float32]) – An array containing numeric elements.

  • round (int) – precision to round the numbers to.

Returns

A string representation of the cleaned array.

Return type

str

Example

>>> clean_1D_array([1.234567895546, 2.3456789645645, 3.45678945645])
'[1.23457, 2.34568, 3.45679]'
>>> clean_1D_array([1+2j, 3+4j, 5+6j])
'[1+2j, 3+4j, 5+6j]'
>>> clean_1D_array([1+0j, 0.5+0j, 5.+1j])
'[1, 0.5, 5+1j]'
>>> clean_1D_array([1.0, 2.1, 3.0])
'[1, 2.1, 3]'
>>> clean_1D_array([1+0j, 0+0j, 5.])
'[1, 0, 5]'
>>> clean_1D_array([1.0, 2.0, 3.0])
'[1, 2, 3]'
>>> clean_1D_array([-0.01e-09+9.82811211e-01j,  1.90112689e-01+5.22320655e-09j,
... 2.91896816e-09-2.15963155e-09j, -4.17753839e-09-5.64638430e-09j,
... 9.44235988e-08-8.58300965e-01j, -5.42123454e-08+2.07957438e-07j,
... 5.13144658e-01+2.91786504e-08j, -0000000.175980538-1.44108434e-07j])
'[0.98281j, 0.19011, 0, 0, -0.8583j, 0, 0.51314, -0.17598]'
>>> clean_1D_array([-0.01e-09+9.82811211e-01j,  1.90112689e-01+5.22320655e-09j,
... 2.91896816e-09-2.15963155e-09j, -4.17753839e-09-5.64638430e-09j,
... 9.44235988e-08-8.58300965e-01j, -5.42123454e-08+2.07957438e-07j,
... 5.13144658e-01+2.91786504e-08j, -0000000.175980538-1.44108434e-07j], round=7)
'[0.9828112j, 0.1901127, 0, 0, 1e-07-0.858301j, -1e-07+2e-07j, 0.5131446, -0.1759805-1e-07j]'
clean_matrix(matrix, round=5, align=True)[source]

Cleans and formats elements of a 2D matrix. This function rounds the parts of the numbers in the matrix and formats them as integers if appropriate. It returns a string representation of the cleaned matrix.

Parameters
  • matrix (Matrix) – An 2d array containing numeric elements.

  • round (int) – The number of decimal places to round the real and imaginary parts.

  • align (bool) – Whether to align the elements for a cleaner output.

Returns

A string representation of the cleaned matrix.

Examples

>>> print(clean_matrix(np.array([[1.234567895546, 2.3456789645645, 3.45678945645],
... [1+5j, 0+1j, 5.],
... [1.223123425+0.95113462364j, 2.0, 3.0]])))
[[1.23457         , 2.34568, 3.45679],
 [1+5j            , 1j     , 5      ],
 [1.22312+0.95113j, 2      , 3      ]]
clean_number_repr(number, round=7)[source]

Cleans and formats a number. This function rounds the parts of complex numbers and formats them as integers if appropriate. It returns a string representation of the number.

Parameters
  • number (Union[complex, complex64]) – The number to be formatted

  • round (int) –

Returns

A string representation of the number.

Example

>>> clean_number_repr(1.234567895546)
'1.2345679'
>>> clean_number_repr(1.0+2.0j)
'1+2j'
>>> clean_number_repr(1+0j)
'1'
>>> clean_number_repr(0.0 + 1.0j)
'1j'
format_element(element, round=5)[source]

Formats a numeric or symbolic element for cleaner representation. Rounds the real and imaginary parts of a number to a specified number of decimal places, formats whole numbers as integers, and properly handles symbolic expressions by simplifying them. It produces a string representation of the element, with ‘j’ notation for complex numbers and a simplified form for symbolic expressions.

Parameters
  • element (Union[int, float, complex | Expr]) – The element to format, which can be an integer, float, complex number, or symbolic expression.

  • round (int) – The number of decimal places to round to for real and imaginary parts.

Returns

A string representation of the formatted element.

Return type

str

Example

>>> format_element(3.456789, round=4)
'3.4568'
>>> format_element(1+2j, round=2)
'1+2j'
>>> format_element(3+0j)
'3'
>>> from sympy import symbols, Expr
>>> x = symbols('x')
>>> format_element(Expr(x + x))
'2*x'
one_lined_repr(obj)[source]

One-liner returning a representation of the given object by removing extra whitespace.

Parameters

obj (object) – The object for which a representation is desired.

pprint(matrix, round=5, align=True)[source]

Print a cleans and formats elements of a matrix. It rounds the real parts of complex numbers in the matrix places and formats them as integers if they are whole numbers. It returns a string representation of the cleaned matrix without parentheses.

Parameters
  • matrix (Matrix) – A matrix containing numeric elements, possibly including complex numbers.

  • round (int) – The number of decimal places to round the real and imaginary parts.

  • align (bool) – Whether to align the elements for a cleaner output.

Example

>>> pprint(np.array([[1.234567895546, 2.3456789645645, 3.45678945645],
... [1+5j, 0+1j, 5.],
... [1.223123425+0.95113462364j, 2.0, 3.0]]))
[[1.23457         , 2.34568, 3.45679],
 [1+5j            , 1j     , 5      ],
 [1.22312+0.95113j, 2      , 3      ]]
state_vector_ket_shape(sv)[source]

Formats a state vector into its ket format.

Parameters

sv (npt.NDArray[np.complex64]) –

Return type

str

with_sign(val)[source]

Sometimes, we want values under a specific format, in particular <sign> <value>. Where value is as simple as possible (e.g. no period or no imaginary part if there is no need).

Parameters

val (Union[complex64, complex128]) – The value to be formatted.

Returns

The formatted value

Return type

str

Errors

from mpqp.tools.errors import *

You will find here the custom exceptions we created in order to provide clearer errors. When relevant, we also append the trace of the error raised by a provider’s SDK.

exception AWSBraketRemoteExecutionError[source]

Bases: RemoteExecutionError

Raised when an error occurred during the remote execution process of job(s) on the remote Amazon Braket.

exception AdditionalGateNoiseWarning[source]

Bases: UserWarning

Warning for additional noise on native gate used in the decomposition of noisy gate.

exception DeviceJobIncompatibleError[source]

Bases: ValueError

Raised when one tries to run a job with a JobType that is not suitable for the selected device (for example SAMPLE job on a statevector simulator).

exception IBMRemoteExecutionError[source]

Bases: RemoteExecutionError

Raised when an error occurred during the remote execution process of job(s) on an IBM device.

exception InstructionParsingError[source]

Bases: ValueError

Raised when an QASM instruction encountered by the parser is malformed.

exception NonReversibleWarning[source]

Bases: UserWarning

Warning for nonreversible instruction used in inverse function.

exception NumberQubitsError[source]

Bases: ValueError

Raised when the number of qubits defining an instruction, a gate, or a measurement, is not coherent with the related objects (circuit, matrix, observable, etc…).

exception NumberQubitsWarning[source]

Bases: UserWarning

Raised when the number of qubits defining an instruction, a gate, or a measurement, is not coherent with the related objects (circuit, matrix, observable, etc…).

exception OpenQASMTranslationWarning[source]

Bases: UserWarning

Warning for potential translation error when exporting to OpenQASM.

exception QLMRemoteExecutionError[source]

Bases: RemoteExecutionError

Raised when an error occurred during the remote execution process of job(s) on the remote QLM.

exception RemoteExecutionError[source]

Bases: ConnectionError

Raised when an error occurred during a remote connection, submission or execution.

exception ResultAttributeError[source]

Bases: AttributeError

Raised when one tries to access the attribute of the result that is incoherent with the associated job.

exception UnsupportedBraketFeaturesWarning[source]

Bases: UserWarning

Warning for potential compatibility issues with Braket.

Circuit tricks

from mpqp.tools.circuit import *
compute_expected_matrix(qcircuit)[source]

Computes the expected matrix resulting from applying single-qubit gates in reverse order on a quantum circuit.

Parameters

qcircuit (QCircuit) – The quantum circuit object containing instructions.

Returns

Expected matrix resulting from applying the gates.

Raises

ValueError – If any gate in the circuit is not a SingleQubitGate.

random_circuit(gate_classes=None, nb_qubits=5, nb_gates=None, seed=None)[source]

This function creates a QCircuit with a specified number of qubits and gates. The gates are chosen randomly from the provided list of native gate classes.

Parameters
  • nb_qubits (int) – Number of qubits in the circuit.

  • gate_classes (Optional[Sequence[type[Gate]]]) – List of native gate classes to use in the circuit.

  • nb_gates (Optional[int]) – Number of gates to add to the circuit.

  • seed (Optional[int]) – Seed used to initialize the random number generation.

Returns

A quantum circuit with the specified number of qubits and randomly chosen gates.

Raises

ValueError – If the number of qubits is too low for the specified gates.

Examples

>>> print(random_circuit([U, TOF], 3)) 
     ┌───┐┌───┐     ┌───┐                            ┌───┐┌───┐
q_0: ┤ X ├┤ X ├──■──┤ X ├────────────────────────────┤ X ├┤ X ├
     └─┬─┘└─┬─┘┌─┴─┐└─┬─┘                            └─┬─┘└─┬─┘
q_1: ──■────■──┤ X ├──■────────────────────────────────■────■──
       │    │  └─┬─┘  │  ┌──────────────────────────┐  │    │
q_2: ──■────■────■────■──┤ U(0.31076,5.5951,6.2613) ├──■────■──
                         └──────────────────────────┘
>>> print(random_circuit([U, TOF], 3, seed=123)) 
          ┌───┐                           ┌───┐
q_0: ──■──┤ X ├───────────────────────────┤ X ├
     ┌─┴─┐└─┬─┘┌─────────────────────────┐└─┬─┘
q_1: ┤ X ├──■──┤ U(5.1025,5.8015,1.7378) ├──■──
     └─┬─┘  │  ├─────────────────────────┤  │
q_2: ──■────■──┤ U(5.5914,3.2231,1.5392) ├──■──
               └─────────────────────────┘
random_gate(gate_classes=None, nb_qubits=5, seed=None)[source]

This function creates a gate with a specified number of qubits. The gate are chosen randomly from the provided list of native gate classes.

Parameters
  • nb_qubits (int) – Number of qubits in the circuit.

  • gate_classes (Optional[Sequence[type[Gate]]]) – List of native gate classes to use in the circuit.

  • seed (Optional[Union[int, Generator]]) –

Returns

A quantum circuit with the specified number of qubits and randomly chosen gates.

Raises

ValueError – If the number of qubits is too low for the specified gates.

Return type

Gate

Examples

>>> random_gate([U, TOF], 3) 
U(2.067365317109373, 0.18652872274018245, 0.443968374745352, 0)
>>> random_gate(nb_qubits=4) 
SWAP(3, 1)
random_noise(noise_model=None, seed=None)[source]

This function creates a noise model. The noise are chosen randomly from the provided list of noise model.

Parameters
Returns

A quantum circuit with the specified number of qubits and randomly chosen gates.

Raises

ValueError – If the number of qubits is too low for the specified gates.

Return type

NoiseModel

Examples

>>> random_noise() 
Depolarizing(0.37785041428875576)
replace_custom_gate(custom_unitary, nb_qubits)[source]

Decompose and replace the (custom) qiskit unitary given in parameter by a qiskit \(QuantumCircuit\) composed of U and CX gates.

Note

When using Qiskit, a global phase is introduced (related to usage of u in OpenQASM2). This may be problematic in some cases, so this function also returns the global phase introduced so it can be corrected later on.

Parameters
  • custom_unitary (CircuitInstruction) – instruction containing the custom unitary operator.

  • nb_qubits (int) – Number of qubits of the circuit from which the unitary instruction was taken.

Returns

A circuit containing the decomposition of the unitary in terms of gates U and CX, and the global phase used to correct the statevector if need be.

Return type

tuple[‘QuantumCircuit’, float]

Choice Tree

from mpqp.tools.choice_tree import *

This module provides functionalities for working with decision trees, allowing for seamless navigation and interaction within a tree structure.

You can define a QuestionNode, containing your question and options. Each option (AnswerNode) contains the description of the option together with optional actions and follow up question.

To run your choice tree, just run run_choice_tree() on your root question.

class AnswerNode(label, action, next_question=None)[source]

Bases: object

Represents a node in a decision tree corresponding to an answer to a question. An answer can lead to an action, or to another question.

Parameters
  • label (str) – See attribute description.

  • action (Callable[[...], tuple[str, Iterable[Any]]]) – See attribute description.

  • next_question (Optional[QuestionNode]) – See attribute description.

action: Callable[[...], tuple[str, Iterable[Any]]]

A callable representing an action function linked to an answer. The return value of the action is composed of the text to display after it ran (something like “Success”, “Failure”, or “You’re a wonderful human being”), and the parameters to pass to the next action to be executed. These parameters to be passed between actions are useful to keep memory of the previous actions for instance.

label: str

The label or text associated with the answer.

next_question: Optional[QuestionNode] = None

An optional reference to the next question node.

class QuestionNode(label, answers, leaf_loop_to_here=False)[source]

Bases: object

Represents a node in a decision tree corresponding to a question.

Parameters
  • label (str) – The label or text associated with the question.

  • answers (list[mpqp.tools.choice_tree.AnswerNode]) – List of possible answers to this question.

  • leaf_loop_to_here (bool) – If True answers without followup questions will loop back to here.

answers: list[mpqp.tools.choice_tree.AnswerNode]
label: str
leaf_loop_to_here: bool = False
run_choice_tree(question)[source]

Execute the choice tree by starting with the question node in parameter.

Parameters

question (QuestionNode) – Root question from which the choice tree will start

Example

def tea_picker(*_):
    name = input("What's your favorite tea brand?\n\t")
    return f"Very good choice, I love {name}!", [name]

choice_tree = QuestionNode(
    "Hey, what's your favorite beverage?",
    [
        AnswerNode(
            "Tea",
            tea_picker,
            QuestionNode(
                "Do you want to come at my place to drink some",
                [
                    AnswerNode(
                        "Yes",  lambda n: (f"Cool, let's go for this cup of {n} :D", [])
                    ),
                    AnswerNode("Can't :(", lambda n: ("No worries, another time!", [])),
                ],
            ),
        ),
        AnswerNode(
            "Coffee",
            lambda: ("I get it, you gotta do what you gotta do to stay awake", []),
            QuestionNode(
                "And how do you like it?",
                [
                    AnswerNode(
                        "American style",
                        lambda: (
                            "Hydrating is important, but I'm not sure this counts as coffee",
                            [],
                        ),
                    ),
                    AnswerNode("Italian", lambda: ("The only right way!", [])),
                ],
            ),
        ),
    ],
)

assert choice_tree.answers[-1].next_question is not None

choice_tree.answers[-1].next_question.answers[0].next_question = choice_tree

run_choice_tree(choice_tree)

Theoretical simulations

from mpqp.tools.theoretical_simulation import *

This module contains the tools to check if a result obtained by running the circuit on one of our provider’s device produce a result compatible with the theory. (Which could itself be useful to find problems either in our code or in our provider’s code.)

In order to do so, theoretical_probs() performs a theoretical simulation of a QCircuit. The noise models in the circuit will be taken into account (but not the shot noise).

This theoretical simulation will be compared against the execution on a AvailableDevice in exp_id_dist(). This comparison is performed using a distance between statistical distribution called the Jensen-Shannon distance.

If the distance is small enough, validate_noisy_circuit() will return True, meaning that the empirical data is within the expected range.

The size of the trust interval is computed in trust_int() by computing the Jensen-Shannon distance between a non noisy circuit and a noisy one. The idea being that the more noise you have in a circuit, the bigger your trust interval needs to be (the uncertainty is bigger).

The interval size is not linearly related with this distance so this needs to be passed in a re-normalizing function before being used. This is the role dist_alpha_matching() is playing.

amplitude(circ)[source]

Computes the theoretical probabilities of a (potentially) noisy circuit execution.

Parameters

circ (QCircuit) – The circuit to run.

Returns

The probabilities corresponding to each basis state.

Return type

ndarray[Any, dtype[complex64]]

dist_alpha_matching(alpha)[source]

The trust interval is computed from the distance between the circuit without noise and the noisy circuits probability distributions. This interval depends on this distance in a non linear manner. This function gives the relation between the two.

Parameters

alpha (float) – The distance Jensen-Shannon between the non noisy distribution and the noisy distribution.

Returns

Diameter of the trust interval for the distance.

exp_id_dist(circuit, shots=1024, device=AWSDevice.BRAKET_LOCAL_SIMULATOR)[source]

This function computes Jensen-Shannon the distance between the non noisy distribution and the noisy distribution.

Parameters
  • circuit (QCircuit) – The circuit.

  • shots (int) – Number of shots in the basis measurement.

  • device (AvailableDevice) – The device to be tested.

Returns

The distance between the non noisy distribution and the noisy distribution.

exp_id_dist_excess(circuit, shots=1024)[source]

Computes the gap between theory and our noise pipeline for a circuit.

Parameters
  • circuit (QCircuit) – The circuit (with potential noises).

  • shots (int) – Number of shots in the basis measurement.

Returns

  1. the distance between theory and our results;

  2. and the trust interval.

Return type

The gap between

theoretical_probs(circ)[source]

Computes the theoretical probabilities of a (potentially) noisy circuit execution.

Parameters

circ (QCircuit) – The circuit to run.

Returns

The probabilities corresponding to each basis state.

Return type

ndarray[Any, dtype[float32]]

trust_int(circuit)[source]

Given a circuit, this computes the diameter of the trust interval for the output samples given into consideration the noise in the circuit.

Parameters

circuit (QCircuit) – The circuit.

Returns

The size of the trust interval (related to the Jensen-Shannon distance).

validate_noisy_circuit(circuit, shots=1024, device=AWSDevice.BRAKET_LOCAL_SIMULATOR)[source]

Validates our noise pipeline for a circuit.

Parameters
  • circuit (QCircuit) – The circuit (with potential noises).

  • shots (int) – Number of shots in the basis measurement.

  • device (AvailableDevice) – The device to be tested.

Returns

Weather our noise pipeline matches the theory or not.

Return type

bool