From ad591ab5f0bbe61feafb93cffc37cf5b65ec5ba6 Mon Sep 17 00:00:00 2001 From: Jonathon Misiewicz Date: Mon, 20 May 2024 12:37:24 -0400 Subject: [PATCH] Trotterizable mixin (#253) * Trotterizable mixin --- src/qforte/abc/algorithm.py | 30 ++++++++++++++++-------------- src/qforte/abc/ansatz.py | 6 +++++- src/qforte/abc/mixin.py | 32 ++++++++++++++++++++++++++++++++ src/qforte/abc/uccpqeabc.py | 2 +- src/qforte/abc/uccvqeabc.py | 5 ++++- src/qforte/ite/qite.py | 17 ++++------------- src/qforte/qkd/mrsqk.py | 18 ++++-------------- src/qforte/qkd/nt_srqk.py | 9 ++------- src/qforte/qkd/srqk.py | 19 ++++--------------- src/qforte/qpea/qpe.py | 13 +++---------- src/qforte/ucc/adaptvqe.py | 16 ++-------------- src/qforte/ucc/spqe.py | 15 ++------------- src/qforte/ucc/uccnpqe.py | 15 ++------------- src/qforte/ucc/uccnvqe.py | 15 ++------------- 14 files changed, 83 insertions(+), 129 deletions(-) create mode 100644 src/qforte/abc/mixin.py diff --git a/src/qforte/abc/algorithm.py b/src/qforte/abc/algorithm.py index e51b6d6d..c5531587 100644 --- a/src/qforte/abc/algorithm.py +++ b/src/qforte/abc/algorithm.py @@ -7,6 +7,7 @@ from abc import ABC, abstractmethod import qforte as qf from qforte.utils.state_prep import * +from qforte.abc.mixin import Trotterizable class Algorithm(ABC): @@ -30,16 +31,6 @@ class Algorithm(ABC): measurment (unphysical for quantum computer). Most algorithms only have a fast implentation. - _trotter_order : int - The Trotter order to use for exponentiated operators. - (exact in the infinite limit). - - _trotter_number : int - The number of trotter steps (m) to perform when approximating the matrix - exponentials (Um or Un). For the exponential of two non commuting terms - e^(A + B), the approximate operator C(m) = (e^(A/m) * e^(B/m))^m is - exact in the infinite m limit. - _Egs : float The final ground state energy value. @@ -69,8 +60,6 @@ def __init__( system, reference=None, state_prep_type="occupation_list", - trotter_order=1, - trotter_number=1, fast=True, verbose=False, print_summary_file=False, @@ -119,8 +108,7 @@ def __init__( self._hf_energy = 0.0 self._Nl = len(self._qb_ham.terms()) - self._trotter_order = trotter_order - self._trotter_number = trotter_number + self._fast = fast self._verbose = verbose self._print_summary_file = print_summary_file @@ -202,6 +190,20 @@ def verify_required_attributes(self): "Concrete Algorithm class must define self._n_pauli_trm_measures attribute." ) + def print_generic_options(self): + """Print options applicable to any algorithm.""" + print( + "Trial reference state: ", + ref_string(self._ref, self._nqb), + ) + print("Number of Hamiltonian Pauli terms: ", self._Nl) + print("Trial state preparation method: ", self._state_prep_type) + if isinstance(self, Trotterizable): + self.print_trotter_options() + print("Use fast version of algorithm: ", str(self._fast)) + if not self._fast: + print("Measurement variance thresh: ", 0.01) + class AnsatzAlgorithm(Algorithm): """A class that characterizes the most basic functionality for all diff --git a/src/qforte/abc/ansatz.py b/src/qforte/abc/ansatz.py index a2049f95..ec105e81 100644 --- a/src/qforte/abc/ansatz.py +++ b/src/qforte/abc/ansatz.py @@ -11,13 +11,17 @@ from qforte.utils.state_prep import build_Uprep from qforte.utils.trotterization import trotterize from qforte.utils.compact_excitation_circuits import compact_excitation_circuit +from qforte.abc.mixin import Trotterizable -class UCC: +class UCC(Trotterizable): """A mixin class for implementing the UCC circuit ansatz, to be inherited by a concrete class UCC+algorithm class. """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + def ansatz_circuit(self, amplitudes=None): """This function returns the Circuit object built from the appropriate amplitudes. diff --git a/src/qforte/abc/mixin.py b/src/qforte/abc/mixin.py new file mode 100644 index 00000000..5d182986 --- /dev/null +++ b/src/qforte/abc/mixin.py @@ -0,0 +1,32 @@ +""" +Lightweight Mixin Classes +==================================== +This file contains several mixin classes. +Such classes are intended for multiple inheritance and should always +call super().__init__ in their constructors. +""" + + +class Trotterizable: + """ + A mixin class for methods that employ Trotter approximation. + + _trotter_order : int + The Trotter order to use for exponentiated operators. + (exact in the infinite limit). + + _trotter_number : int + The number of trotter steps (m) to perform when approximating the matrix + exponentials (Um or Un). For the exponential of two non commuting terms + e^(A + B), the approximate operator C(m) = (e^(A/m) * e^(B/m))^m is + exact in the infinite m limit. + """ + + def __init__(self, *args, trotter_order=1, trotter_number=1, **kwargs): + super().__init__(*args, **kwargs) + self._trotter_order = trotter_order + self._trotter_number = trotter_number + + def print_trotter_options(self): + print("Trotter order (rho): ", self._trotter_order) + print("Trotter number (m): ", self._trotter_number) diff --git a/src/qforte/abc/uccpqeabc.py b/src/qforte/abc/uccpqeabc.py index 9ac5922f..2968311f 100644 --- a/src/qforte/abc/uccpqeabc.py +++ b/src/qforte/abc/uccpqeabc.py @@ -18,7 +18,7 @@ import numpy as np -class UCCPQE(PQE, UCC): +class UCCPQE(UCC, PQE): """The abstract base class inheritied by any algorithm that seeks to find eigenstates by minimization of the residual condition diff --git a/src/qforte/abc/uccvqeabc.py b/src/qforte/abc/uccvqeabc.py index 7ddb5f35..75e13ae5 100644 --- a/src/qforte/abc/uccvqeabc.py +++ b/src/qforte/abc/uccvqeabc.py @@ -19,7 +19,7 @@ import numpy as np -class UCCVQE(VQE, UCC): +class UCCVQE(UCC, VQE): """The abstract base class inheritied by any algorithm that seeks to find eigenstates by variational minimization of the Energy @@ -70,6 +70,9 @@ class UCCVQE(VQE, UCC): """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + @abstractmethod def get_num_ham_measurements(self): pass diff --git a/src/qforte/ite/qite.py b/src/qforte/ite/qite.py index e5f059f8..a7327bc2 100644 --- a/src/qforte/ite/qite.py +++ b/src/qforte/ite/qite.py @@ -6,6 +6,7 @@ """ import qforte as qf from qforte.abc.algorithm import Algorithm +from qforte.abc.mixin import Trotterizable from qforte.utils.transforms import get_jw_organizer, organizer_to_circuit from qforte.utils.state_prep import * @@ -20,7 +21,7 @@ ### Throughout this file, we'll refer to DOI 10.1038/s41567-019-0704-4 as Motta. -class QITE(Algorithm): +class QITE(Trotterizable, Algorithm): """This class implements the quantum imaginary time evolution (QITE) algorithm in a fashion amenable to non k-local hamiltonains, which is the focus of the origional algorithm (see DOI 10.1038/s41567-019-0704-4). @@ -185,18 +186,8 @@ def print_options_banner(self): print("\n\n ==> QITE options <==") print("-----------------------------------------------------------") - # General algorithm options. - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Number of Hamiltonian Pauli terms: ", self._Nl) - print("Trial state preparation method: ", self._state_prep_type) - print("Trotter order (rho): ", self._trotter_order) - print("Trotter number (m): ", self._trotter_number) - print("Use fast version of algorithm: ", str(self._fast)) - if not self._fast: - print("Measurement variance thresh: ", 0.01) + + self.print_generic_options() # Specific QITE options. print("Total imaginary evolution time (beta): ", self._beta) diff --git a/src/qforte/qkd/mrsqk.py b/src/qforte/qkd/mrsqk.py index 1ba42ad4..ffa322e4 100644 --- a/src/qforte/qkd/mrsqk.py +++ b/src/qforte/qkd/mrsqk.py @@ -7,6 +7,7 @@ """ import qforte +from qforte.abc.mixin import Trotterizable from qforte.abc.qsdabc import QSD from qforte.qkd.srqk import SRQK from qforte.helper.printing import matprint @@ -27,7 +28,7 @@ from scipy.linalg import eig -class MRSQK(QSD): +class MRSQK(Trotterizable, QSD): """A quantum subspace diagonalization algorithm that generates the many-body basis from :math:`s` real time evolutions of :math:`d` differnt orthogonal reference states :math:`| \Phi_I \\rangle`: @@ -224,19 +225,8 @@ def print_options_banner(self): print("\n\n ==> MRSQK options <==") print("-----------------------------------------------------------") - # General algorithm options. - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Trial state preparation method: ", self._state_prep_type) - print("Trotter order (rho): ", self._trotter_order) - print("Trotter number (m): ", self._trotter_number) - print("Use fast version of algorithm: ", str(self._fast)) - if self._fast: - print("Measurement varience thresh: ", "NA") - else: - print("Measurement varience thresh: ", 0.01) + + self.print_generic_options() # Specific QITE options. print("Dimension of reference space (d): ", self._d) diff --git a/src/qforte/qkd/nt_srqk.py b/src/qforte/qkd/nt_srqk.py index 6a008817..5b999740 100644 --- a/src/qforte/qkd/nt_srqk.py +++ b/src/qforte/qkd/nt_srqk.py @@ -80,13 +80,8 @@ def print_options_banner(self): print("\n\n ==> NTQK options <==") print("-----------------------------------------------------------") - # General algorithm options. - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Number of Hamiltonian Pauli terms: ", self._Nl) - print("Trial state preparation method: ", self._state_prep_type) + + self.print_generic_options() # Specific SRQK options. print("Dimension of Krylov space (N): ", self._nstates) diff --git a/src/qforte/qkd/srqk.py b/src/qforte/qkd/srqk.py index 58ec90cf..a4d2926c 100644 --- a/src/qforte/qkd/srqk.py +++ b/src/qforte/qkd/srqk.py @@ -7,6 +7,7 @@ """ import qforte +from qforte.abc.mixin import Trotterizable from qforte.abc.qsdabc import QSD from qforte.helper.printing import matprint @@ -18,7 +19,7 @@ import numpy as np -class SRQK(QSD): +class SRQK(Trotterizable, QSD): """A quantum subspace diagonalization algorithm that generates the many-body basis from different durations of real time evolution: @@ -73,20 +74,8 @@ def print_options_banner(self): print("\n\n ==> QK options <==") print("-----------------------------------------------------------") - # General algorithm options. - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Number of Hamiltonian Pauli terms: ", self._Nl) - print("Trial state preparation method: ", self._state_prep_type) - print("Trotter order (rho): ", self._trotter_order) - print("Trotter number (m): ", self._trotter_number) - print("Use fast version of algorithm: ", str(self._fast)) - if self._fast: - print("Measurement varience thresh: ", "NA") - else: - print("Measurement varience thresh: ", 0.01) + + self.print_generic_options() # Specific SRQK options. print("Dimension of Krylov space (N): ", self._nstates) diff --git a/src/qforte/qpea/qpe.py b/src/qforte/qpea/qpe.py index b6fbe3d5..21612f35 100644 --- a/src/qforte/qpea/qpe.py +++ b/src/qforte/qpea/qpe.py @@ -1,5 +1,6 @@ import qforte from qforte.abc.algorithm import Algorithm +from qforte.abc.mixin import Trotterizable from qforte.utils.transforms import ( circuit_to_organizer, organizer_to_circuit, @@ -15,7 +16,7 @@ from scipy import stats -class QPE(Algorithm): +class QPE(Trotterizable, Algorithm): def run( self, guess_energy: float, t=1.0, nruns=20, success_prob=0.5, num_precise_bits=4 ): @@ -147,15 +148,7 @@ def print_options_banner(self): print("\n\n ==> QPE options <==") print("-----------------------------------------------------------") # General algorithm options. - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Trial state preparation method: ", self._state_prep_type) - print("Trotter order (rho): ", self._trotter_order) - print("Trotter number (m): ", self._trotter_number) - print("Use fast version of algorithm: ", str(self._fast)) - print("Measurement variance thresh: ", "NA" if self._fast else 0.01) + self.print_generic_options() # Specific QPE options. print("Target success probability: ", self._success_prob) diff --git a/src/qforte/ucc/adaptvqe.py b/src/qforte/ucc/adaptvqe.py index e59dec30..1854a6e2 100644 --- a/src/qforte/ucc/adaptvqe.py +++ b/src/qforte/ucc/adaptvqe.py @@ -250,20 +250,8 @@ def print_options_banner(self): print("\n\n ==> ADAPT-VQE options <==") print("---------------------------------------------------------") - # General algorithm options. - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Number of Hamiltonian Pauli terms: ", self._Nl) - print("Trial state preparation method: ", self._state_prep_type) - print("Trotter order (rho): ", self._trotter_order) - print("Trotter number (m): ", self._trotter_number) - print("Use fast version of algorithm: ", str(self._fast)) - if self._fast: - print("Measurement varience thresh: ", "NA") - else: - print("Measurement varience thresh: ", 0.01) + + self.print_generic_options() print("Use qubit excitations: ", self._qubit_excitations) print("Use compact excitation circuits: ", self._compact_excitations) diff --git a/src/qforte/ucc/spqe.py b/src/qforte/ucc/spqe.py index be7893dd..3c64e208 100644 --- a/src/qforte/ucc/spqe.py +++ b/src/qforte/ucc/spqe.py @@ -325,19 +325,8 @@ def print_options_banner(self): print("\n\n ==> SPQE options <==") print("---------------------------------------------------------") - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Number of Hamiltonian Pauli terms: ", self._Nl) - print("Trial state preparation method: ", self._state_prep_type) - print("Trotter order (rho): ", self._trotter_order) - print("Trotter number (m): ", self._trotter_number) - print("Use fast version of algorithm: ", str(self._fast)) - if self._fast: - print("Measurement varience thresh: ", "NA") - else: - print("Measurement varience thresh: ", 0.01) + + self.print_generic_options() print("Use qubit excitations: ", self._qubit_excitations) print("Use compact excitation circuits: ", self._compact_excitations) diff --git a/src/qforte/ucc/uccnpqe.py b/src/qforte/ucc/uccnpqe.py index 50b4180b..7ab9f4e2 100644 --- a/src/qforte/ucc/uccnpqe.py +++ b/src/qforte/ucc/uccnpqe.py @@ -145,19 +145,8 @@ def print_options_banner(self): print("\n\n ==> UCC-PQE options <==") print("---------------------------------------------------------") - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Number of Hamiltonian Pauli terms: ", self._Nl) - print("Trial state preparation method: ", self._state_prep_type) - print("Trotter order (rho): ", self._trotter_order) - print("Trotter number (m): ", self._trotter_number) - print("Use fast version of algorithm: ", str(self._fast)) - if self._fast: - print("Measurement varience thresh: ", "NA") - else: - print("Measurement varience thresh: ", 0.01) + + self.print_generic_options() print("Use qubit excitations: ", self._qubit_excitations) print("Use compact excitation circuits: ", self._compact_excitations) diff --git a/src/qforte/ucc/uccnvqe.py b/src/qforte/ucc/uccnvqe.py index fd131daf..085401b3 100644 --- a/src/qforte/ucc/uccnvqe.py +++ b/src/qforte/ucc/uccnvqe.py @@ -130,19 +130,8 @@ def print_options_banner(self): print("\n\n ==> UCCN-VQE options <==") print("---------------------------------------------------------") # General algorithm options. - print( - "Trial reference state: ", - ref_string(self._ref, self._nqb), - ) - print("Number of Hamiltonian Pauli terms: ", self._Nl) - print("Trial state preparation method: ", self._state_prep_type) - print("Trotter order (rho): ", self._trotter_order) - print("Trotter number (m): ", self._trotter_number) - print("Use fast version of algorithm: ", str(self._fast)) - if self._fast: - print("Measurement variance thresh: ", "NA") - else: - print("Measurement variance thresh: ", 0.01) + + self.print_generic_options() print("Use qubit excitations: ", self._qubit_excitations) print("Use compact excitation circuits: ", self._compact_excitations)