Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #41

Closed
wants to merge 13 commits into from
Closed

Dev #41

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions mpqp/core/instruction/measurement/pauli_string.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Represents Pauli strings, which is linear combinations of
:class:`PauliMonomial` with is a combination of :class:`PauliAtom`.
:class:`PauliMonomial` which is a combination of :class:`PauliAtom`.
:class:`PauliString` objects can be added, subtracted, and multiplied by
scalars. They also support matrix multiplication with other :class:`PauliString`
objects.
Expand Down Expand Up @@ -48,7 +48,7 @@ def __init__(self, monomials: Optional[list["PauliStringMonomial"]] = None):

@property
def monomials(self) -> list[PauliStringMonomial]:
"""Get the monomials of the PauliString.
"""Gets the monomials of the PauliString.

Returns:
The list of monomials in the PauliString.
Expand All @@ -57,7 +57,7 @@ def monomials(self) -> list[PauliStringMonomial]:

@property
def nb_qubits(self) -> int:
"""Get the number of qubits associated with the PauliString.
"""Gets the number of qubits associated with the PauliString.

Returns:
The number of qubits associated with the PauliString.
Expand Down Expand Up @@ -139,11 +139,11 @@ def __eq__(self, other: object) -> bool:
return self.to_dict() == other.to_dict()

def simplify(self, inplace: bool = False) -> PauliString:
"""Simplify the PauliString by combining like terms and removing terms
"""Simplifies the PauliString by combining like terms and removing terms
with zero coefficients.

Args:
inplace: If the simplify should change self.
inplace: Indicates if ``simplify`` should update self.

Example:
>>> ps = I @ I - 2 * I @ I + Z @ I - Z @ I
Expand Down Expand Up @@ -175,7 +175,7 @@ def simplify(self, inplace: bool = False) -> PauliString:
return res

def to_matrix(self) -> Matrix:
"""Convert the PauliString to a matrix representation.
"""Converts the PauliString to a matrix representation.

Example:
>>> ps = I + Z
Expand All @@ -195,18 +195,18 @@ def to_matrix(self) -> Matrix:

@classmethod
def from_matrix(cls, matrix: Matrix) -> PauliString:
"""Construct a PauliString from a matrix.
"""Constructs a PauliString from a matrix.

Args:
Matrix from which the PauliString is generated
matrix: Matrix from which the PauliString is generated

Example:
>>> ps = PauliString.from_matrix(np.array([[0, 1], [1, 2]]))
>>> print(ps)
(1+0j)*I + (1+0j)*X + (-1+0j)*Z

Returns:
PauliString: form class PauliString.
PauliString: Pauli string decomposition of the matrix in parameter.

Raises:
ValueError: If the input matrix is not square or its dimensions are not a power of 2.
Expand Down Expand Up @@ -238,15 +238,15 @@ def from_matrix(cls, matrix: Matrix) -> PauliString:
return pauli_list

def to_dict(self) -> dict[str, float]:
"""Convert the PauliString object to a dictionary representation.

Returns:
Dictionary representation of the PauliString object.
"""Converts the PauliString object to a dictionary representation.

Example:
>>> ps = 1 * I @ Z + 2 * I @ I
>>> print(ps.to_dict())
{'II': 2, 'IZ': 1}

Returns:
Dictionary representation of the PauliString object.
"""
self = self.simplify()
dict = {}
Expand Down Expand Up @@ -383,8 +383,8 @@ class PauliStringAtom(PauliStringMonomial):
"""Represents a single Pauli operator acting on a qubit in a Pauli string.

Args:
Label: The label representing the Pauli operator (e.g., 'I', 'X', 'Y', 'Z').
Matrix: The matrix representation of the Pauli operator.
label: The label representing the Pauli operator (e.g., 'I', 'X', 'Y', 'Z').
matrix: The matrix representation of the Pauli operator.

Raises:
AttributeError: new atoms cannot be created
Expand Down
7 changes: 4 additions & 3 deletions mpqp/execution/runner.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from numbers import Complex
from typing import Optional, Union
from typing import Optional, Sequence, Union

import numpy as np
from sympy import Expr
Expand All @@ -20,6 +20,7 @@
from mpqp.execution.providers.ibm import run_ibm, submit_ibmq
from mpqp.execution.result import BatchResult, Result
from mpqp.tools.errors import RemoteExecutionError
from mpqp.tools.generics import OneOrMany


@typechecked
Expand Down Expand Up @@ -147,7 +148,7 @@ def _run_single(
@typechecked
def run(
circuit: QCircuit,
device: AvailableDevice | list[AvailableDevice],
device: OneOrMany[AvailableDevice],
values: Optional[dict[Expr | str, Complex]] = None,
) -> Union[Result, BatchResult]:
"""Runs the circuit on the backend, or list of backend, provided in
Expand Down Expand Up @@ -198,7 +199,7 @@ def run(
if values is None:
values = {}

if isinstance(device, list):
if isinstance(device, Sequence):
# Duplicate devices are removed
set_device = list(set(device))
if len(set_device) == 1:
Expand Down
1 change: 1 addition & 0 deletions mpqp/tools/generics.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typeguard import typechecked

T = TypeVar("T")
OneOrMany = Union[T, Sequence[T]]
ListOrSingle = Union[list[T], T]
"""Type alias for both elements of type ``T``, or list of elements of type ``T``."""
ArbitraryNestedSequence = Union[Sequence["ArbitraryNestedSequence"], T]
Expand Down
78 changes: 77 additions & 1 deletion mpqp/tools/maths.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from __future__ import annotations

import math
from functools import reduce
from numbers import Complex, Real
from typing import TYPE_CHECKING

import numpy as np
import numpy.typing as npt
import sympy as sp
from qiskit import quantum_info
from scipy.linalg import inv, sqrtm
from sympy import Expr, I, pi # pyright: ignore[reportUnusedImport]
from typeguard import typechecked

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

@typechecked
def matrix_eq(lhs: Matrix, rhs: Matrix) -> bool:
r"""Checks whether two matrix are element-wise equal, within a tolerance.
r"""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|)`.
Expand Down Expand Up @@ -182,3 +186,75 @@ def exp(angle: Expr | Complex) -> sp.Expr | complex:
res = sp.exp(angle)
assert isinstance(res, Expr)
return res


def rand_orthogonal_matrix_seed(size: int, seed: int) -> npt.NDArray[np.complex64]:
"""Generate a random orthogonal matrix with a given seed.

Args:
size: Size (number of columns, or rows) of the squared matrix to generate.
seed: Seed used to control the random generation of the matrix.

Returns:
A random orthogonal Matrix.
"""
# TODO: example
np.random.seed(seed)
m = np.random.rand(size, size)
return m.dot(inv(sqrtm(m.T.dot(m))))


def rand_orthogonal_matrix(size: int) -> npt.NDArray[np.complex64]:
"""Generate a random orthogonal matrix without a given seed"""
# TODO: to comment + examples
m = np.random.rand(size, size)
return m.dot(inv(sqrtm(m.T.dot(m))))


def rand_clifford_matrix(nb_qubits: int) -> npt.NDArray[np.complex64]:
"""Generate a random Clifford matrix"""
# TODO: to comment + examples
return quantum_info.random_clifford(
nb_qubits
).to_matrix() # pyright: ignore[reportReturnType]


def rand_unitary_2x2_matrix() -> npt.NDArray[np.complex64]:
"""Generate a random one-qubit unitary matrix"""
# TODO: to comment + examples
theta, phi, gamma = np.random.rand(3) * 2 * math.pi
c, s, eg, ep = (
np.cos(theta / 2),
np.sin(theta / 2),
np.exp(gamma * 1j),
np.exp(phi * 1j),
)
return np.array([[c, -eg * s], [eg * s, eg * ep * c]])


def rand_product_local_unitaries(nb_qubits: int) -> npt.NDArray[np.complex64]:
"""
Generate a random tensor product of unitary matrices

Args:
nb_qubits: Number of qubits on which the product of unitaries will act.
"""
return reduce(
np.kron,
[rand_unitary_2x2_matrix() for _ in range(nb_qubits - 1)],
np.eye(1, dtype=np.complex64),
)


def rand_hermitian_matrix(size: int) -> npt.NDArray[np.complex64]:
"""Generate a random Hermitian matrix.

Args:
size: Size (number of columns, or rows) of the squared matrix to generate.

Returns:
A random orthogonal Matrix.
"""
# TODO: examples
m = np.random.rand(size, size).astype(np.complex64)
return m + m.conjugate().transpose()
4 changes: 3 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ filterwarnings =
ignore:Setting metadata to None.*:DeprecationWarning
ignore:divide by zero.*:RuntimeWarning
ignore:The qiskit.extensions module is pending deprecation.*:PendingDeprecationWarning
ignore:Building a flow controller with keyword arguments is going to be deprecated.*:PendingDeprecationWarning
ignore:Building a flow controller with keyword arguments is going to be deprecated.*:PendingDeprecationWarning
ignore:.*OpenQASM language features that m.*
ignore:.*OpenQASMTranslationWarning.*
Loading