"""Post Processing - Randomized Measure - Wavefunction Overlap V1 - Echo Cell
(:mod:`qurry.process.randomized_measure.wavefunction_overlap_v1.echo_cell`)
"""
import warnings
from typing import Union
import numpy as np
from ...utils import ensemble_cell as ensemble_cell_py, cycling_slice as cycling_slice_py
from ...availability import (
availablility,
default_postprocessing_backend,
PostProcessingBackendLabel,
)
from ...exceptions import (
PostProcessingRustImportError,
PostProcessingRustUnavailableWarning,
)
try:
from ....boorust import randomized # type: ignore
echo_cell_rust_source = randomized.echo_cell_rust
RUST_AVAILABLE = True
FAILED_RUST_IMPORT = None
except ImportError as err:
RUST_AVAILABLE = False
FAILED_RUST_IMPORT = err
def echo_cell_rust_source(*args, **kwargs):
"""Dummy function for cho_cell_rust."""
raise PostProcessingRustImportError(
"Rust is not available, using python to calculate purity cell."
) from FAILED_RUST_IMPORT
BACKEND_AVAILABLE = availablility(
"randomized_measure.wavefunction_overlap_v1.echo_cell",
[
("Rust", RUST_AVAILABLE, FAILED_RUST_IMPORT),
("Cython", "Depr.", None),
],
)
DEFAULT_PROCESS_BACKEND = default_postprocessing_backend(
RUST_AVAILABLE,
False,
)
[docs]
def echo_cell_py(
idx: int,
first_counts: dict[str, int],
second_counts: dict[str, int],
bitstring_range: tuple[int, int],
subsystem_size: int,
) -> tuple[int, np.float64]:
"""Calculate the echo cell, one of overlap, of a subsystem by Python.
Args:
idx (int): Index of the cell (counts).
first_counts (dict[str, int]): Counts measured from the first quantum circuit.
second_counts (dict[str, int]): Counts measured from the second quantum circuit.
bitstring_range (tuple[int, int]): The range of the subsystem.
subsystem_size (int): Subsystem size included.
Returns:
tuple[int, float]: Index, one of overlap purity.
"""
shots = sum(first_counts.values())
shots2 = sum(second_counts.values())
assert shots == shots2, f"shots {shots} does not match shots2 {shots2}"
_dummy_string = list(range(len(list(first_counts.keys())[0])))
_dummy_string2 = list(range(len(list(second_counts.keys())[0])))
assert (
_dummy_string == _dummy_string2
), f"first_counts {first_counts} != second_counts {second_counts}"
if _dummy_string[bitstring_range[0] : bitstring_range[1]] == cycling_slice_py(
_dummy_string, bitstring_range[0], bitstring_range[1], 1
):
first_counts_under_degree = dict.fromkeys(
[k[bitstring_range[0] : bitstring_range[1]] for k in first_counts], 0
)
for bitstring in list(first_counts):
first_counts_under_degree[
bitstring[bitstring_range[0] : bitstring_range[1]]
] += first_counts[bitstring]
second_counts_under_degree = dict.fromkeys(
[k[bitstring_range[0] : bitstring_range[1]] for k in second_counts], 0
)
for bitstring in list(second_counts):
second_counts_under_degree[
bitstring[bitstring_range[0] : bitstring_range[1]]
] += second_counts[bitstring]
else:
first_counts_under_degree = dict.fromkeys(
[cycling_slice_py(k, bitstring_range[0], bitstring_range[1], 1) for k in first_counts],
0,
)
for bitstring in list(first_counts):
first_counts_under_degree[
cycling_slice_py(bitstring, bitstring_range[0], bitstring_range[1], 1)
] += first_counts[bitstring]
second_counts_under_degree = dict.fromkeys(
[cycling_slice_py(k, bitstring_range[0], bitstring_range[1], 1) for k in second_counts],
0,
)
for bitstring in list(second_counts):
second_counts_under_degree[
cycling_slice_py(bitstring, bitstring_range[0], bitstring_range[1], 1)
] += second_counts[bitstring]
_echo_cell = np.float64(0)
for s_i, s_i_meas in first_counts_under_degree.items():
for s_j, s_j_meas in second_counts_under_degree.items():
_echo_cell += ensemble_cell_py(s_i, s_i_meas, s_j, s_j_meas, subsystem_size, shots)
return idx, _echo_cell
[docs]
def echo_cell_rust(
idx: int,
first_counts: dict[str, int],
second_counts: dict[str, int],
bitstring_range: tuple[int, int],
subsystem_size: int,
) -> tuple[int, float]:
"""Calculate the echo cell, one of overlap, of a subsystem by Rust.
Args:
idx (int): Index of the cell (counts).
first_counts (dict[str, int]): Counts measured from the first quantum circuit.
second_counts (dict[str, int]): Counts measured from the second quantum circuit.
bitstring_range (tuple[int, int]): The range of the subsystem.
subsystem_size (int): Subsystem size included.
Returns:
tuple[int, float]: Index, one of overlap purity.
"""
return echo_cell_rust_source(idx, first_counts, second_counts, bitstring_range, subsystem_size)
[docs]
def echo_cell(
idx: int,
first_counts: dict[str, int],
second_counts: dict[str, int],
bitstring_range: tuple[int, int],
subsystem_size: int,
backend: PostProcessingBackendLabel = DEFAULT_PROCESS_BACKEND,
) -> tuple[int, Union[float, np.float64]]:
"""Calculate the echo cell, one of overlap, of a subsystem.
Args:
idx (int): Index of the cell (counts).
first_counts (dict[str, int]): Counts measured from the first quantum circuit.
second_counts (dict[str, int]): Counts measured from the second quantum circuit.
bitstring_range (tuple[int, int]): The range of the subsystem.
subsystem_size (int): Subsystem size included.
Returns:
tuple[int, float]: Index, one of overlap purity.
"""
if not RUST_AVAILABLE and backend == "Rust":
warnings.warn(
"Rust is not available, using Cython or Python to calculate purity cell."
+ f"Check the error: {FAILED_RUST_IMPORT}",
PostProcessingRustUnavailableWarning,
)
backend = "Python"
if backend == "Rust":
return echo_cell_rust(idx, first_counts, second_counts, bitstring_range, subsystem_size)
return echo_cell_py(idx, first_counts, second_counts, bitstring_range, subsystem_size)