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.
- Return type:
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 | float) – The angle considered.
- Returns:
Cosine of the given
angle.- Return type:
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:
Expr | complex
- is_diagonal(matrix)[source]
Checks whether the square matrix in parameter is diagonal.
- Parameters:
matrix (npt.NDArray[Any]) – Matrix for which we want to know if it is diagonal.
- Returns:
Trueif the matrix in parameter is diagonal.- Return type:
bool
Examples
>>> is_diagonal(np.diag([1, 4, 2, 3])) True >>> is_diagonal(np.array([[1, 1], [1, -1]])) False
- 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:
Trueif 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.
- Return type:
bool
Example
>>> is_power_of_two(4) True >>> is_power_of_two(6) False
- is_unitary(matrix, fuzzy=False)[source]
Checks whether the matrix in parameter is unitary.
- Parameters:
matrix (Matrix) – Matrix for which we want to know if it is unitary.
fuzzy (bool) – Whether the check used in the matrix equality should be fuzzy or not.
- Returns:
Trueif the matrix in parameter is Unitary.- Return type:
bool
Examples
>>> 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, fuzzy=False)[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|)\).
A fuzzy option can be enabled to check if the matrices have a chance to be equal by considering that if a coefficient of the difference between the two matrix is symbolic then it could 0. Note that this is quite naive and that to be more complete we could derive equations from the equality and ensure that the equations have at least one solution, but this would take too much time so we stick with the naive approach.
- Parameters:
lhs (Matrix) – Left-hand side matrix of the equality.
rhs (Matrix) – Right-hand side matrix of the equality.
atol (float) – The absolute tolerance parameter.
rtol (float) – The relative tolerance parameter.
fuzzy (bool) – If
True, symbolic difference is considered as nullable (see above for more detail).
- Returns:
Trueif 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.complex128]) – The vector to be normalized.
- Returns:
The normalized vector.
- Return type:
npt.NDArray[np.complex128]
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:
nb_qubits (int) – Qubits of the clifford operator to be generated.
seed (Optional[int]) – Seed used to initialize the random number generation.
- Returns:
A random Clifford matrix.
- Return type:
npt.NDArray[np.complex128]
Examples
>>> pprint(rand_clifford_matrix(2)) [[0.5 , -0.5j, -0.5 , -0.5j], [0.5j, 0.5 , 0.5j , -0.5 ], [0.5j, -0.5 , -0.5j, -0.5 ], [0.5 , 0.5j , 0.5 , -0.5j]]
>>> pprint(rand_clifford_matrix(2, seed=123)) [[0.70711 , 0 , 0.70711 , 0 ], [0 , -0.70711, 0 , -0.70711 ], [-0.70711j, 0 , 0.70711j, 0 ], [0 , 0.70711j, 0 , -0.70711j]]
- 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.complex128]
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.complex128]
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.complex128]
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:
seed (Optional[Union[int, np.random.Generator]]) – Used for the random number generation. If unspecified, a new generator will be used. If a
Generatoris provided, it will be used to generate any random number needed. Finally if anintis provided, it will be used to initialize a new generator.- Returns:
A random Clifford matrix.
- Return type:
npt.NDArray[np.complex128]
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]]
- rand_unitary_matrix(size)[source]
Generate a random Unitary matrix sampled from the group U(N), calling the associated \(scipy\) function.
- Parameters:
size (int) – Size (number of columns) of the square matrix to generate.
- Returns:
A random unitary matrix with complex coefficients.
- Return type:
Example
>>> is_unitary(rand_unitary_matrix(4)) True >>> is_unitary(rand_unitary_matrix(8)) True
- sin(angle)[source]
Generalization of the sine function, to take as input either
sympy’s expressions or floating numbers.- Parameters:
angle (Expr | float) – The angle considered.
- Returns:
Sine of the given
angle.- Return type:
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(new_class_name, /, names, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
Bases:
EnumEnum subclass allowing you to access the docstring of the members of your enum through the
messageproperty.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:
objectThis 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
reprto 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:
typeMetaclass 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 T
A generic type.
alias of TypeVar(‘T’)
- class classproperty(func)[source]
Bases:
objectDecorator yo unite the
classmethodandpropertydecorators.- Parameters:
func (Callable[..., Any])
- find(sequence, oracle)[source]
Finds the first element in the sequence that satisfies the given oracle.
- Parameters:
- 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:
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:
- 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 (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 (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
Sequence[ArbitraryNestedSequence[T]] |T
- Matrix
Type alias denoting all the matrices we consider (either matrices of complex or of
sympyexpressions, given tonumpyas objects)alias of
ndarray[tuple[Any, …],dtype[complex64]] |ndarray[tuple[Any, …],dtype[float64]] |ndarray[tuple[Any, …],dtype[object_]] |ndarray[tuple[Any, …],dtype[complex128]]
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.complex128 | np.float64 | np.float32 | np.int32]) – 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.5131447, -0.1759805-1e-07j]'
- clean_matrix(matrix, round=5, align=True, max_display_size=0)[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.
max_display_size (int) – If greater that 0, the displayed matrix will show ellipsis for indexes other the given size.
- 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.0], ... [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 (complex | complex128) – 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, precision=10)[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.
- Parameters:
element (Union[Real, int, float, complex, Expr, Basic]) – The element to format, which can be an integer, float, complex number, or symbolic expression.
precision (int) – The number of decimal places to round to for real and imaginary parts.
- Returns:
The formatted element without converting it to a string.
Example
>>> type(format_element(3.456789, 4)) <class 'float'> >>> type(format_element(1+2j, 2)) <class 'complex'> >>> type(format_element(3+0j)) <class 'int'> >>> from sympy import symbols, Expr >>> x = symbols('x') >>> x_ = 1 +x >>> x_ = x_.subs({'x': 1}) >>> type(x_) <class 'sympy.core.numbers.Integer'> >>> type(format_element(x_)) <class 'int'>
- format_element_str(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_str(3.456789, round=4) '3.4568' >>> format_element_str(1+2j, round=2) '1+2j' >>> format_element_str(3+0j) '3' >>> from sympy import symbols, Expr >>> x = symbols('x') >>> format_element_str(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 | list[Complex] | npt.NDArray[np.complex128 | np.float32 | np.int32]) – 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 ]] >>> pprint([1.0, 2.1, 3.0]) [1, 2.1, 3] >>> pprint([1+0j, 0+0j, 5.]) [1, 0, 5] >>> pprint([1.0, 2.0, 3.0]) [1, 2, 3]
- state_vector_ket_shape(sv)[source]
Formats a state vector into its ket format.
- Parameters:
sv (npt.NDArray[np.complex128])
- 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 (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:
RemoteExecutionErrorRaised when an error occurred during the remote execution process of job(s) on the remote Amazon Braket.
- exception AdditionalGateNoiseWarning[source]
Bases:
UserWarningWarning for additional noise on native gate used in the decomposition of noisy gate.
- exception DeviceJobIncompatibleError[source]
Bases:
ValueErrorRaised 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 DeviceJobIncompatibleWarning[source]
Bases:
UserWarningA warning is issued when a job is not compatible with the selected device.
- exception IBMNoiseModelGeneration[source]
Bases:
UserWarningWarning for potential compatibility issues with IBM noise model.
- exception IBMRemoteExecutionError[source]
Bases:
RemoteExecutionErrorRaised when an error occurred during the remote execution process of job(s) on an IBM device.
- exception InstructionAfterMeasurementError[source]
Bases:
ValueErrorRaised when one tries to add an instruction after a measurement in a circuit.
- exception InstructionParsingError[source]
Bases:
ValueErrorRaised when an QASM instruction encountered by the parser is malformed.
- exception NonReversibleWarning[source]
Bases:
UserWarningWarning for nonreversible instruction used in inverse function.
- exception NumberQubitsError[source]
Bases:
ValueErrorRaised 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:
UserWarningRaised 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:
UserWarningWarning for potential translation error when exporting to OpenQASM.
- exception QLMRemoteExecutionError[source]
Bases:
RemoteExecutionErrorRaised when an error occurred during the remote execution process of job(s) on the remote QLM.
- exception RemoteExecutionError[source]
Bases:
ConnectionErrorRaised when an error occurred during a remote connection, submission or execution.
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 (Sequence[type[TypeAliasForwardRef('Gate')]] | None) – List of native gate classes to use in the circuit.
nb_gates (int | None) – Number of gates to add to the circuit.
seed (int | None) – 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 (Sequence[type[TypeAliasForwardRef('Gate')]] | None) – List of native gate classes to use in the circuit.
seed (int | Generator | None)
- 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:
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:
noise_model (Sequence[type[TypeAliasForwardRef('NoiseModel')]] | None) – List of noise model.
seed (int | Generator | None)
- 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:
Examples
>>> random_noise() Depolarizing(0.37785041428875576)
- replace_custom_gate(custom_unitary, nb_qubits, targets)[source]
Decompose and replace the (custom) qiskit unitary given in parameter by a qiskit \(QuantumCircuit\) composed of
UandCXgates.Note
When using Qiskit, a global phase is introduced (related to usage of
uin 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.
targets (list[int])
- Returns:
A circuit containing the decomposition of the unitary in terms of gates
UandCX, and the global phase used to correct the statevector if need be.- Return type:
tuple[QuantumCircuit, float]
- statevector_from_random_circuit(nb_qubits=5, seed=None)[source]
This function creates a statevector with a specified number of qubits, generated from a random circuit executed on IBM AER Simulator. The QCircuit is generated randomly and his statevector is calculated.
- Parameters:
nb_qubits (int) – Number of qubits in the circuit.
seed (Optional[int]) – Seed used to initialize the random number generation.
- Returns:
The statevector with the specified number of qubits
- Return type:
npt.NDArray[np.complex128]
Examples
>>> print(statevector_from_random_circuit(2, seed=123)) [0.70710678+0.j 0. -0.j 0.26893257-0.65396886j 0. -0.j ]
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:
objectRepresents 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 (QuestionNode | None) – 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: QuestionNode | None = None
An optional reference to the next question node.
- class QuestionNode(label, answers, leaf_loop_to_here=False)[source]
Bases:
objectRepresents a node in a decision tree corresponding to a question.
- Parameters:
label (str) – The label or text associated with the question.
answers (list[TypeAliasForwardRef('AnswerNode')]) – List of possible answers to this question.
leaf_loop_to_here (bool) – If
Trueanswers without followup questions will loop back to here.
- answers: list[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[tuple[Any, …], dtype[complex128]]
- 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:
the distance between theory and our results;
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[tuple[Any, …], dtype[float64]]
- 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
Observable decomposition
from mpqp.tools.obs_decomposition import *
Functions used for the decomposition of observables in the Pauli basis.
- class DiagPauliNode(atom=None, parent=None)[source]
Bases:
objectA class represents a node in the Pauli tree used for decomposing a diagonal observable into a PauliString.
- Parameters:
atom (Optional[PauliStringAtom]) – The Pauli atom (I, Z) associated with this node.
parent (Optional['DiagPauliNode']) – The parent node.
Nonemeans that the node is the root of the tree.
- get_monomial()[source]
Constructs and returns the PauliStringMonomial corresponding to the node.
- Returns:
The monomial representation of the node.
- Return type:
- property childI
Returns the child node corresponding to the Pauli-I atom.
- property childZ
Returns the child node corresponding to the Pauli-Z atom.
- class PauliNode(atom=None, parent=None)[source]
Bases:
objectA class represents a node in the Pauli tree used for decomposing a Hermitian matrix into a PauliString.
- Parameters:
atom (Optional[PauliStringAtom]) – The Pauli atom (I, X, Y, Z) associated with this node.
parent (Optional['PauliNode']) – The parent node in the decomposition tree.
Nonemeans that the node is the root of the tree.
- compute_coefficients(k, m, current_node, matrix, monomial_list)[source]
Computes coefficients for the current node in the pauli tree based on the given matrix.
- Parameters:
k (list[int]) – A list of column indices where the non-zero elements of the matrix are located.
m (list[bool]) – A list of booleans corresponding to the non-zero coefficients of the matrix indexed in \(k\). False refers to -1, while True to 1.
current_node (PauliNode) – The current node in the Pauli tree.
matrix (Matrix) – The given Hermitian matrix to be decomposed.
monomial_list (list[TypeAliasForwardRef('PauliStringMonomial')]) – A list to store the computed monomials.
- compute_coefficients_diagonal_case(m, current_node, diag_elements, monomial_list)[source]
Computes coefficients for the current node in the pauli tree based on the diagonal elements.
- Parameters:
m (list[bool]) – A list of booleans corresponding to the non-zero coefficients of the matrix indexed in \(k\). False refers to -1, while True to 1.
current_node (DiagPauliNode) – The current node in the Pauli tree.
diag_elements (npt.NDArray[np.float64]) – The diagonal elements of the observable.
monomial_list (list[PauliStringMonomial]) – A list to store the computed monomials.
- compute_coefficients_walsh(H_matrix, diagonal_elements)[source]
Computes the coefficients using the Walsh-Hadamard transform.
- Parameters:
H_matrix (npt.NDArray[np.int8]) – The Hadamard matrix.
diagonal_elements (npt.NDArray[np.float64]) – The diagonal elements of the observable.
- Returns:
The computed coefficients.
- Return type:
list[float]
- decompose_diagonal_observable_ptdr(diag_elements, print_progression=False)[source]
Decomposes a diagonal observable into a Pauli string representation.
- Parameters:
diag_elements (list[float] | npt.NDArray[np.float64]) – The diagonal elements of the observable.
print_progression (bool) – Print the progression of the algorithm through the run (exploration of the node).
- Returns:
The corresponding Pauli string representation.
- Return type:
- decompose_diagonal_observable_walsh_hadamard(diag_elements)[source]
Decomposes the observable represented by the diagonal elements into a Pauli string using the Walsh-Hadamard transform.
- Parameters:
diag_elements (list[float] | npt.NDArray[np.float64]) – The diagonal elements of the observable.
- Returns:
The corresponding Pauli string representation.
- Return type:
- decompose_hermitian_matrix_ptdr(matrix, print_progression=False)[source]
- Decompose the observable represented by the hermitian matrix given in
parameter into a PauliString.
- Parameters:
matrix (Matrix) – Hermitian matrix representing the observable to be decomposed.
print_progression (bool) – Print the progression of the algorithm through the run (exploration of the node).
- Returns:
The resulting decomposition as a PauliString representation.
- Return type:
- Raises:
ValueError – If the matrix is not Hermitian or its dimensions are not a power of 2.
- Reference:
Océane Koska, Marc Baboulin & Arnaud Gazda. (2024). A tree-approach Pauli decomposition algorithm with application to quantum computing. Link: https://arxiv.org/pdf/2403.11644
- generate_and_explore_node(k, m, current_node, matrix, n, monomials, progression=None)[source]
Recursively generates and explores nodes in the Pauli tree.
- Parameters:
k (list[int]) – A list of column indices where the non-zero elements of the matrix are located. Since the matrix corresponding to each Pauli monomial is sparse and contains only one non-zero element per line, we only store the index of this element for each line.
m (list[bool]) – A list of booleans corresponding to the non-zero coefficients of the matrix indexed in \(k\). By considering the modified Pauli basis (
Yreplaced byiY), only 2 values are possible: 1 and -1. Thus, they can be stored as booleans: False refers to -1, while True to 1.current_node (PauliNode) – The current node in the Pauli tree.
matrix (Matrix) – The given Hermitian matrix to be decomposed.
n (int) – Number of qubits of the observable.
monomials (list[TypeAliasForwardRef('PauliStringMonomial')]) – A list to store the computed monomials.
progression (list[int] | None) – Tuple of integers storing the index of the current node and the tree size for printing the progression. None if no printing required.
- generate_and_explore_node_diagonal_case(m, current_node, diag_elements, n, monomials, progression=None)[source]
Recursively explores the Pauli tree and computes the required monomials.
- Parameters:
m (list[bool]) – A list of booleans corresponding to the non-zero coefficients of the matrix indexed in \(k\). By considering monomials only made of I and Z, only 2 values are possible: 1 and -1. Thus, they can be stored as booleans: False refers to -1, while True to 1.
current_node (DiagPauliNode) – The current node in the Pauli tree.
diag_elements (npt.NDArray[np.float64]) – The diagonal elements of the observable.
n (int) – The number of qubits.
monomials (list[PauliStringMonomial]) – A list to store the computed monomials.
progression (Optional[list[int]]) – Tuple of integers storing the index of the current node and the tree size for printing the progression. None if no printing required.
- generate_hadamard(n)[source]
Generates a Hadamard matrix of size n x n.
- Parameters:
n (int) – The size of the Hadamard matrix, must be a power of 2.
- Returns:
The generated Hadamard matrix.
- Raises:
ValueError – If n is not a power of 2.
- Return type:
npt.NDArray[np.int8]
- update_tree(current_node, k, m)[source]
Updates k (indices) and m (values) based on the Pauli type of the current node, and computing coefficients.
- Parameters:
current_node (PauliNode) – The current node in the Pauli tree.
k (list[int]) – A list of column indices where the non-zero elements of the matrix are located.
m (list[bool]) – A list of booleans corresponding to the non-zero coefficients of the matrix indexed in \(k\). False refers to -1, while True to 1.
- update_tree_diagonal_case(current_node, m)[source]
Updates
mbased on the Pauli type of the current node.- Parameters:
current_node (DiagPauliNode) – The current node in the tree.
m (list[bool]) – A list of booleans corresponding to the non-zero coefficients of the matrix indexed in \(k\). False refers to -1, while True to 1.
Pauli Grouping
from mpqp.tools.pauli_grouping import *
- find_qubitwise_rotations(group)[source]
Returns the single qubit rotations to handle multi observables in case of QWC grouping. This function is used in conjunction with the observables grouping, it rotates each qubits into the shared eigenbasis of the elements of the group.
- Returns:
A list of single qubit instructions.
- Parameters:
group (list[TypeAliasForwardRef('PauliStringMonomial')])
- Return type:
list[TypeAliasForwardRef(‘Instruction’)]
- full_commutation_pauli_grouping_ibm_clique(monomials)[source]
- Parameters:
monomials (list[TypeAliasForwardRef('PauliStringMonomial')])
- pauli_grouping_greedy(monomials, type)[source]
Regroups the Pauli operators in parameters into groups of mutual commuting Pauli operators using a greedy approach.
- Parameters:
monomials (list[TypeAliasForwardRef('PauliStringMonomial')]) – list of Pauli monomials to regroup.
type (CommutingTypes) – the commuting type by which we want to group the Pauli operators.
- Returns:
A list of list of Pauli monomials containing the fully commutativity grouping.
- Return type:
list[list[TypeAliasForwardRef(‘PauliStringMonomial’)]]
Examples
>>> pauli_grouping_greedy( ... [pI@pX@pX, pY@pY@pZ, pI@pI@pI, -3*pZ@pY@pX, pY@pX@pY, -pZ@pZ@pY, 2*pX@pX@pY], ... CommutingTypes.FULL, ... ) [[pI@pX@pX, pY@pY@pZ, pI@pI@pI], [-3*pZ@pY@pX, -1*pZ@pZ@pY], [pY@pX@pY], [2*pX@pX@pY]]
- pauli_monomial_eigenvalues(monom)[source]
- Parameters:
monom (PauliStringMonomial)
- Return type:
npt.NDArray[np.float64]
Unitary decomposition
from mpqp.tools.unitary_decomposition import *
This module regroups functions used for decomposition of arbitrary unitary operator into elementary gates regrouped in a quantum circuit.
- quantum_shannon_decomposition(U)[source]
Returns a circuit containing the decomposition of a unitary. The resulting circuit is composed of gates CNOT, Ry and Rz.
The Quantum Shannon Decomposition works by splitting an unitary into 3 matrices using the cosine sine decomposition. Which decompose an unitary matrix into 2 n-sized matrices around a multiplexed Ry gate.
The two other matrices are then decomposed into 2 n-1 sized unitaries surrounding a multiplexed Rz gate.
This process repeats until the matrices are reduced to 1 qubit gates which can be easily split with the ZYZ decomposition.
- Parameters:
U (Matrix) – The unitary matrix to be decomposed
- Returns:
A quantum circuit equivalent to U.
- Return type:
References: .. [1] Mikko Möttönen, Juha J. Vartiainen, Ville Bergholm, and Martti M. Salomaa. 2004. Quantum circuits for general multi-qubit gates. American Physical Society (APS) : 93-13.
Examples
>>> U = np.array([[1,0],[0,1]]) >>> circuit = quantum_shannon_decomposition(U) >>> print(matrix_eq(U, circuit.to_matrix())) True