Source code for qbraid.runtime.result

# Copyright (C) 2024 qBraid
#
# This file is part of the qBraid-SDK
#
# The qBraid-SDK is free software released under the GNU General Public License v3
# or later. You can redistribute and/or modify it under the terms of the GPL v3.
# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
#
# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3.

"""
Module defining abstract GateModelJobResult Class

"""
from abc import ABC, abstractmethod
from typing import Any, Optional

import numpy as np


def normalize_measurement_counts(measurements: list[dict[str, int]]) -> list[dict[str, int]]:
    """
    Normalizes measurement count dictionaries to have the same bit length across all keys.

    Args:
        measurements (list[dict[str, int]]): A list of dicts with binary keys and integer values.

    Returns:
        list[dict[str, int]]: A new list of dictionaries with normalized key lengths.
    """
    if len(measurements) == 0:
        return measurements

    max_bit_length = max(len(key) for counts in measurements for key in counts.keys())

    normalized_counts_list = []
    for counts in measurements:
        normalized_counts = {}
        for key, true_value in counts.items():
            normalized_key = key.zfill(max_bit_length)
            normalized_counts[normalized_key] = true_value
        normalized_counts_list.append(normalized_counts)

    return normalized_counts_list


[docs] class QuantumJobResult: """Result of a quantum job. Args: result (optional, Any): Result data """
[docs] def __init__(self, result: Optional[Any] = None): self._result = result
[docs] class GateModelJobResult(ABC, QuantumJobResult): """Abstract interface for gate model quantum job results.""" @abstractmethod def measurements(self) -> np.ndarray: """Return measurements as list""" @abstractmethod def raw_counts(self, **kwargs): """Returns raw histogram data of the run""" @staticmethod def format_counts(counts: dict, include_zero_values: bool = False) -> dict: """Formats, sorts, and adds missing bit indices to counts dictionary Can pass in a 'include_zero_values' parameter to decide whether to include the states with zero counts. For example: .. code-block:: python >>> counts {'1 1': 13, '0 0': 46, '1 0': 79} >>> GateModelJobResult.format_counts(counts) {'00': 46, '10': 79, '11': 13} >>> GateModelJobResult.format_counts(counts, include_zero_values=True) {'00': 46, '01': 0, '10': 79, '11': 13} """ counts = {key.replace(" ", ""): value for key, value in counts.items()} num_bits = max(len(key) for key in counts) all_keys = [format(i, f"0{num_bits}b") for i in range(2**num_bits)] final_counts = {key: counts.get(key, 0) for key in sorted(all_keys)} if not include_zero_values: final_counts = {key: value for key, value in final_counts.items() if value != 0} return final_counts def measurement_counts(self, include_zero_values: bool = False, **kwargs) -> dict: """Returns the sorted histogram data of the run""" raw_counts = self.raw_counts(**kwargs) if isinstance(raw_counts, dict): return self.format_counts(raw_counts, include_zero_values=include_zero_values) batch_counts = [ self.format_counts(counts, include_zero_values=include_zero_values) for counts in raw_counts ] return normalize_measurement_counts(batch_counts)