"""ToolKits for Random Unitary Operator (:mod:`qurry.qurrium.utils.random_unitary`)"""
from typing import Union, Optional
from collections.abc import Sequence
import numpy as np
SeedType = Union[int, np.random.Generator]
[docs]
def generate_seeds_for_single_circ(
seed_for_single_circ: Optional[SeedType],
num_qubits: int,
) -> dict[int, int]:
"""Generate the seed for single circuit.
Args:
seed_for_single_circ (Optional[SeedType]): The seed for single circuit.
num_qubits (int): The number of qubits.
Raises:
ValueError: If the seed is not int, np.random.Generator
Returns:
dict[int, int]:
The seed for single circuit.
"""
if seed_for_single_circ is None:
return {j: np.random.default_rng().integers(0, 2**32, dtype=int) for j in range(num_qubits)}
if isinstance(seed_for_single_circ, int):
return {j: seed_for_single_circ for j in range(num_qubits)}
if isinstance(seed_for_single_circ, np.random.Generator):
return {j: seed_for_single_circ.integers(0, 2**32, dtype=int) for j in range(num_qubits)}
raise ValueError(
"The seed is not int, np.random.Generator, " + f"but {type(seed_for_single_circ)}"
)
[docs]
def check_and_generate_for_single_circ(
seed_for_single_circ: Optional[Union[SeedType, Sequence[SeedType], dict[int, SeedType]]],
num_qubits: int,
) -> dict[int, int]:
"""Check the input of :func:`generate_random_unitary_seeds`.
Args:
seed_for_single_circ (Optional[Union[SeedType, Sequence[SeedType], dict[int, SeedType]]]):
The seed for single circuit.
num_qubits (int):
The number of qubits.
Raises:
TypeError:
If the seed is not int, :class:`~numpy.random.Generator`,
:class:`~typing.Sequence`, or :class:`dict`.
ValueError:
If the seed for one qubit is not found.
If the seed is not int or :class:`~numpy.random.Generator`.
If the length of seed is not equal to num_qubits.
Returns:
dict[int, int]:
The seed for single circuit.
"""
if seed_for_single_circ is None or isinstance(seed_for_single_circ, (int, np.random.Generator)):
return generate_seeds_for_single_circ(seed_for_single_circ, num_qubits)
if not isinstance(seed_for_single_circ, (Sequence, dict)):
raise TypeError(
"The seed must be int, numpy.random.Generator, Sequence, or dict, "
+ f"not {type(seed_for_single_circ)}"
)
if len(seed_for_single_circ) != num_qubits:
raise ValueError(
"The length of seed must be equal to num_qubits: "
+ f"{len(seed_for_single_circ)} != {num_qubits}."
)
invalids = {}
single_seed = {}
for j in range(num_qubits):
seed_for_one_qubit = (
seed_for_single_circ.get(j, None)
if isinstance(seed_for_single_circ, dict)
else seed_for_single_circ[j]
)
if seed_for_one_qubit is None:
raise ValueError(f"The seed for qubit {j} is not found.")
if isinstance(seed_for_one_qubit, int):
single_seed[j] = seed_for_one_qubit
elif isinstance(seed_for_one_qubit, np.random.Generator):
single_seed[j] = seed_for_one_qubit.integers(0, 2**32, dtype=int)
else:
invalids[j] = (seed_for_one_qubit, type(seed_for_one_qubit))
if invalids:
raise TypeError(
f"The seed must be int or np.random.Generator, but some of them are: {invalids}."
)
if len(single_seed) != num_qubits:
raise ValueError(
"The length of seed must be equal to num_qubits: "
+ f"{len(single_seed)} != {num_qubits}."
)
return single_seed
[docs]
def generate_random_unitary_seeds(
times: int,
num_qubits: int,
seed: Optional[
Union[
SeedType,
Sequence[Union[SeedType, Sequence[SeedType], dict[int, SeedType]]],
dict[int, Union[SeedType, Sequence[SeedType], dict[int, SeedType]]],
]
] = None,
) -> dict[int, dict[int, int]]:
"""Generate random unitary seeds.
Args:
times (int): The number of random unitary operator.
num_qubits (int): The number of qubits.
seed (Union[int, np.random.Generator, \
Sequence[Union[\
int, np.random.Generator, Sequence[Union[int, np.random.Generator]], \
dict[int, Union[int, np.random.Generator]]\
]], \
dict[int, Union[\
int, np.random.Generator, Sequence[Union[int, np.random.Generator]], \
dict[int, Union[int, np.random.Generator]]\
]]], optional):
The seed of random generator.
Raises:
TypeError:
If the seed is not int, np.random.Generator, Sequence, or dict.
ValueError:
If the length of seed is not equal to times.
Returns:
dict[int, dict[int, int]]]: The random unitary seeds.
"""
if seed is None or isinstance(seed, (int, np.random.Generator)):
return {i: generate_seeds_for_single_circ(seed, num_qubits) for i in range(times)}
if not isinstance(seed, (Sequence, dict)):
raise TypeError(
"The seed must be int, np.random.Generator, Sequence, or dict, " + f"not {type(seed)}"
)
if len(seed) != times:
raise ValueError("The length of seed must be equal to times: " + f"{len(seed)} != {times}.")
invalids = {}
single_seed = {}
for i in range(times):
seed_for_one_circ = seed.get(i, None) if isinstance(seed, dict) else seed[i]
try:
single_seed[i] = check_and_generate_for_single_circ(seed_for_one_circ, num_qubits)
except (TypeError, ValueError) as e:
invalids[i] = e
if invalids:
raise TypeError(
"The seed must be int, np.random.Generator, Sequence, or dict, "
+ f"but some of them are: {invalids}."
)
return single_seed