# Copyright 2019-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from __future__ import annotations
import json
from dataclasses import dataclass
from typing import Any, Counter, Dict
import numpy as np
[docs]@dataclass
class GateModelQuantumTaskResult:
"""
Result of a gate model quantum task execution. This class is intended
to be initialized by a QuantumTask class.
Args:
measurements (numpy.ndarray): 2d array - row is shot, column is qubit.
measurement_counts (Counter): A Counter of measurements. Key is the measurements
in a big endian binary string. Value is the number of times that measurement occurred.
measurement_probabilities (Dict[str, float]): A dictionary of probabilistic results.
Key is the measurements in a big endian binary string.
Value is the probability the measurement occurred.
measurements_copied_from_device (bool): flag whether `measurements` were copied from device.
If false, `measurements` are calculated from device data.
measurement_counts_copied_from_device (bool): flag whether `measurement_counts` were copied
from device. If false, `measurement_counts` are calculated from device data.
measurement_probabilities_copied_from_device (bool): flag whether
`measurement_probabilities` were copied from device. If false,
`measurement_probabilities` are calculated from device data.
task_metadata (Dict[str, Any]): Dictionary of task metadata.
state_vector (Dict[str, complex]): Dictionary where Key is state and Value is amplitude.
"""
measurements: np.ndarray
measurement_counts: Counter
measurement_probabilities: Dict[str, float]
measurements_copied_from_device: bool
measurement_counts_copied_from_device: bool
measurement_probabilities_copied_from_device: bool
task_metadata: Dict[str, Any]
state_vector: Dict[str, complex] = None
def __eq__(self, other) -> bool:
if isinstance(other, GateModelQuantumTaskResult):
# __eq__ on numpy arrays results in an array of booleans and therefore can't use
# the default dataclass __eq__ implementation. Override equals to check if all
# elements in the array are equal.
self_fields = (self.task_metadata, self.state_vector)
other_fields = (other.task_metadata, other.state_vector)
return (self.measurements == other.measurements).all() and self_fields == other_fields
return NotImplemented
[docs] @staticmethod
def measurement_counts_from_measurements(measurements: np.ndarray) -> Counter:
"""
Creates measurement counts from measurements
Args:
measurements (numpy.ndarray): 2d array - row is shot, column is qubit.
Returns:
Counter: A Counter of measurements. Key is the measurements in a big endian binary
string. Value is the number of times that measurement occurred.
"""
bitstrings = []
for j in range(len(measurements)):
bitstrings.append("".join([str(element) for element in measurements[j]]))
return Counter(bitstrings)
[docs] @staticmethod
def measurement_probabilities_from_measurement_counts(
measurement_counts: Counter,
) -> Dict[str, float]:
"""
Creates measurement probabilities from measurement counts
Args:
measurement_counts (Counter): A Counter of measurements. Key is the measurements
in a big endian binary string. Value is the number of times that measurement
occurred.
Returns:
Dict[str, float]: A dictionary of probabilistic results. Key is the measurements
in a big endian binary string. Value is the probability the measurement occurred.
"""
measurement_probabilities = {}
shots = sum(measurement_counts.values())
for key, count in measurement_counts.items():
measurement_probabilities[key] = count / shots
return measurement_probabilities
[docs] @staticmethod
def measurements_from_measurement_probabilities(
measurement_probabilities: Dict[str, float], shots: int
) -> np.ndarray:
"""
Creates measurements from measurement probabilities
Args:
measurement_probabilities (Dict[str, float]): A dictionary of probabilistic results.
Key is the measurements in a big endian binary string.
Value is the probability the measurement occurred.
shots (int): number of iterations on device
Returns:
Dict[str, float]: A dictionary of probabilistic results.
Key is the measurements in a big endian binary string.
Value is the probability the measurement occurred.
"""
measurements_list = []
for bitstring in measurement_probabilities:
measurement = list(bitstring)
individual_measurement_list = [measurement] * int(
round(measurement_probabilities[bitstring] * shots)
)
measurements_list.extend(individual_measurement_list)
return np.asarray(measurements_list, dtype=int)
[docs] @staticmethod
def from_dict(result: Dict[str, Any]):
"""
Create GateModelQuantumTaskResult from dict.
Args:
result (Dict[str, Any]): Results dict with GateModelQuantumTaskResult attributes as keys
Returns:
GateModelQuantumTaskResult: A GateModelQuantumTaskResult based on the given dict
Raises:
ValueError: If neither "Measurements" nor "MeasurementProbabilities" is a key
in the result dict
Note:
For the "StateVector" key, the value should be of type Dict[str, complex];
each bitstring's amplitude is a Python complex number.
"""
return GateModelQuantumTaskResult._from_dict_internal(result)
[docs] @staticmethod
def from_string(result: str) -> GateModelQuantumTaskResult:
"""
Create GateModelQuantumTaskResult from string.
Args:
result (str): JSON object string, with GateModelQuantumTaskResult attributes as keys.
Returns:
GateModelQuantumTaskResult: A GateModelQuantumTaskResult based on the given string
Raises:
ValueError: If neither "Measurements" nor "MeasurementProbabilities" is a key
in the result dict
Note:
For the "StateVector" key, the value should be of type Dict[str, List[float, float]];
each bitstring's amplitude is represented by a two-element list, with first element
the real component and second element the imaginary component.
"""
json_obj = json.loads(result)
state_vector = json_obj.get("StateVector", None)
if state_vector:
for state in state_vector:
state_vector[state] = complex(*state_vector[state])
return GateModelQuantumTaskResult._from_dict_internal(json_obj)
@classmethod
def _from_dict_internal(cls, result: Dict[str, Any]):
task_metadata = result["TaskMetadata"]
state_vector = result.get("StateVector", None)
if "Measurements" in result:
measurements = np.asarray(result["Measurements"], dtype=int)
m_counts = GateModelQuantumTaskResult.measurement_counts_from_measurements(measurements)
m_probs = GateModelQuantumTaskResult.measurement_probabilities_from_measurement_counts(
m_counts
)
measurements_copied_from_device = True
m_counts_copied_from_device = False
m_probabilities_copied_from_device = False
elif "MeasurementProbabilities" in result:
shots = task_metadata["Shots"]
m_probs = result["MeasurementProbabilities"]
measurements = GateModelQuantumTaskResult.measurements_from_measurement_probabilities(
m_probs, shots
)
m_counts = GateModelQuantumTaskResult.measurement_counts_from_measurements(measurements)
measurements_copied_from_device = False
m_counts_copied_from_device = False
m_probabilities_copied_from_device = True
else:
raise ValueError(
'One of "Measurements" or "MeasurementProbabilities" must be in the results dict'
)
return cls(
state_vector=state_vector,
task_metadata=task_metadata,
measurements=measurements,
measurement_counts=m_counts,
measurement_probabilities=m_probs,
measurements_copied_from_device=measurements_copied_from_device,
measurement_counts_copied_from_device=m_counts_copied_from_device,
measurement_probabilities_copied_from_device=m_probabilities_copied_from_device,
)