Skip to content

Commit 47e0e4f

Browse files
Merge branch 'dev' of https://github.com/ColibrITD-SAS/mpqp into dev
2 parents ff46020 + eac73e2 commit 47e0e4f

File tree

7 files changed

+380
-358
lines changed

7 files changed

+380
-358
lines changed

mpqp/core/instruction/measurement/pauli_string.py

+15-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Represents Pauli strings, which is linear combinations of
2-
:class:`PauliMonomial` with is a combination of :class:`PauliAtom`.
2+
:class:`PauliMonomial` which is a combination of :class:`PauliAtom`.
33
:class:`PauliString` objects can be added, subtracted, and multiplied by
44
scalars. They also support matrix multiplication with other :class:`PauliString`
55
objects.
@@ -48,7 +48,7 @@ def __init__(self, monomials: Optional[list["PauliStringMonomial"]] = None):
4848

4949
@property
5050
def monomials(self) -> list[PauliStringMonomial]:
51-
"""Get the monomials of the PauliString.
51+
"""Gets the monomials of the PauliString.
5252
5353
Returns:
5454
The list of monomials in the PauliString.
@@ -57,7 +57,7 @@ def monomials(self) -> list[PauliStringMonomial]:
5757

5858
@property
5959
def nb_qubits(self) -> int:
60-
"""Get the number of qubits associated with the PauliString.
60+
"""Gets the number of qubits associated with the PauliString.
6161
6262
Returns:
6363
The number of qubits associated with the PauliString.
@@ -139,11 +139,11 @@ def __eq__(self, other: object) -> bool:
139139
return self.to_dict() == other.to_dict()
140140

141141
def simplify(self, inplace: bool = False) -> PauliString:
142-
"""Simplify the PauliString by combining like terms and removing terms
142+
"""Simplifies the PauliString by combining like terms and removing terms
143143
with zero coefficients.
144144
145145
Args:
146-
inplace: If the simplify should change self.
146+
inplace: Indicates if ``simplify`` should update self.
147147
148148
Example:
149149
>>> ps = I @ I - 2 * I @ I + Z @ I - Z @ I
@@ -175,7 +175,7 @@ def simplify(self, inplace: bool = False) -> PauliString:
175175
return res
176176

177177
def to_matrix(self) -> Matrix:
178-
"""Convert the PauliString to a matrix representation.
178+
"""Converts the PauliString to a matrix representation.
179179
180180
Example:
181181
>>> ps = I + Z
@@ -195,18 +195,18 @@ def to_matrix(self) -> Matrix:
195195

196196
@classmethod
197197
def from_matrix(cls, matrix: Matrix) -> PauliString:
198-
"""Construct a PauliString from a matrix.
198+
"""Constructs a PauliString from a matrix.
199199
200200
Args:
201-
Matrix from which the PauliString is generated
201+
matrix: Matrix from which the PauliString is generated
202202
203203
Example:
204204
>>> ps = PauliString.from_matrix(np.array([[0, 1], [1, 2]]))
205205
>>> print(ps)
206206
(1+0j)*I + (1+0j)*X + (-1+0j)*Z
207207
208208
Returns:
209-
PauliString: form class PauliString.
209+
PauliString: Pauli string decomposition of the matrix in parameter.
210210
211211
Raises:
212212
ValueError: If the input matrix is not square or its dimensions are not a power of 2.
@@ -238,15 +238,15 @@ def from_matrix(cls, matrix: Matrix) -> PauliString:
238238
return pauli_list
239239

240240
def to_dict(self) -> dict[str, float]:
241-
"""Convert the PauliString object to a dictionary representation.
242-
243-
Returns:
244-
Dictionary representation of the PauliString object.
241+
"""Converts the PauliString object to a dictionary representation.
245242
246243
Example:
247244
>>> ps = 1 * I @ Z + 2 * I @ I
248245
>>> print(ps.to_dict())
249246
{'II': 2, 'IZ': 1}
247+
248+
Returns:
249+
Dictionary representation of the PauliString object.
250250
"""
251251
self = self.simplify()
252252
dict = {}
@@ -383,8 +383,8 @@ class PauliStringAtom(PauliStringMonomial):
383383
"""Represents a single Pauli operator acting on a qubit in a Pauli string.
384384
385385
Args:
386-
Label: The label representing the Pauli operator (e.g., 'I', 'X', 'Y', 'Z').
387-
Matrix: The matrix representation of the Pauli operator.
386+
label: The label representing the Pauli operator (e.g., 'I', 'X', 'Y', 'Z').
387+
matrix: The matrix representation of the Pauli operator.
388388
389389
Raises:
390390
AttributeError: new atoms cannot be created

mpqp/execution/runner.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from numbers import Complex
4-
from typing import Optional, Union
4+
from typing import Optional, Sequence, Union
55

66
import numpy as np
77
from sympy import Expr
@@ -20,6 +20,7 @@
2020
from mpqp.execution.providers.ibm import run_ibm, submit_ibmq
2121
from mpqp.execution.result import BatchResult, Result
2222
from mpqp.tools.errors import RemoteExecutionError
23+
from mpqp.tools.generics import OneOrMany
2324

2425

2526
@typechecked
@@ -147,7 +148,7 @@ def _run_single(
147148
@typechecked
148149
def run(
149150
circuit: QCircuit,
150-
device: AvailableDevice | list[AvailableDevice],
151+
device: OneOrMany[AvailableDevice],
151152
values: Optional[dict[Expr | str, Complex]] = None,
152153
) -> Union[Result, BatchResult]:
153154
"""Runs the circuit on the backend, or list of backend, provided in
@@ -198,7 +199,7 @@ def run(
198199
if values is None:
199200
values = {}
200201

201-
if isinstance(device, list):
202+
if isinstance(device, Sequence):
202203
# Duplicate devices are removed
203204
set_device = list(set(device))
204205
if len(set_device) == 1:

mpqp/tools/generics.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from typeguard import typechecked
99

1010
T = TypeVar("T")
11+
OneOrMany = Union[T, Sequence[T]]
1112
ListOrSingle = Union[list[T], T]
1213
"""Type alias for both elements of type ``T``, or list of elements of type ``T``."""
1314
ArbitraryNestedSequence = Union[Sequence["ArbitraryNestedSequence"], T]

mpqp/tools/maths.py

+77-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
from __future__ import annotations
22

3+
import math
4+
from functools import reduce
35
from numbers import Complex, Real
46
from typing import TYPE_CHECKING
57

68
import numpy as np
79
import numpy.typing as npt
810
import sympy as sp
11+
from qiskit import quantum_info
12+
from scipy.linalg import inv, sqrtm
913
from sympy import Expr, I, pi # pyright: ignore[reportUnusedImport]
1014
from typeguard import typechecked
1115

@@ -41,7 +45,7 @@ def normalize(v: npt.NDArray[np.complex64]) -> npt.NDArray[np.complex64]:
4145

4246
@typechecked
4347
def matrix_eq(lhs: Matrix, rhs: Matrix) -> bool:
44-
r"""Checks whether two matrix are element-wise equal, within a tolerance.
48+
r"""Checks whether two matrix (including vectors) are element-wise equal, within a tolerance.
4549
4650
For respectively each elements `a` and `b` of both inputs, we check this
4751
specific condition: `|a - b| \leq (atol + rtol * |b|)`.
@@ -182,3 +186,75 @@ def exp(angle: Expr | Complex) -> sp.Expr | complex:
182186
res = sp.exp(angle)
183187
assert isinstance(res, Expr)
184188
return res
189+
190+
191+
def rand_orthogonal_matrix_seed(size: int, seed: int) -> npt.NDArray[np.complex64]:
192+
"""Generate a random orthogonal matrix with a given seed.
193+
194+
Args:
195+
size: Size (number of columns, or rows) of the squared matrix to generate.
196+
seed: Seed used to control the random generation of the matrix.
197+
198+
Returns:
199+
A random orthogonal Matrix.
200+
"""
201+
# TODO: example
202+
np.random.seed(seed)
203+
m = np.random.rand(size, size)
204+
return m.dot(inv(sqrtm(m.T.dot(m))))
205+
206+
207+
def rand_orthogonal_matrix(size: int) -> npt.NDArray[np.complex64]:
208+
"""Generate a random orthogonal matrix without a given seed"""
209+
# TODO: to comment + examples
210+
m = np.random.rand(size, size)
211+
return m.dot(inv(sqrtm(m.T.dot(m))))
212+
213+
214+
def rand_clifford_matrix(nb_qubits: int) -> npt.NDArray[np.complex64]:
215+
"""Generate a random Clifford matrix"""
216+
# TODO: to comment + examples
217+
return quantum_info.random_clifford(
218+
nb_qubits
219+
).to_matrix() # pyright: ignore[reportReturnType]
220+
221+
222+
def rand_unitary_2x2_matrix() -> npt.NDArray[np.complex64]:
223+
"""Generate a random one-qubit unitary matrix"""
224+
# TODO: to comment + examples
225+
theta, phi, gamma = np.random.rand(3) * 2 * math.pi
226+
c, s, eg, ep = (
227+
np.cos(theta / 2),
228+
np.sin(theta / 2),
229+
np.exp(gamma * 1j),
230+
np.exp(phi * 1j),
231+
)
232+
return np.array([[c, -eg * s], [eg * s, eg * ep * c]])
233+
234+
235+
def rand_product_local_unitaries(nb_qubits: int) -> npt.NDArray[np.complex64]:
236+
"""
237+
Generate a random tensor product of unitary matrices
238+
239+
Args:
240+
nb_qubits: Number of qubits on which the product of unitaries will act.
241+
"""
242+
return reduce(
243+
np.kron,
244+
[rand_unitary_2x2_matrix() for _ in range(nb_qubits - 1)],
245+
np.eye(1, dtype=np.complex64),
246+
)
247+
248+
249+
def rand_hermitian_matrix(size: int) -> npt.NDArray[np.complex64]:
250+
"""Generate a random Hermitian matrix.
251+
252+
Args:
253+
size: Size (number of columns, or rows) of the squared matrix to generate.
254+
255+
Returns:
256+
A random orthogonal Matrix.
257+
"""
258+
# TODO: examples
259+
m = np.random.rand(size, size).astype(np.complex64)
260+
return m + m.conjugate().transpose()

pytest.ini

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ filterwarnings =
1414
ignore:Setting metadata to None.*:DeprecationWarning
1515
ignore:divide by zero.*:RuntimeWarning
1616
ignore:The qiskit.extensions module is pending deprecation.*:PendingDeprecationWarning
17-
ignore:Building a flow controller with keyword arguments is going to be deprecated.*:PendingDeprecationWarning
17+
ignore:Building a flow controller with keyword arguments is going to be deprecated.*:PendingDeprecationWarning
18+
ignore:.*OpenQASM language features that m.*
19+
ignore:.*OpenQASMTranslationWarning.*

0 commit comments

Comments
 (0)