from __future__ import annotations
import re
from typing import TYPE_CHECKING, Union
import numpy as np
import numpy.typing as npt
if TYPE_CHECKING:
from sympy import Expr, Basic
from typeguard import typechecked
from .generics import Matrix
[docs]@typechecked
def state_vector_ket_shape(sv: npt.NDArray[np.complex64]) -> str:
"""Formats a state vector into its ket format."""
if len(sv.shape) != 1:
raise ValueError(f"Input state {sv} should be a vector (1 dimensional matrix).")
nb_qubits = int(np.log2(len(sv)))
if 2**nb_qubits != len(sv):
raise ValueError(f"Input state {sv} should have a power of 2 size")
return (
" ".join(
f"{with_sign(v)}|{np.binary_repr(i,nb_qubits)}⟩"
for i, v in enumerate(sv)
if v.round(3) != 0
)
)[2:]
# @typechecked
# FIXME: Resolve type-checking errors encountered during test execution.
[docs]def with_sign(val: np.complex64) -> str:
"""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).
Args:
val: The value to be formatted.
Returns:
The formatted value
"""
rounded = _remove_null_imag(val)
if rounded == 1:
return "+ "
if rounded.real == 0:
sign = "+ " if rounded.imag >= 0 else "- "
rounded = _remove_unnecessary_decimals(abs(rounded))
return sign + str(rounded) + "j"
str_rounded = str(rounded)
if str_rounded.startswith("-") or str_rounded.startswith("(-"):
return "- " + str(-rounded)
return "+ " + str_rounded
# @typechecked
# FIXME: Resolve type-checking errors encountered during test execution.
def _remove_null_imag(val: np.complex64) -> np.complex64 | np.float32 | int:
val = np.round(val, 3)
if val.imag != 0:
return val
return _remove_unnecessary_decimals(val.real)
# @typechecked
# FIXME: Resolve type-checking errors encountered during test execution.
def _remove_unnecessary_decimals(val: np.float32 | int) -> np.float32 | int:
val = np.float32(val)
if val.is_integer():
return int(val)
return val
@typechecked
def _unpack_expr(expr: Expr | Basic):
if str(expr).startswith("Expr"):
return _unpack_expr(expr.args[0])
return expr
[docs]@typechecked
def clean_1D_array(
array: list[complex] | npt.NDArray[np.complex64 | np.float32], round: int = 5
) -> str:
"""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.
Args:
array: An array containing numeric elements.
round: precision to round the numbers to.
Returns:
A string representation of the cleaned array.
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]'
"""
return (
"["
+ ", ".join(
clean_number_repr(element, round)
for element in np.array(array, dtype=np.complex64)
)
+ "]"
)
# @typechecked
# FIXME: Resolve type-checking errors encountered during test execution.
[docs]def clean_number_repr(number: complex, round: int = 7):
"""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.
Args:
number: The number to be formatted
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'
"""
real_part = np.round(np.real(number), round)
imag_part = np.round(np.imag(number), round)
if real_part == int(real_part):
real_part = int(real_part)
if imag_part == int(imag_part):
imag_part = int(imag_part)
if real_part == 0 and imag_part != 0:
real_part = ""
if imag_part == 0:
imag_part = ""
else:
imag_part = str(imag_part) + "j"
if real_part != "" and not imag_part.startswith("-"):
imag_part = "+" + imag_part
return f"{str(real_part)}{str(imag_part)}"
# @typechecked
# FIXME: Resolve type-checking errors encountered during test execution.
[docs]def clean_matrix(matrix: Matrix, round: int = 5, align: bool = True):
"""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.
Args:
matrix: An 2d array containing numeric elements.
round: The number of decimal places to round the real and imaginary parts.
align: Whether to align the elements for a cleaner output.
Returns:
A string representation of the cleaned matrix.
Examples:
>>> print(clean_matrix([[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 ]]
"""
formatted_matrix = [
[format_element(element, round) for element in row] for row in matrix
]
if align:
max_lengths = [
max(len(row[i]) for row in formatted_matrix)
for i in range(len(formatted_matrix[0]))
]
formatted_matrix = [
[element.ljust(max_lengths[i]) for i, element in enumerate(row)]
for row in formatted_matrix
]
return (
"["
+ ",\n ".join(["[" + ", ".join(row) + "]" for row in formatted_matrix])
+ "]"
)
# @typechecked
# FIXME: Resolve type-checking errors encountered during test execution.
[docs]def pprint(matrix: Matrix, round: int = 5, align: bool = True):
"""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.
Args:
matrix: A matrix containing numeric elements, possibly including complex numbers.
round: The number of decimal places to round the real and imaginary parts.
align: Whether to align the elements for a cleaner output.
Example:
>>> pprint([[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 ]]
"""
print(clean_matrix(matrix, round, align))
[docs]@typechecked
def one_lined_repr(obj: object):
"""One-liner returning a representation of the given object by removing
extra whitespace.
Args:
obj: The object for which a representation is desired.
"""
return re.sub(r"\s+", " ", repr(obj))