Source code for qhbmlib.inference.qhbm

# Copyright 2021 The QHBM Library Authors. 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.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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.
# ==============================================================================
"""Tools for inference on quantum Hamiltonians."""

import functools
from typing import Union

import tensorflow as tf

from qhbmlib.inference import ebm  # pylint: disable=unused-import
from qhbmlib.inference import qnn  # pylint: disable=unused-import
from qhbmlib.models import hamiltonian
from qhbmlib import utils


[docs]class QHBM(tf.keras.layers.Layer): r"""Methods for inference involving normalized exponentials of Hamiltonians. We also call the normalized exponential of a Hamiltonian a "thermal state". Here we formalize some aspects of thermal states, which will be used later to explain particular methods of this class. # TODO(#119): add reference to updated QHBM paper. Each method takes as input some modular Hamiltonian $$K_{\theta\phi} = U_\phi K_\theta U_\phi^\dagger.$$ The [thermal state][1] corresponding to the model is $$ \rho_T = Z^{-1} e^{-\beta K_{\theta\phi}}.$$ For QHBMs, we assume $\beta = 1$, effectively absorbing it into the definition of the modular Hamiltonian. Then $\rho_T$ can be expanded as $$\rho_T = \sum_x p_\theta(x)U_\phi\ket{x}\bra{x}U_\phi^\dagger,$$ where the probability is given by $$p_\theta(x) = \tr[\exp(-K_\theta)]\bra{x}\exp(-K_\theta)\ket{x}$$ for $x\in\{1, \ldots, \dim(K_{\theta\phi})\} = \mathcal{X}$. Note that each $U_\phi\ket{x}$ is an eigenvector of both $\rho_T$ and $K_{\theta\phi}$. Corresponding to this density operator is an [ensemble of quantum states][2]. Using the terms above, we define the particular ensemble $$\mathcal{E} = \{p_\theta(x), U_\phi\ket{x}\}_{x\in\mathcal{X}},$$ also known as the [canonical ensemble][2] corresponding to $\rho_T$. Each method of this class implicitly samples from this ensemble, then post-processes to perform a particular inference task. #### References [1]: Nielsen, Michael A. and Chuang, Isaac L. (2010). Quantum Computation and Quantum Information. Cambridge University Press. [2]: Wilde, Mark M. (2017). Quantum Information Theory (second edition). Cambridge University Press. """ def __init__(self, input_ebm: ebm.EnergyInference, input_qnn: qnn.QuantumInference, name: Union[None, str] = None): """Initializes a QHBM. Args: input_ebm: Attends to density operator eigenvalues. input_qnn: Attends to density operator eigenvectors. name: Optional name for the model. """ super().__init__(name=name) self._e_inference = input_ebm self._q_inference = input_qnn self._modular_hamiltonian = hamiltonian.Hamiltonian( self.e_inference.energy, self.q_inference.circuit) @property def e_inference(self): """The object used for inference on density operator eigenvalues.""" return self._e_inference @property def q_inference(self): """The object used for inference on density operator eigenvectors.""" return self._q_inference @property def modular_hamiltonian(self): """The modular Hamiltonian defining this QHBM.""" return self._modular_hamiltonian
[docs] def circuits(self, num_samples: int): r"""Draws thermally distributed eigenstates from the model Hamiltonian. Here we explain the algorithm. First, construct $X$ to be a classical random variable with probability distribution $p_\theta(x)$ set by `model.e_inference.energy`. Then, draw $n = $`num\_samples` bitstrings, $S=\{x_1, \ldots, x_n\}$, from $X$. For each unique $x_i\in S$, set `states[i]` to the TFQ string representation of $U_\phi\ket{x_i}$, where $U_\phi$ is set by `self.q_inference.circuit`. Finally, set `counts[i]` equal to the number of times $x_i$ occurs in $S$. Args: model: The modular Hamiltonian whose normalized exponential is the density operator governing the ensemble of states from which to sample. num_samples: Number of states to draw from the ensemble. Returns: states: 1D `tf.Tensor` of dtype `tf.string`. Each entry is a TFQ string representation of an eigenstate of `self.modular_hamiltonian`. counts: 1D `tf.Tensor` of dtype `tf.int32`. `counts[i]` is the number of times `states[i]` was drawn from the ensemble. """ samples = self.e_inference.sample(num_samples) bitstrings, _, counts = utils.unique_bitstrings_with_counts(samples) states = self.q_inference.circuit(bitstrings) return states, counts
[docs] def expectation(self, observables: Union[tf.Tensor, hamiltonian.Hamiltonian]): """Estimates observable expectation values against the density operator. TODO(#119): add expectation and derivative equations and discussions from updated paper. Implicitly sample pure states from the canonical ensemble corresponding to the thermal state defined by `self.modular_hamiltonian`. For each such state |psi>, estimate the expectation value <psi|op_j|psi> for each `ops[j]`. Then, average these expectation values over the sampled states. Args: model: The modular Hamiltonian whose normalized exponential is the density operator against which expectation values will be estimated. obervables: Hermitian operators to measure. See docstring of `QuantumInference.expectation` for details. Returns: `tf.Tensor` with shape [n_ops] whose entries are are the sample averaged expectation values of each entry in `ops`. """ return self.e_inference.expectation( functools.partial( self.q_inference.expectation, observables=observables))