Source code for braket.circuits.observables

# 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 functools
from typing import List, Tuple

import numpy as np
from braket.circuits.observable import Observable
from braket.circuits.quantum_operator_helpers import (
    is_hermitian,
    verify_quantum_operator_matrix_dimensions,
)


[docs]class H(Observable): """Hadamard operation as an observable.""" def __init__(self): """ Examples: >>> Observable.I() """ super().__init__(qubit_count=1, ascii_symbols=["H"])
[docs] def to_ir(self) -> List[str]: return ["h"]
[docs] def to_matrix(self) -> np.ndarray: return 1.0 / np.sqrt(2.0) * np.array([[1.0, 1.0], [1.0, -1.0]], dtype=complex)
Observable.register_observable(H)
[docs]class I(Observable): # noqa: E742, E261 """Identity operation as an observable.""" def __init__(self): """ Examples: >>> Observable.I() """ super().__init__(qubit_count=1, ascii_symbols=["I"])
[docs] def to_ir(self) -> List[str]: return ["i"]
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1.0, 0.0], [0.0, 1.0]], dtype=complex)
Observable.register_observable(I)
[docs]class X(Observable): """Pauli-X operation as an observable.""" def __init__(self): """ Examples: >>> Observable.X() """ super().__init__(qubit_count=1, ascii_symbols=["X"])
[docs] def to_ir(self) -> List[str]: return ["x"]
[docs] def to_matrix(self) -> np.ndarray: return np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex)
Observable.register_observable(X)
[docs]class Y(Observable): """Pauli-Y operation as an observable.""" def __init__(self): """ Examples: >>> Observable.Y() """ super().__init__(qubit_count=1, ascii_symbols=["Y"])
[docs] def to_ir(self) -> List[str]: return ["y"]
[docs] def to_matrix(self) -> np.ndarray: return np.array([[0.0, -1.0j], [1.0j, 0.0]], dtype=complex)
Observable.register_observable(Y)
[docs]class Z(Observable): """Pauli-Z operation as an observable.""" def __init__(self): """ Examples: >>> Observable.Z() """ super().__init__(qubit_count=1, ascii_symbols=["Z"])
[docs] def to_ir(self) -> List[str]: return ["z"]
[docs] def to_matrix(self) -> np.ndarray: return np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)
Observable.register_observable(Z)
[docs]class TensorProduct(Observable): """Tensor product of observables""" def __init__(self, observables: List[Observable]): """ Args: observables (List[Observable]): List of observables for tensor product Examples: >>> t1 = Observable.Y() @ Observable.X() >>> t1.to_matrix() array([[0.+0.j, 0.+0.j, 0.-0.j, 0.-1.j], [0.+0.j, 0.+0.j, 0.-1.j, 0.-0.j], [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j], [0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j]]) >>> t2 = Observable.Z() @ t1 >>> t2.observables (Z('qubit_count': 1), Y('qubit_count': 1), X('qubit_count': 1)) Note: list of observables for tensor product must be given in the desired order that the tensor product will be calculated. For `TensorProduct(observables=[ob1, ob2, ob3])`, the tensor product's matrix will be the result of the tensor product of `ob1`, `ob2`, `ob3`, or `np.kron(np.kron(ob1.to_matrix(), ob2.to_matrix()), ob3.to_matrix())` """ self._observables = tuple(observables) qubit_count = sum([obs.qubit_count for obs in observables]) display_name = "@".join([obs.ascii_symbols[0] for obs in observables]) super().__init__(qubit_count=qubit_count, ascii_symbols=[display_name] * qubit_count)
[docs] def to_ir(self) -> List[str]: ir = [] for obs in self.observables: ir.extend(obs.to_ir()) return ir
@property def observables(self) -> Tuple[Observable]: """Tuple[Observable]: observables part of tensor product""" return self._observables
[docs] def to_matrix(self) -> np.ndarray: return functools.reduce(np.kron, [obs.to_matrix() for obs in self.observables])
def __matmul__(self, other): if isinstance(other, TensorProduct): return TensorProduct(list(self.observables) + list(other.observables)) if isinstance(other, Observable): return TensorProduct(list(self.observables) + [other]) raise ValueError("Can only perform tensor products between observables.") def __rmatmul__(self, other): if isinstance(other, Observable): return TensorProduct([other] + list(self.observables)) raise ValueError("Can only perform tensor products between observables.") def __repr__(self): return "TensorProduct(" + ", ".join([repr(o) for o in self.observables]) + ")" def __eq__(self, other): return self.matrix_equivalence(other)
Observable.register_observable(TensorProduct)
[docs]class Hermitian(Observable): """Hermitian matrix as an observable.""" def __init__(self, matrix: np.ndarray, display_name: str = "Hermitian"): """ Args: matrix (numpy.ndarray): Hermitian matrix which defines the observable. display_name (str): Name to be used for an instance of this Hermitian matrix observable for circuit diagrams. Defaults to `Hermitian`. Raises: ValueError: If `matrix` is not a two-dimensional square matrix, or has a dimension length which is not a positive exponent of 2, or is non-hermitian. Example: >>> Observable.Hermitian(matrix=np.array([[0, 1],[1, 0]])) """ verify_quantum_operator_matrix_dimensions(matrix) self._matrix = np.array(matrix, dtype=complex) qubit_count = int(np.log2(self._matrix.shape[0])) if not is_hermitian(self._matrix): raise ValueError(f"{self._matrix} is not hermitian") super().__init__(qubit_count=qubit_count, ascii_symbols=[display_name] * qubit_count)
[docs] def to_ir(self) -> List[List[List[List[float]]]]: return [ [[[element.real, element.imag] for element in row] for row in self._matrix.tolist()] ]
[docs] def to_matrix(self) -> np.ndarray: return self._matrix
def __eq__(self, other) -> bool: return self.matrix_equivalence(other)
Observable.register_observable(Hermitian)