"""Post Processing - Randomized Measure - Entangled Entropy V1 - Purity Cell
(:mod:`qurry.process.randomized_measure.entangled_entropy_v1.purity_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,
PostProcessingBackendDeprecatedWarning,
)
try:
# Proven import point for rust modules
# from ..boorust.randomized import ( # type: ignore
# purity_cell_rust as purity_cell_rust_source, # type: ignore
# entangled_entropy_core_rust as entangled_entropy_core_rust_source, # type: ignore
# )
from ....boorust import randomized # type: ignore
purity_cell_rust_source = randomized.purity_cell_rust
RUST_AVAILABLE = True
FAILED_RUST_IMPORT = None
except ImportError as err:
RUST_AVAILABLE = False
FAILED_RUST_IMPORT = err
def purity_cell_rust_source(*args, **kwargs):
"""Dummy function for purity_cell_rust."""
raise PostProcessingRustImportError(
"Rust is not available, using python to calculate purity cell."
) from FAILED_RUST_IMPORT
BACKEND_AVAILABLE = availablility(
"randomized_measure.entangle_entropy_v1.purity_cell",
[
("Rust", RUST_AVAILABLE, FAILED_RUST_IMPORT),
("Cython", "Depr.", None),
],
)
DEFAULT_PROCESS_BACKEND = default_postprocessing_backend(RUST_AVAILABLE, False)
# Randomized measure
[docs]
def purity_cell_py(
idx: int,
single_counts: dict[str, int],
bitstring_range: tuple[int, int],
subsystem_size: int,
) -> tuple[int, np.float64]:
"""Calculate the purity cell, one of overlap, of a subsystem by Python.
Args:
idx (int): Index of the cell (counts).
single_counts (dict[str, int]): Counts measured by the single 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(single_counts.values())
_dummy_string = list(range(len(list(single_counts.keys())[0])))
if _dummy_string[bitstring_range[0] : bitstring_range[1]] == cycling_slice_py(
_dummy_string, bitstring_range[0], bitstring_range[1], 1
):
single_counts_under_degree = dict.fromkeys(
[k[bitstring_range[0] : bitstring_range[1]] for k in single_counts], 0
)
for bitstring in list(single_counts):
single_counts_under_degree[
bitstring[bitstring_range[0] : bitstring_range[1]]
] += single_counts[bitstring]
else:
single_counts_under_degree = dict.fromkeys(
[cycling_slice_py(k, bitstring_range[0], bitstring_range[1], 1) for k in single_counts],
0,
)
for bitstring in list(single_counts):
single_counts_under_degree[
cycling_slice_py(bitstring, bitstring_range[0], bitstring_range[1], 1)
] += single_counts[bitstring]
_purity_cell = np.float64(0)
for s_ai, s_ai_meas in single_counts_under_degree.items():
for s_aj, s_aj_meas in single_counts_under_degree.items():
_purity_cell += ensemble_cell_py(
s_ai, s_ai_meas, s_aj, s_aj_meas, subsystem_size, shots
)
return idx, _purity_cell
[docs]
def purity_cell_rust(
idx: int,
single_counts: dict[str, int],
bitstring_range: tuple[int, int],
subsystem_size: int,
) -> tuple[int, float]:
"""Calculate the purity cell, one of overlap, of a subsystem by Rust.
Args:
idx (int): Index of the cell (counts).
single_counts (dict[str, int]): Counts measured by the single 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 purity_cell_rust_source(idx, single_counts, bitstring_range, subsystem_size)
[docs]
def purity_cell(
idx: int,
single_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 purity cell, one of overlap, of a subsystem.
Args:
idx (int): Index of the cell (counts).
single_counts (dict[str, int]): Counts measured by the single quantum circuit.
bitstring_range (tuple[int, int]): The range of the subsystem.
subsystem_size (int): Subsystem size included.
backend (ExistingProcessBackendLabel, optional):
Backend for the process. Defaults to DEFAULT_PROCESS_BACKEND.
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 == "Cython":
warnings.warn(
"Cython backend is deprecated, using Python or Rust to calculate purity cell.",
PostProcessingBackendDeprecatedWarning,
)
backend = DEFAULT_PROCESS_BACKEND
if backend == "Rust":
return purity_cell_rust(idx, single_counts, bitstring_range, subsystem_size)
return purity_cell_py(idx, single_counts, bitstring_range, subsystem_size)