"""StringOperator - Experiment (:mod:`qurry.qurries.string_operator.experiment`)"""
from collections.abc import Hashable
from typing import Optional, Type, Any
import tqdm
from qiskit import QuantumCircuit
from .analysis import StringOperatorAnalysis
from .arguments import StringOperatorArguments, SHORT_NAME
from .utils import circuit_method, StringOperatorLibType, StringOperatorDirection, STRING_OPERATOR
from ...qurrium.experiment import ExperimentPrototype, Commonparams
from ...process.string_operator.string_operator import (
string_operator_order,
StringOperator,
DEFAULT_PROCESS_BACKEND,
PostProcessingBackendLabel,
)
from ...tools import set_pbar_description
[docs]
class StringOperatorExperiment(
ExperimentPrototype[
StringOperatorArguments,
StringOperatorAnalysis,
]
):
"""The instance of experiment."""
__name__ = "EntropyMeasureRandomizedExperiment"
short_name = SHORT_NAME
@property
def arguments_instance(self) -> Type[StringOperatorArguments]:
"""The arguments instance for this experiment."""
return StringOperatorArguments
@property
def analysis_instance(self) -> Type[StringOperatorAnalysis]:
"""The analysis instance for this experiment."""
return StringOperatorAnalysis
[docs]
@classmethod
def params_control(
cls,
targets: list[tuple[Hashable, QuantumCircuit]],
exp_name: str = "exps",
i: Optional[int] = None,
k: Optional[int] = None,
str_op: StringOperatorLibType = "i",
on_dir: StringOperatorDirection = "x",
**custom_kwargs: Any,
) -> tuple[StringOperatorArguments, Commonparams, dict[str, Any]]:
"""Handling all arguments and initializing a single experiment.
Args:
targets (list[tuple[Hashable, QuantumCircuit]]):
The circuits of the experiment.
exp_name (str, optional):
The name of the experiment.
Naming this experiment to recognize it when the jobs are pending to IBMQ Service.
This name is also used for creating a folder to store the exports.
Defaults to `'exps'`.
i (Optional[int], optional):
The index of beginning qubits in the quantum circuit.
k (Optional[int], optional):
The index of ending qubits in the quantum circuit.
str_op (StringOperatorLibType, optional):
The string operator. Defaults to "i".
on_dir (StringOperatorDirection, optional):
The direction of the string operator, either 'x' or 'y'. Defaults to "x".
custom_kwargs (Any):
The custom parameters.
Returns:
tuple[StringOperatorArguments, Commonparams, dict[str, Any]]:
The arguments of the experiment, the common parameters, and the custom parameters.
"""
if len(targets) > 1:
raise ValueError("The number of target circuits should be only one.")
target_key, target_circuit = targets[0]
num_qubits = target_circuit.num_qubits
if on_dir not in STRING_OPERATOR:
raise ValueError("The `on_dir` must be either 'x' or 'y'.")
if str_op not in STRING_OPERATOR[on_dir]:
raise ValueError(f"The `str_op` must be one of {list(STRING_OPERATOR[on_dir])}.")
if k is None:
k = num_qubits - 1
if i is None:
i = 0
if i >= k:
raise ValueError(f"i: {i} is not less than k: {k}.")
if k - i + 1 < len(STRING_OPERATOR[on_dir][str_op]):
raise ValueError(
f"The `k - i + 1` must be greater than or equal to "
f"{len(STRING_OPERATOR[on_dir][str_op])}. But got k: {k} - i: {i} = {k - i + 1}."
)
# pylint: disable=protected-access
return StringOperatorArguments._filter(
exp_name=f"{exp_name}.i_{i}_k_{k}.op_{str_op}_dir_{on_dir}.{SHORT_NAME}",
target_keys=[target_key],
num_qubits=num_qubits,
str_op=str_op,
i=i,
k=k,
**custom_kwargs,
)
# pylint: enable=protected-access
[docs]
@classmethod
def method(
cls,
targets: list[tuple[Hashable, QuantumCircuit]],
arguments: StringOperatorArguments,
pbar: Optional[tqdm.tqdm] = None,
multiprocess: bool = True,
) -> tuple[list[QuantumCircuit], dict[str, Any]]:
"""The method to construct circuit.
Args:
targets (list[tuple[Hashable, QuantumCircuit]]):
The circuits of the experiment.
arguments (StringOperatorArguments):
The arguments of the experiment.
pbar (Optional[tqdm.tqdm], optional):
The progress bar for showing the progress of the experiment. Defaults to None.
multiprocess (bool, optional):
Whether to use multiprocessing. Defaults to `True`.
Returns:
tuple[list[QuantumCircuit], dict[str, Any]]:
The circuits of the experiment and the side products.
"""
set_pbar_description(pbar, f"Prepare permutation for {arguments.num_qubits} qubits.")
target_key, target_circuit = targets[0]
target_key = "" if isinstance(target_key, int) else str(target_key)
assert arguments.i is not None and arguments.k is not None, (
f"i and k should be given, but got {arguments.i} and {arguments.k}. "
"Please check the arguments."
)
return [
circuit_method(
target_circuit,
target_key,
arguments.i,
arguments.k,
arguments.str_op,
arguments.on_dir,
)
], {}
[docs]
def analyze(self, pbar: Optional[tqdm.tqdm] = None) -> StringOperatorAnalysis:
"""Calculate magnet square with more information combined.
Args:
pbar (Optional[tqdm.tqdm], optional):
The progress bar. Defaults to None.
Returns:
StringOperatorAnalysis: The result of the analysis.
"""
qs = self.quantities(shots=self.commons.shots, counts=self.afterwards.counts, pbar=pbar)
serial = len(self.reports)
analysis = self.analysis_instance(
i=self.args.i,
k=self.args.k,
length=self.args.k - self.args.i + 1,
str_op=self.args.str_op,
on_dir=self.args.on_dir,
num_qubits=self.args.num_qubits,
shots=self.commons.shots,
serial=serial,
**qs,
)
assert analysis.content.k - analysis.content.i + 1 == analysis.content.length, (
f"Length of the string operator should be equal to k - i + 1, "
f"but got length: {analysis.content.length} != "
f"k - i + 1: {analysis.content.k - analysis.content.i + 1}."
)
self.reports[serial] = analysis
return analysis
[docs]
@classmethod
def quantities(
cls,
shots: Optional[int] = None,
counts: Optional[list[dict[str, int]]] = None,
backend: PostProcessingBackendLabel = DEFAULT_PROCESS_BACKEND,
pbar: Optional[tqdm.tqdm] = None,
) -> StringOperator:
"""Calculate the string operator.
Args:
shots (int):
The number of shots.
counts (list[dict[str, int]]):
The counts of the experiment.
backend (PostProcessingBackendLabel, optional):
The backend label. Defaults to DEFAULT_PROCESS_BACKEND.
pbar (Optional[tqdm.tqdm], optional):
The progress bar. Defaults to None.
Returns:
StringOperator: The result of the magnet square.
"""
if shots is None:
raise ValueError("The number of shots should be given.")
if counts is None:
raise ValueError("The counts should be given.")
return string_operator_order(
shots=shots,
counts=counts,
backend=backend,
pbar=pbar,
)