Source code for braket.circuits.result_type

# 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

from typing import Any, Dict, List

from braket.circuits.observable import Observable
from braket.circuits.qubit import QubitInput
from braket.circuits.qubit_set import QubitSet, QubitSetInput


[docs]class ResultType: """ Class `ResultType` represents a requested result type for the circuit. This class is considered the result type definition containing the metadata that defines what a requested result type is and what it does. """ def __init__(self, ascii_symbols: List[str]): """ Args: ascii_symbols (List[str]): ASCII string symbols for the result type. This is used when printing a diagram of circuits. Raises: ValueError: `ascii_symbols` is None """ if ascii_symbols is None: raise ValueError("ascii_symbols must not be None") self._ascii_symbols = ascii_symbols @property def ascii_symbols(self) -> List[str]: """List[str]: Returns the ascii symbols for the requested result type.""" return self._ascii_symbols @property def name(self) -> str: """ Returns the name of the result type Returns: The name of the result type as a string """ return self.__class__.__name__
[docs] def to_ir(self, *args, **kwargs) -> Any: """Returns IR object of the result type Args: *args: Positional arguments **kwargs: Keyword arguments Returns: IR object of the result type """ raise NotImplementedError("to_ir has not been implemented yet.")
[docs] def copy(self, target_mapping: Dict[QubitInput, QubitInput] = {}, target: QubitSetInput = None): """ Return a shallow copy of the result type. Note: If `target_mapping` is specified, then `self.target` is mapped to the specified qubits. This is useful apply an instruction to a circuit and change the target qubits. Args: target_mapping (dictionary[int or Qubit, int or Qubit], optional): A dictionary of qubit mappings to apply to the target. Key is the qubit in this `target` and the value is what the key is changed to. Default = {}. target (int, Qubit, or iterable of int / Qubit, optional): Target qubits for the new instruction. Returns: ResultType: A shallow copy of the result type. Raises: TypeError: If both `target_mapping` and `target` are supplied. Examples: >>> result_type = ResultType.Probabilities(targets=[0]) >>> new_result_type = result_type.copy() >>> new_result_type.targets QubitSet(Qubit(0)) >>> new_result = result_type.copy(target_mapping={0: 5}) >>> new_result_type.target QubitSet(Qubit(5)) >>> new_result = result_type.copy(target=[5]) >>> new_result_type.target QubitSet(Qubit(5)) """ copy = self.__copy__() if target_mapping and target is not None: raise TypeError("Only 'target_mapping' or 'target' can be supplied, but not both.") elif target is not None: if hasattr(copy, "target"): copy.target = target else: if hasattr(copy, "target"): copy.target = self._target.map(target_mapping) return copy
[docs] @classmethod def register_result_type(cls, result_type: "ResultType"): """Register a result type implementation by adding it into the ResultType class. Args: result_type (ResultType): ResultType instance to register. """ setattr(cls, result_type.__name__, result_type)
def __repr__(self) -> str: return f"{self.name}()"
[docs]class ObservableResultType(ResultType): """ Result types with observables and targets. If no targets are specified, the observable must only operate on 1 qubit and it will be applied to all qubits in parallel. Otherwise, the number of specified targets must be equivalent to the number of qubits the observable can be applied to. See :mod:`braket.circuits.observables` module for all of the supported observables. """ def __init__( self, ascii_symbols: List[str], observable: Observable, target: QubitSetInput = None ): """ Args: observable (Observable): the observable for the result type target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the result type is requested for. Default is None, which means the observable must only operate on 1 qubit and it will be applied to all qubits in parallel Raises: ValueError: if target=None and the observable's qubit count is not 1. Or, if target!=None and the observable's qubit count and the number of target qubits are not equal. Or, if target!=None and the observable's qubit count and the number of ascii_symbols are not equal. """ super().__init__(ascii_symbols) self._observable = observable self._target = QubitSet(target) if not self._target: if self._observable.qubit_count != 1: raise ValueError( f"Observable {self._observable} must only operate on 1 qubit for target=None" ) else: if self._observable.qubit_count != len(self._target): raise ValueError( "Observable's qubit count and the number of target qubits must be equal" ) if self._observable.qubit_count != len(self.ascii_symbols): raise ValueError( "Observable's qubit count and the number of ASCII symbols must be equal" ) @property def observable(self) -> Observable: return self._observable @property def target(self) -> QubitSet: return self._target @target.setter def target(self, target: QubitSetInput) -> None: self._target = QubitSet(target) def __eq__(self, other) -> bool: if isinstance(other, ObservableResultType): return ( self.name == other.name and self.target == other.target and self.observable == other.observable ) return NotImplemented def __repr__(self) -> str: return f"{self.name}(observable={self.observable}, target={self.target})" def __copy__(self) -> ObservableResultType: return type(self)(observable=self.observable, target=self.target)