"""EchoListenHadamard - Experiment (:mod:`qurry.qurrech.hadamard_test.experiment`)"""
from typing import Optional, Type, Any
from collections.abc import Hashable
import tqdm
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from .analysis import EchoListenHadamardAnalysis
from .arguments import EchoListenHadamardArguments, SHORT_NAME
from ...qurrium.experiment import ExperimentPrototype, Commonparams
from ...process.utils import qubit_selector
from ...process.hadamard_test import hadamard_overlap_echo as overlap_echo
[docs]
class EchoListenHadamardExperiment(
ExperimentPrototype[EchoListenHadamardArguments, EchoListenHadamardAnalysis]
):
"""The experiment for calculating entangled entropy with more information combined."""
__name__ = "EchoListenHadamardExperiment"
@property
def arguments_instance(self) -> Type[EchoListenHadamardArguments]:
"""The arguments instance for this experiment."""
return EchoListenHadamardArguments
@property
def analysis_instance(self) -> Type[EchoListenHadamardAnalysis]:
"""The analysis instance for this experiment."""
return EchoListenHadamardAnalysis
[docs]
@classmethod
def params_control(
cls,
targets: list[tuple[Hashable, QuantumCircuit]],
exp_name: str = "exps",
degree: Optional[tuple[int, int]] = None,
**custom_kwargs: Any,
) -> tuple[EchoListenHadamardArguments, 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 `'experiment'`.
degree (Optional[tuple[int, int]], optional):
The degree range.
Defaults to None.
custom_kwargs (Any):
The custom parameters.
Raises:
ValueError: The number of target circuits should be 2.
ValueError: If the number of qubits in two circuits is not the same.
Returns:
tuple[EntropyMeasureHadamardArguments, Commonparams, dict[str, Any]]:
The arguments of the experiment, the common parameters, and the custom parameters.
"""
if len(targets) != 2:
raise ValueError("The number of target circuits should be 2.")
target_key_01, target_circuit_01 = targets[0]
num_qubits_01 = target_circuit_01.num_qubits
target_key_02, target_circuit_02 = targets[1]
num_qubits_02 = target_circuit_02.num_qubits
if num_qubits_01 != num_qubits_02:
raise ValueError(
"The number of qubits in two circuits should be the same, "
+ f"but got {target_key_01}: {num_qubits_01} and {target_key_02}: {num_qubits_02}."
)
degree = qubit_selector(num_qubits_01, degree=degree)
exp_name = f"{exp_name}.degree_{degree[0]}_{degree[1]}.{SHORT_NAME}"
# pylint: disable=protected-access
return EchoListenHadamardArguments._filter(
exp_name=exp_name,
target_keys=[target_key_01, target_key_02],
degree=degree,
**custom_kwargs,
)
# pylint: enable=protected-access
[docs]
@classmethod
def method(
cls,
targets: list[tuple[Hashable, QuantumCircuit]],
arguments: EchoListenHadamardArguments,
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 (EchoListenHadamardArguments):
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 arguments of the experiment.
"""
assert isinstance(
arguments.degree, tuple
), f"The degree should be a tuple, got {arguments.degree}."
target_key_01, target_circuit_01 = targets[0]
target_key_01 = "" if isinstance(target_key_01, int) else str(target_key_01)
num_qubits_01 = target_circuit_01.num_qubits
old_name_01 = "" if isinstance(target_circuit_01.name, str) else target_circuit_01.name
target_key_02, target_circuit_02 = targets[1]
target_key_02 = "" if isinstance(target_key_02, int) else str(target_key_02)
num_qubits_02 = target_circuit_02.num_qubits
old_name_02 = "" if isinstance(target_circuit_02.name, str) else target_circuit_02.name
assert (
num_qubits_01 == num_qubits_02
), "The number of qubits in two circuits should be the same."
q_ancilla = QuantumRegister(1, "ancilla_1")
q_func1 = QuantumRegister(num_qubits_01, "q1")
q_func2 = QuantumRegister(num_qubits_01, "q2")
c_meas1 = ClassicalRegister(1, "c1")
qc_exp1 = QuantumCircuit(q_ancilla, q_func1, q_func2, c_meas1)
qc_exp1.name = (
f"{arguments.exp_name}" + ""
if len(target_key_01) < 1 and len(target_key_02) < 1
else (
f".{target_key_01}_{target_key_02}" + ""
if len(old_name_01) < 1 and len(old_name_02) < 1
else f".{old_name_01}_{old_name_02}"
)
)
qc_exp1.compose(
target_circuit_01,
[q_func1[i] for i in range(num_qubits_01)],
inplace=True,
)
qc_exp1.compose(
target_circuit_02,
[q_func2[i] for i in range(num_qubits_01)],
inplace=True,
)
qc_exp1.barrier()
qc_exp1.h(q_ancilla)
for i in range(*arguments.degree):
qc_exp1.cswap(q_ancilla[0], q_func1[i], q_func2[i])
qc_exp1.h(q_ancilla)
qc_exp1.measure(q_ancilla, c_meas1)
return [qc_exp1], {}
[docs]
def analyze(
self,
pbar: Optional[tqdm.tqdm] = None,
) -> EchoListenHadamardAnalysis:
"""Calculate entangled entropy with more information combined.
Args:
degree (Union[tuple[int, int], int]): Degree of the subsystem.
pbar (Optional[tqdm.tqdm], optional):
The progress bar. Defaults to None.
Returns:
dict[str, float]: A dictionary contains
purity, entropy.
"""
if pbar is not None:
pbar.set_description("Calculating wave function overlap")
shots = self.commons.shots
counts = self.afterwards.counts
qs = self.quantities(
shots=shots,
counts=counts,
)
serial = len(self.reports)
analysis = self.analysis_instance(
serial=serial,
shots=shots,
**qs, # type: ignore
)
self.reports[serial] = analysis
return analysis
[docs]
@classmethod
def quantities(
cls,
shots: Optional[int] = None,
counts: Optional[list[dict[str, int]]] = None,
) -> dict[str, float]:
"""Calculate entangled entropy with more information combined.
Args:
shots (int): Shots of the experiment on quantum machine.
counts (list[dict[str, int]]): Counts of the experiment on quantum machine.
Returns:
dict[str, float]: A dictionary contains
purity, entropy.
"""
if shots is None or counts is None:
raise ValueError("shots and counts should be specified.")
return overlap_echo(
shots=shots,
counts=counts,
)