'''Result Adapter
Utility functions for converting and normalizing quantum measurement results.
'''
__all__ = ["shots2prob", "kv2list", "list2kv", "normalize_result", "QASMResultAdapter"]
from copy import deepcopy
import numpy as np
from typing import Dict, List, Optional, Union
[docs]
def shots2prob(measured_result : Dict[str, int],
total_shots = None):
"""Convert a shot-counts dict to a probability distribution.
Args:
measured_result (Dict[str, int]): Measurement counts, e.g. ``{'00': 512, '11': 488}``.
total_shots (int, optional): Total number of shots. If not provided,
it is inferred by summing the values of *measured_result*.
Returns:
Dict[str, float]: Probability dict where each count is divided by *total_shots*.
"""
if not total_shots:
total_shots = np.sum(list(measured_result.values()))
return {k : measured_result[k] / total_shots for k in measured_result}
[docs]
def list2kv(data: List[str]) -> Dict[str, int]:
'''Convert a measurement result list to a key-value frequency dict.
Args:
data (List[str]): A list of measurement outcome strings,
e.g. ``['00', '01', '10', '00', '11', '00']``.
Returns:
Dict[str, int]: Frequency dict where keys are outcome strings
and values are occurrence counts. Returns ``{}`` for empty input.
'''
result: Dict[str, int] = {}
for item in data:
result[item] = result.get(item, 0) + 1
return result
[docs]
def normalize_result(data: Union[Dict[str, int], List[str]]) -> Dict[str, float]:
'''Normalize measurement results to a probability distribution.
Accepts either a frequency dict or a raw list of outcome strings.
List input is first converted via :func:`list2kv`.
Returns a dict whose values sum to 1.0.
Returns ``{}`` for empty input.
Args:
data (Union[Dict[str, int], List[str]]): Measurement results as
a frequency dict ``{'00': 3, '01': 1, ...}`` or a raw list
``['00', '01', '10', ...]``.
Returns:
Dict[str, float]: Probability distribution dict with values
summing to 1.0.
'''
if isinstance(data, list):
kv = list2kv(data)
else:
kv = data
if not kv:
return {}
total = sum(kv.values())
return {k: v / total for k, v in kv.items()}
[docs]
def kv2list(kv_result : dict, guessed_qubit_num):
"""Convert a key-value result dict to a flat list indexed by integer keys.
The list has length ``2 ** guessed_qubit_num`` and is indexed by the
integer representation of the measurement outcome.
Args:
kv_result (dict): Key-value result dict, e.g. ``{0: 0.1, 3: 0.9}``.
Keys must be integers.
guessed_qubit_num (int): Number of qubits, used to determine the
output list length (``2 ** guessed_qubit_num``).
Returns:
list: Flat list where ``ret[k]`` holds the value for outcome ``k``.
"""
ret = [0] * (2 ** guessed_qubit_num)
for k in kv_result:
ret[k] = kv_result[k]
return ret
[docs]
class QASMResultAdapter:
"""Adapter for QASM Simulator results, converting raw output to a
standardized analysis-ready format.
Takes a raw measurement counts dict from a QASM simulator and produces
an :class:`AnalysisResult`-compatible object containing counts,
probabilities, and metadata.
The output can be passed directly to :mod:`uniqc.analyzer.draw`
visualization functions.
Args:
counts: Raw measurement counts, e.g. ``{"00": 512, "11": 488}``.
shots: Total number of shots. If not provided, inferred from counts.
metadata: Optional metadata dict (e.g. simulator type, circuit info).
Attributes:
counts (Dict[str, int]): Original measurement counts.
probabilities (Dict[str, float]): Normalized probability distribution.
shots (int): Total number of shots.
metadata (dict): Simulation metadata.
Example:
>>> adapter = QASMResultAdapter(
... counts={"00": 512, "11": 488},
... metadata={"simulator": "qasm_simulator"},
... )
>>> adapter.probabilities
{'00': 0.512, '11': 0.488}
>>> adapter.shots
1000
"""
def __init__(
self,
counts: Dict[str, int],
shots: Optional[int] = None,
metadata: Optional[dict] = None,
):
self.counts: Dict[str, int] = dict(counts)
self.shots: int = shots if shots is not None else sum(self.counts.values())
self.metadata: dict = metadata if metadata is not None else {}
self.metadata.setdefault("simulator", "qasm_simulator")
self.metadata.setdefault("shots", self.shots)
self.probabilities: Dict[str, float] = normalize_result(self.counts)
def __repr__(self) -> str:
return (
f"QASMResultAdapter(shots={self.shots}, "
f"outcomes={len(self.counts)}, "
f"metadata={self.metadata})"
)
[docs]
def to_dict(self) -> dict:
"""Convert to a plain dict for serialization.
Returns:
dict with keys ``counts``, ``probabilities``, ``shots``, ``metadata``.
"""
return {
"counts": self.counts,
"probabilities": self.probabilities,
"shots": self.shots,
"metadata": self.metadata,
}