From f886af7456b75e915efcdd8b08a611b5a1f419b8 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 1 Jul 2021 10:37:32 +0200 Subject: [PATCH 01/64] qed adc1 --- adcc/AdcMatrix.py | 904 ++++++++++++++++++- adcc/AmplitudeVector.py | 447 ++++++++++ adcc/DataHfProvider.py | 1 + adcc/HfCounterData.py | 1 + adcc/LazyMp.py | 116 ++- adcc/ReferenceState.py | 85 ++ adcc/adc_pp/matrix.py | 1117 +++++++++++++++++++++++- adcc/adc_pp/transition_dm.py | 20 +- adcc/backends/__init__.py | 2 +- adcc/backends/psi4.py | 34 +- adcc/functions.py | 29 +- adcc/guess/guesses_from_diagonal.py | 11 + adcc/solver/davidson.py | 27 +- adcc/solver/explicit_symmetrisation.py | 30 +- adcc/solver/preconditioner.py | 19 +- adcc/workflow.py | 51 +- 16 files changed, 2824 insertions(+), 70 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index d40d2058..52a83d85 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -20,6 +20,8 @@ ## along with adcc. If not, see . ## ## --------------------------------------------------------------------- +from os import name +from numpy.lib.function_base import blackman import libadcc import warnings import numpy as np @@ -29,7 +31,7 @@ from .timings import Timer, timed_member_call from .AdcMethod import AdcMethod from .Intermediates import Intermediates -from .AmplitudeVector import AmplitudeVector +from .AmplitudeVector import QED_AmplitudeVector, AmplitudeVector, gs_vec class AdcMatrixlike: @@ -43,9 +45,10 @@ class AdcMatrix(AdcMatrixlike): # Default perturbation-theory orders for the matrix blocks (== standard ADC-PP). default_block_orders = { # ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), - "adc0": dict(ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc1": dict(ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc2": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 + #"adc0": dict(gs_gs=0, gs_ph=0, ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc0": dict(ph_gs=0, ph_ph=0, pphh_gs=None, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc1": dict(ph_gs=1, ph_ph=1, pphh_gs=None, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc2": dict(ph_gs=2, ph_ph=2, pphh_gs=1 , ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 "adc2x": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=1), # noqa: E501 "adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501 } @@ -85,10 +88,15 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): self.is_core_valence_separated = method.is_core_valence_separated self.ndim = 2 + if method.base_method.name == "adc2x" or method.base_method.name == "adc3": + NotImplementedError("Neither adc2x nor adc3 are implemented for QED-ADC") + + self.intermediates = intermediates if self.intermediates is None: self.intermediates = Intermediates(self.ground_state) + # Determine orders of PT in the blocks if block_orders is None: block_orders = self.default_block_orders[method.base_method.name] @@ -99,7 +107,7 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): # Sanity checks on block_orders for block in block_orders.keys(): - if block not in ("ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"): + if block not in ("ph_gs", "gs_ph", "pphh_gs", "ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"): raise ValueError(f"Invalid block order key: {block}") if block_orders["ph_pphh"] != block_orders["pphh_ph"]: raise ValueError("ph_pphh and pphh_ph should always have " @@ -110,6 +118,7 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): self.block_orders = block_orders # Build the blocks and diagonals + """#non-qed with self.timer.record("build"): variant = None if method.is_core_valence_separated: @@ -120,10 +129,164 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): variant=variant) for block, order in block_orders.items() if order is not None } + #for block, order in block_orders.items(): + # if order is not None: + # print(block, order) self.__diagonal = sum(bl.diagonal for bl in self.blocks_ph.values() if bl.diagonal) self.__diagonal.evaluate() self.__init_space_data(self.__diagonal) + #print("following is self.__init_space_data(self.__diagonal)") + #print(self.__init_space_data(self.__diagonal)) + """ + + # here we try to use the whole functionality given for the non-qed case (with the AmplitudeVector class) + # and then use these to build the QED_AmplitudeVector class. Then we do something like + # self.blocks_ph = QED_AmplitudeVector(val0, self.blocks_ph0, val1, self.blocks_ph1) and + # self.diagonal = QED_AmplitudeVector(val0, self.__diagonal0, val1, self.__diagonal1) + + with self.timer.record("build"): + variant = None + if method.is_core_valence_separated: + variant = "cvs" + + # first electronic part + + self.elec = AdcMatrix_submatrix(method, hf_or_mp, "elec") + + self.blocks_ph_00 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=order, intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + """ + for block, order in block_orders.items(): + if order is not None: + print(block, order) + self.__diagonal_0 = sum(bl.diagonal for bl in self.blocks_ph_0.values() + if bl.diagonal) + self.__diagonal_0.evaluate() + #self.__init_space_data_0(self.__diagonal_0) + """ + + # now for coupling parts + + self.elec_couple = AdcMatrix_submatrix(method, hf_or_mp, "elec_couple") + + self.blocks_ph_10 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_couple", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + + self.phot_couple = AdcMatrix_submatrix(method, hf_or_mp, "phot_couple") + + self.blocks_ph_01 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot_couple", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + + # now for photonic part + + self.phot = AdcMatrix_submatrix(method, hf_or_mp, "phot") + + self.blocks_ph_11 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + """ + #for block, order in block_orders.items(): + # if order is not None: + # print(block, order) + self.__diagonal_1 = sum(bl.diagonal for bl in self.blocks_ph_1.values() + if bl.diagonal) + self.__diagonal_1.evaluate() + #self.__init_space_data_0(self.__diagonal_1) + """ + + # build QED_AmplitudeVector + + self.blocks_ph_1_temp = {} + for key in self.blocks_ph_10: + self.blocks_ph_1_temp[key + "_couple"] = self.blocks_ph_10[key] + for key in self.blocks_ph_01: + self.blocks_ph_1_temp[key + "_phot_couple"] = self.blocks_ph_01[key] + for key in self.blocks_ph_11: + self.blocks_ph_1_temp[key + "_phot"] = self.blocks_ph_11[key] + self.blocks_ph = {**self.blocks_ph_00, **self.blocks_ph_1_temp} + + self.__diagonal_gs = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph + if "gs_gs" in block and not block.endswith("phot")) # coupling gs_gs blocks have diagonal = 0 + self.__diagonal_gs1 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph + if "gs_gs" in block and block.endswith("phot")) + #self.blocks_ph = {**self.elec.blocks_ph, **self.blocks_ph_1_temp} + #self.blocks_ph = QED_AmplitudeVector(0, self.elec.blocks_ph, 0, self.phot.blocks_ph) + #self.__diagonal = QED_AmplitudeVector(0, self.elec.diagonal, 0, self.phot.diagonal) + #self.__init_space_data_qed(self.elec.diagonal) # here we need to adapt the attributes, defined via this function + if hasattr(self.elec.diagonal(), "pphh"): # this needs to be adapted, so that the diagonals for gs and gs1 are actually those from matrix.py, + # but if this is only for the guesses, this should only be a perfomance issue and not relevant for the precision + #print("from adcmatrix init diagonal has pphh part") + self.__diagonal = QED_AmplitudeVector(self.__diagonal_gs, self.elec.diagonal().ph, self.elec.diagonal().pphh, + self.__diagonal_gs1, self.phot.diagonal().ph, self.phot.diagonal().pphh) + else: + #print("from adcmatrix init diagonal has no pphh part!!!!!!!!!!!!!!!!!!!") + self.__diagonal = QED_AmplitudeVector(self.__diagonal_gs, self.elec.diagonal().ph, None, + self.__diagonal_gs1, self.phot.diagonal().ph, None) + self.__init_space_data_qed(self.elec.diagonal()) # here we need to adapt the attributes, defined via this function + # namely self.axis_spaces, self.axis_lengths, self.shape + self.__diagonal.evaluate() + print(self.elec.blocks_ph) + print(self.blocks_ph) + print(self.axis_spaces) + print(self.axis_lengths) + print(self.shape) + print(self.elec.axis_lengths) + print(self.elec.shape) + #print("matvec has to be corrected, since .gs and .gs1 parts are not yet included, also remaining blocks have to included") + + + + """ + #self.blocks_ph = QED_AmplitudeVector(0, self.blocks_ph_0, 0, self.blocks_ph_1) + self.blocks_ph_1_temp = {} + for key in self.blocks_ph_1: + self.blocks_ph_1_temp[key + "_phot"] = self.blocks_ph_1[key] + self.blocks_ph = {**self.blocks_ph_0, **self.blocks_ph_1_temp} + self.__diagonal = QED_AmplitudeVector(0, self.__diagonal_0, 0, self.__diagonal_1) + self.__init_space_data_qed(self.__diagonal_0) # here we need to adapt the attributes, defined via this function + # namely self.axis_spaces, self.axis_lengths, self.shape + print(self.blocks_ph_0) + print(self.blocks_ph_1) + print(self.blocks_ph) + print(self.axis_spaces) + print(self.axis_lengths) + print(self.shape) + """ + + + # since the guesses need the whole matrix object as input, istead of just the diagonals, it is useful to define + # self.elec and self.phot as the the corresponding original matrices + # this is done in one class, since we also require the full matrix for the solver + + + def __init_space_data_qed(self, diagonal0): + self.__init_space_data(diagonal0) + axis_spaces0 = self.axis_spaces # this (and the following) will be assigned in the submatrix class as well + axis_lengths0 = self.axis_lengths + shape0 = self.shape + self.shape = ((shape0[0]+1) * 2, (shape0[0]+1) * 2) + self.axis_spaces["gs"] = ["o0", "v0"] + self.axis_lengths["gs"] = 1 + # axis_spaces and axis_lengths both are dicts, referring to "ph", "pphh" as a key, so either make extra blocks here, or probably better + # make a longer list in funciton 'axis_blocks', so that later everything for "ph" and "pphh" is just used twice + # also it seems, that self.axis_lengths is not further used, but shape[0] is rather used for that, so not necessary to build self.axis_lengths correctly + def __init_space_data(self, diagonal): """Update the cached data regarding the spaces of the ADC matrix""" @@ -136,6 +299,10 @@ def __init_space_data(self, diagonal): ]) self.shape = (sum(self.axis_lengths.values()), sum(self.axis_lengths.values())) + #print(self.axis_spaces) + #print(self.axis_lengths) + #print(self.shape) + def __repr__(self): ret = f"AdcMatrix({self.method.name}, " @@ -162,12 +329,715 @@ def block_spaces(self, block): "will be removed in 0.16.0. " "Use `matrix.axis_spaces[block]` in the future.") return { + "g": self.axis_spaces.get("gs", None), "s": self.axis_spaces.get("ph", None), "d": self.axis_spaces.get("pphh", None), "t": self.axis_spaces.get("ppphhh", None), }[block] + #@property #non-qed + #def axis_blocks(self): + """ + Return the blocks used along one of the axes of the ADC matrix + (e.g. ['ph', 'pphh']). + """ + # return list(self.axis_spaces.keys()) + + @property + def axis_blocks(self): # this is only for debugging and has not been adapted yet + #print(list(self.axis_spaces.keys()) + list(self.axis_spaces.keys())) + return list(self.axis_spaces.keys()) + list(self.axis_spaces.keys()) + + def diagonal(self, block=None): + """Return the diagonal of the ADC matrix""" + if block is not None: + warnings.warn("Support for the block argument will be dropped " + "in 0.16.0.") + if block == "g": + return self.__diagonal.gs #this is just a float, but it could be e.g. omega, for which I dont know if required data is given in this function + if block == "s": + return self.__diagonal.ph + if block == "d": + return self.__diagonal.pphh + return self.__diagonal + + def compute_apply(self, block, tensor): + warnings.warn("The compute_apply function is deprecated and " + "will be removed in 0.16.0.") + if block in ("gg", "gs", "sg" ,"ss", "sd", "ds", "dd"): + warnings.warn("The singles-doubles interface is deprecated and " + "will be removed in 0.16.0.") + block = {"gg": "gs_gs", "gs": "gs_ph", "sg": "ph_gs", + "ss": "ph_ph", "sd": "ph_pphh", + "ds": "pphh_ph", "dd": "pphh_pphh"}[block] + return self.block_apply(block, tensor) + + def block_apply(self, block, tensor): + """ + Compute the application of a block of the ADC matrix + with another AmplitudeVector or Tensor. Non-matching blocks + in the AmplitudeVector will be ignored. + """ + if not isinstance(tensor, libadcc.Tensor): + raise TypeError("tensor should be an adcc.Tensor") + + with self.timer.record(f"apply/{block}"): + outblock, inblock = block.split("_") + ampl = QED_AmplitudeVector(**{inblock: tensor}) + ret = self.blocks_ph[block].apply(ampl) + return getattr(ret, outblock) + + @timed_member_call() + def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_AmplitudeVector needs to be adjusted + # maybe also this function has to be adapted + # lets try differentiating via _phot or not in self.blocks_ph and then do the summation over the blocks, + # which can then be forwarded to AmplitudeVector for ph and pphh and gs_vec for gs + """ + Compute the matrix-vector product of the ADC matrix + with an excitation amplitude and return the result. + """ + #print("printing matvec stuff from AdcMatrix") + + #res = v.zeros_like() + + #elec1 = self.elec.matvec(v) + #elec2 = self.phot_couple.matvec(v) + #print(type(elec1.ph), elec1.ph) + #print(type(elec2.ph), elec2.ph) + + # maybe also gs_ph and gs_pphh blocks can be included here, by adding them to the ph_ph and pphh_pphh blocks, respectively. + # this should be possible, since we give v as ampl in matrix.py, so we can select e.g. ampl.gs1 in the ph_ph block. + #print("this is v.pphh from matvec ", v.pphh) + elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + gs_part = 0 + gs1_part = 0 + + for block in self.blocks_ph: + if "gs" in block and not block.startswith("gs"): + #print(block, type(self.blocks_ph[block].apply(v)), self.blocks_ph[block].apply(v)) + if "phot_couple" in block: + gs_part += self.blocks_ph[block].apply(v) + elif "phot" in block: + gs1_part += self.blocks_ph[block].apply(v) + elif "couple" in block: + gs1_part += self.blocks_ph[block].apply(v) + else: # elec + gs_part += self.blocks_ph[block].apply(v) + """ + elif "gs" in block and block.startswith("gs"): + if "phot_couple" in block: + elec_part += self.blocks_ph[block].apply(v) + elif "phot" in block: + phot_part += self.blocks_ph[block].apply(v) + elif "couple" in block: + phot_part += self.blocks_ph[block].apply(v) + else: # elec + elec_part += self.blocks_ph[block].apply(v) + """ + + # try .gs and .gs1 parts with floats first, and then give them to the QED_AmplitudeVector in the end + # if that doesnt work, use QED_AmplitudeVectors from the beginning, which then has to be implemented in matrix.py as well + + """ + for block in self.blocks_ph: + if "gs" not in block: + print(block) + if block.endswith("_phot_couple"): + res.elec = sum(res.elec,self.blocks_ph[block].apply(v)) + elif block.endswith("_couple"): + res.phot = sum(res.phot,self.blocks_ph[block].apply(v)) + elif block.endswith("_phot"): + res.phot = sum(res.phot,self.blocks_ph[block].apply(v)) + elif block in self.blocks_ph_00: + print(type(res.elec), type(self.blocks_ph[block].apply(v))) + res.elec = sum(res.elec,self.blocks_ph[block].apply(v)) + else: + print("block {} has not been taken into account in matvec".format(block)) + """ + + + """ + self.blocks_ph_elec = {} + self.blocks_ph_phot = {} + self.blocks_ph_elec0 = {} # this is only necessary, if gs block part in ph/pphh space are in explicit blocks, and not e.g. in ph_ph block + self.blocks_ph_phot0 = {} # this is only necessary, if gs block part in ph/pphh space are in explicit blocks, and not e.g. in ph_ph_phot block + for block in self.blocks_ph: + if block.endswith("_phot"): + if not block.startswith("block_gs") and "gs" in block: # find all gs space blocks (with ampl.gs/ampl.gs1) + if self.blocks_ph[block].gs == None: + self.blocks_ph_phot0[block] = self.blocks_ph[block].gs1 + else: + self.blocks_ph_phot0[block] = self.blocks_ph[block].gs + else: + if self.blocks_ph[block].elec == None: + self.blocks_ph_phot[block] = self.blocks_ph[block].phot + else: + self.blocks_ph_phot[block] = self.blocks_ph[block].elec + #print(block, self.blocks_ph[block]) + else: + if not block.startswith("block_gs") and "gs" in block: # find all gs1 space blocks (with ampl.gs/ampl.gs1) + if self.blocks_ph[block].gs == None: + self.blocks_ph_elec0[block] = self.blocks_ph[block].gs1 + else: + self.blocks_ph_elec0[block] = self.blocks_ph[block].gs + else: + if self.blocks_ph[block].elec == None: + self.blocks_ph_elec[block] = self.blocks_ph[block].phot + else: + self.blocks_ph_elec[block] = self.blocks_ph[block].elec + #print(block, self.blocks_ph[block]) + + res = v.zeros_like() + #print("res", res.gs, res.elec, res.gs1, res.phot) + + res.gs = sum(block.apply(v) for block in self.blocks_ph_elec.values())# if hasattr(block.apply(v), "gs")) # __add__ from gs_vec + res.elec = sum(block.apply(v) for block in self.blocks_ph_elec.values())# if not hasattr(block.apply(v), "gs")) # __add__ from AmplitudeVector + res.gs1 = sum(block.apply(v) for block in self.blocks_ph_phot.values())# if hasattr(block.apply(v), "gs")) # __add__ from gs_vec + res.phot = sum(block.apply(v) for block in self.blocks_ph_phot.values())# if not hasattr(block.apply(v), "gs")) # __add__ from AmplitudeVector + """ + + #for block in self.blocks_ph_elec.values(): + # print(type(block.apply(v))) + # print(block.apply(v)) + #for block in self.blocks_ph: + # print(block) + """ + for block in self.blocks_ph_elec.values(): + #print(type(block.apply(v))) + if hasattr(block.apply(v), "gs"): # for gs QED_AmplitudeVector, while for ph and pphh AmplitudeVector + #print(block) + if block.apply(v).gs != None: + #print(res.gs, block.apply(v).gs) + res.gs += block.apply(v).gs + elif block.apply(v).gs1 != None: + res.gs += block.apply(v).gs1 + #else: + # if block.apply(v) != None: + # res.elec = sum(res.elec, block.apply(v)) + + for block in self.blocks_ph_phot.values(): + if hasattr(block.apply(v), "gs"): # for gs QED_AmplitudeVector, while for ph and pphh AmplitudeVector + #print(block) + if block.apply(v).gs != None: + #print("printing line 405 now") + #print(res.gs1, block.apply(v).gs) + res.gs1 += block.apply(v).gs + elif block.apply(v).gs1 != None: + res.gs1 += block.apply(v).gs1 + #else: + # if block.apply(v) != None: + # res.phot += block.apply(v) + + #return sum(block.apply(v) for block in self.blocks_ph.values()) + """ + #print(res.gs, res.elec, res.gs1, res.phot) + if "pphh" in elec_part.blocks_ph: + return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh) + else: + return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None) + + + + + def rmatvec(self, v): + # ADC matrix is symmetric + return self.matvec(v) + + def compute_matvec(self, ampl): + """ + Compute the matrix-vector product of the ADC matrix + with an excitation amplitude and return the result. + """ + warnings.warn("The compute_matvec function is deprecated and " + "will be removed in 0.16.0.") + return self.matvec(ampl) + + def __matmul__(self, other): + if isinstance(other, QED_AmplitudeVector): + return self.matvec(other) + if isinstance(other, list): + if all(isinstance(elem, QED_AmplitudeVector) for elem in other): + return [self.matvec(ov) for ov in other] + return NotImplemented + + def block_view(self, block): + """ + Return a view into the AdcMatrix that represents a single + block of the matrix. Currently only diagonal blocks are supported. + """ + b1, b2 = block.split("_") + if b1 != b2: + raise NotImplementedError("Off-diagonal block views not yet " + "implemented.") + # TODO For off-diagonal blocks we probably need a different + # data structure as the AdcMatrix class as these block + # are inherently different than an AdcMatrix (no Hermiticity + # for example) and basically they only need to support some + # form of matrix-vector product and some stastics like + # spaces and sizes etc. + block_orders = {bl: None for bl in self.block_orders.keys()} + block_orders[block] = self.block_orders[block] + return AdcMatrix(self.method, self.ground_state, + block_orders=block_orders, + intermediates=self.intermediates) + + def construct_symmetrisation_for_blocks(self): + """ + Construct the symmetrisation functions, which need to be + applied to relevant blocks of an AmplitudeVector in order + to symmetrise it to the right symmetry in order to be used + with the various matrix-vector-products of this function. + + Most importantly the returned functions antisymmetrise + the occupied and virtual parts of the doubles parts + if this is sensible for the method behind this adcmatrix. + + Returns a dictionary block identifier -> function + """ + ret = {} + if self.is_core_valence_separated: + # CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a) + ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)]) + else: + def symmetrise_generic_adc_doubles(invec): + # doubles part is antisymmetric wrt. (i,j,a,b) <-> (i,j,b,a) + scratch = invec.antisymmetrise([(2, 3)]) + # doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a) + return scratch.symmetrise([(0, 1), (2, 3)]) + ret["pphh"] = symmetrise_generic_adc_doubles + return ret + + def dense_basis(self, axis_blocks=None, ordering="adcc"): + """ + Return the list of indices and their values + of the dense basis representation + + ordering: adcc, spin, spatial + """ + ret = [] + if axis_blocks is None: + axis_blocks = self.axis_blocks + if not isinstance(axis_blocks, list): + axis_blocks = [axis_blocks] + + # Define function to impose the order in the basis + if ordering == "adcc": + def reduce_index(n_orbsa, idx): + return idx, idx + elif ordering == "spin": + def reduce_index(n_orbsa, idx): + is_beta = [idx[i] >= n_orbsa[i] for i in range(len(idx))] + spatial = [idx[i] - n_orbsa[i] if is_beta[i] else idx[i] + for i in range(len(idx))] + # Sort first by spin, then by spatial + return (is_beta, spatial) + elif ordering == "spatial": + def reduce_index(n_orbsa, idx): + is_beta = [idx[i] >= n_orbsa[i] for i in range(len(idx))] + spatial = [idx[i] - n_orbsa[i] if is_beta[i] else idx[i] + for i in range(len(idx))] + # Sort first by spatial, then by spin + return (spatial, is_beta) + + if "gs" in axis_blocks: + ret_g = [] + ret_g.append([(0, 0), 1]) + ret.extend(ret_g) + + if "ph" in axis_blocks: + ret_s = [] + sp_s = self.axis_spaces["ph"] + n_orbs_s = [self.mospaces.n_orbs(sp) for sp in sp_s] + n_orbsa_s = [self.mospaces.n_orbs_alpha(sp) for sp in sp_s] + for i in range(n_orbs_s[0]): + for a in range(n_orbs_s[1]): + ret_s.append([((i, a), 1)]) + + def sortfctn(x): + return min(reduce_index(n_orbsa_s, idx) for idx, factor in x) + ret_s.sort(key=sortfctn) + ret_s.sort(key=sortfctn) + ret.extend(ret_s) + + if "pphh" in axis_blocks: + ret_d = [] + sp_d = self.axis_spaces["pphh"] + n_orbsa_d = [self.mospaces.n_orbs_alpha(sp) for sp in sp_d] + + if sp_d[0] == sp_d[1] and sp_d[2] == sp_d[3]: + nso = self.mospaces.n_orbs(sp_d[0]) + nsv = self.mospaces.n_orbs(sp_d[2]) + ret_d.extend([[((i, j, a, b), +1 / 2), + ((j, i, a, b), -1 / 2), + ((i, j, b, a), -1 / 2), + ((j, i, b, a), +1 / 2)] + for i in range(nso) for j in range(i) + for a in range(nsv) for b in range(a)]) + elif sp_d[2] == sp_d[3]: + nso = self.mospaces.n_orbs(sp_d[0]) + nsc = self.mospaces.n_orbs(sp_d[1]) + nsv = self.mospaces.n_orbs(sp_d[2]) + ret_d.extend([[((i, j, a, b), +1 / np.sqrt(2)), + ((i, j, b, a), -1 / np.sqrt(2))] + for i in range(nso) for j in range(nsc) + for a in range(nsv) for b in range(a)]) + else: + nso = self.mospaces.n_orbs(sp_d[0]) + nsc = self.mospaces.n_orbs(sp_d[1]) + nsv = self.mospaces.n_orbs(sp_d[2]) + nsw = self.mospaces.n_orbs(sp_d[3]) + ret_d.append([((i, j, b, a), 1) + for i in range(nso) for j in range(nsc) + for a in range(nsv) for b in range(nsw)]) + + def sortfctn(x): + return min(reduce_index(n_orbsa_d, idx) for idx, factor in x) + ret_d.sort(key=sortfctn) + ret_d.sort(key=sortfctn) + ret.extend(ret_d) + + if any(b not in ("gs" ,"ph", "pphh") for b in self.axis_blocks): + raise NotImplementedError("Blocks other than gs, ph and pphh " + "not implemented") + return ret + + def to_ndarray(self, out=None): # this is not adapted to gs + """ + Return the ADC matrix object as a dense numpy array. Converts the sparse + internal representation of the ADC matrix to a dense matrix and return + as a numpy array. + + Notes + ----- + + This method is only intended to be used for debugging and + visualisation purposes as it involves computing a large amount of + matrix-vector products and the returned array consumes a considerable + amount of memory. + + The resulting matrix has no spin symmetry imposed, which means that + its eigenspectrum may contain non-physical excitations (e.g. with linear + combinations of α->β and α->α components in the excitation vector). + + This function has not been sufficiently tested to be considered stable. + """ + # TODO Update to ph / pphh + # TODO Still uses deprecated functions + import tqdm + + from adcc import guess_zero + + # Get zero amplitude of the appropriate symmetry + # (TODO: Only true for C1, where there is only a single irrep) + ampl_zero = guess_zero(self) + assert self.mospaces.point_group == "C1" + + # Build the shape of the returned array + # Since the basis of the doubles block is not the unit vectors + # this *not* equal to the shape of the AdcMatrix object + basis = {b: self.dense_basis(b) for b in self.axis_blocks} + mat_len = sum(len(basis[b]) for b in basis) + + if out is None: + out = np.zeros((mat_len, mat_len)) + else: + if out.shape != (mat_len, mat_len): + raise ValueError("Output array has shape ({0:}, {1:}), but " + "shape ({2:}, {2:}) is required." + "".format(*out.shape, mat_len)) + out[:] = 0 # Zero all data in out. + + # Check for the cases actually implemented + if any(b not in ("ph", "pphh") for b in self.axis_blocks): + raise NotImplementedError("Blocks other than ph and pphh " + "not implemented") + if "ph" not in self.axis_blocks: + raise NotImplementedError("Block 'ph' needs to be present") + + # Extract singles-singles block (contiguous) + assert "ph" in self.axis_blocks + n_orbs_ph = [self.mospaces.n_orbs(sp) for sp in self.axis_spaces["ph"]] + n_ph = np.prod(n_orbs_ph) + assert len(basis["ph"]) == n_ph + view_ss = out[:n_ph, :n_ph].reshape(*n_orbs_ph, *n_orbs_ph) + for i in range(n_orbs_ph[0]): + for a in range(n_orbs_ph[1]): + ampl = ampl_zero.copy() + ampl.ph[i, a] = 1 + view_ss[:, :, i, a] = (self @ ampl).ph.to_ndarray() + + # Extract singles-doubles and doubles-doubles block + if "pphh" in self.axis_blocks: + assert self.axis_blocks == ["ph", "pphh"] + view_sd = out[:n_ph, n_ph:].reshape(*n_orbs_ph, len(basis["pphh"])) + view_dd = out[n_ph:, n_ph:] + for j, bas1 in tqdm.tqdm(enumerate(basis["pphh"]), + total=len(basis["pphh"])): + ampl = ampl_zero.copy() + for idx, val in bas1: + ampl.pphh[idx] = val + ret_ampl = self @ ampl + view_sd[:, :, j] = ret_ampl.ph.to_ndarray() + + for i, bas2 in enumerate(basis["pphh"]): + view_dd[i, j] = sum(val * ret_ampl.pphh[idx] + for idx, val in bas2) + + out[n_ph:, :n_ph] = np.transpose(out[:n_ph, n_ph:]) + return out + + + +class AdcMatrix_submatrix(AdcMatrixlike): + # Default perturbation-theory orders for the matrix blocks (== standard ADC-PP). + default_block_orders = { + # ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), + #"adc0": dict(gs_gs=0, gs_ph=0, ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc0": dict(ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc1": dict(ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc2": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 + "adc2x": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=1), # noqa: E501 + "adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501 + } + + def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates=None): + """ + Initialise an ADC matrix. + + Parameters + ---------- + method : str or AdcMethod + Method to use. + hf_or_mp : adcc.ReferenceState or adcc.LazyMp + HF reference or MP ground state + elec_or_phot: string, either "elec" or "phot" + electronic or photonic part of qed-matrix, without groundstate + block_orders : optional + The order of perturbation theory to employ for each matrix block. + If not set, defaults according to the selected ADC method are chosen. + intermediates : adcc.Intermediates or NoneType + Allows to pass intermediates to re-use to this class. + """ + if isinstance(hf_or_mp, (libadcc.ReferenceState, + libadcc.HartreeFockSolution_i)): + hf_or_mp = LazyMp(hf_or_mp) + if not isinstance(hf_or_mp, LazyMp): + raise TypeError("mp_results is not a valid object. It needs to be " + "either a LazyMp, a ReferenceState or a " + "HartreeFockSolution_i.") + + if not isinstance(method, AdcMethod): + method = AdcMethod(method) + + self.timer = Timer() + self.method = method + self.ground_state = hf_or_mp + self.reference_state = hf_or_mp.reference_state + self.mospaces = hf_or_mp.reference_state.mospaces + self.is_core_valence_separated = method.is_core_valence_separated + self.ndim = 2 + + self.intermediates = intermediates + if self.intermediates is None: + self.intermediates = Intermediates(self.ground_state) + + # Determine orders of PT in the blocks + if block_orders is None: + block_orders = self.default_block_orders[method.base_method.name] + else: + tmp_orders = self.default_block_orders[method.base_method.name].copy() + tmp_orders.update(block_orders) + block_orders = tmp_orders + + # Sanity checks on block_orders + for block in block_orders.keys(): + if block not in ("gs_gs", "gs_ph", "ph_gs" ,"ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"): + raise ValueError(f"Invalid block order key: {block}") + if block_orders["ph_pphh"] != block_orders["pphh_ph"]: + raise ValueError("ph_pphh and pphh_ph should always have " + "the same order") + if block_orders["ph_pphh"] is not None \ + and block_orders["pphh_pphh"] is None: + raise ValueError("pphh_pphh cannot be None if ph_pphh isn't.") + self.block_orders = block_orders + + # Build the blocks and diagonals + + with self.timer.record("build"): + variant = None + if method.is_core_valence_separated: + variant = "cvs" + self.blocks_ph = {} + if subblock == "elec": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=order, intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + elif subblock == "elec_couple": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_couple", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + elif subblock == "phot_couple": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot_couple", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + elif subblock == "phot": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + else: + ValueError("Using this class you have to give the parameter elec_or_phot") + + + + #for block, order in block_orders.items(): + # if order is not None: + # print(block, order) + #for bl in self.blocks_ph.values(): + # print(bl.diagonal) + #for bl in self.blocks_ph: + # print(bl) + self.__diagonal = sum(bl.diagonal for bl in self.blocks_ph.values() + if bl.diagonal) + self.__diagonal.evaluate() + self.__init_space_data(self.__diagonal) + #print("following is self.__init_space_data(self.__diagonal)") + #print(self.__init_space_data(self.__diagonal)) + + """ + # here we try to use the whole functionality given for the non-qed case (with the AmplitudeVector class) + # and then use these to build the QED_AmplitudeVector class. Then we do something like + # self.blocks_ph = QED_AmplitudeVector(val0, self.blocks_ph0, val1, self.blocks_ph1) and + # self.diagonal = QED_AmplitudeVector(val0, self.__diagonal0, val1, self.__diagonal1) + + with self.timer.record("build"): + variant = None + if method.is_core_valence_separated: + variant = "cvs" + + # first electronic part + + self.blocks_ph_0 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=order, intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + for block, order in block_orders.items(): + if order is not None: + print(block, order) + self.__diagonal_0 = sum(bl.diagonal for bl in self.blocks_ph_0.values() + if bl.diagonal) + self.__diagonal_0.evaluate() + #self.__init_space_data_0(self.__diagonal_0) + + # now for photonic part + + self.blocks_ph_1 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + #for block, order in block_orders.items(): + # if order is not None: + # print(block, order) + self.__diagonal_1 = sum(bl.diagonal for bl in self.blocks_ph_1.values() + if bl.diagonal) + self.__diagonal_1.evaluate() + #self.__init_space_data_0(self.__diagonal_1) + + # build QED_AmplitudeVector + + self.blocks_ph = QED_AmplitudeVector(0, self.blocks_ph_0, 0, self.blocks_ph_1) + self.__diagonal = QED_AmplitudeVector(0, self.__diagonal_0, 0, self.__diagonal_1) + self.__init_space_data_qed(self.__diagonal_0) # here we need to adapt the attributes, defined via this function + # namely self.axis_spaces, self.axis_lengths, self.shape + print(self.axis_spaces) + print(self.axis_lengths) + print(self.shape) + + # since the guesses need the whole matrix object as input, istead of just the diagonals, it is useful to define + # self.elec and self.phot as the the corresponding original matrices + # this is done in one class, since we also require the full matrix for the solver + """ + """ + def __init_space_data_qed(self, diagonal0): + self.__init_space_data(diagonal0) + axis_spaces0 = self.axis_spaces + axis_lengths0 = self.axis_lengths + shape0 = self.shape + self.shape = (shape0[0] * 2 + 1, shape0[0] * 2 + 1) + self.axis_spaces["gs"] = ["o0", "v0"] + self.axis_lengths["gs"] = 1 + # axis_spaces and axis_lengths both are dicts, referring to "ph", "pphh" as a key, so either make extra blocks here, or probably better + # make a longer list in funciton 'axis_blocks', so that later everything for "ph" and "pphh" is just used twice + # also it seems, that self.axis_lengths is not further used, but shape[0] is rather used for that, so not necessary to build self.axis_lengths correctly + """ + + def __init_space_data(self, diagonal): + """Update the cached data regarding the spaces of the ADC matrix""" + self.axis_spaces = {} + self.axis_lengths = {} + for block in diagonal.blocks_ph: + self.axis_spaces[block] = getattr(diagonal, block).subspaces + self.axis_lengths[block] = np.prod([ + self.mospaces.n_orbs(sp) for sp in self.axis_spaces[block] + ]) + self.axis_spaces["gs"] = ["o0", "v0"] + self.axis_lengths["gs"] = 1 + self.shape = (sum(self.axis_lengths.values()), + sum(self.axis_lengths.values())) + + #print(self.axis_spaces) + #print(self.axis_lengths) + #print(self.shape) + + + def __repr__(self): + ret = f"AdcMatrix({self.method.name}, " + for b, o in self.block_orders.items(): + ret += f"{b}={o}, " + return ret + ")" + + def __len__(self): + return self.shape[0] + @property + def blocks(self): + # TODO Remove in 0.16.0 + return self.__diagonal.blocks + + def has_block(self, block): + warnings.warn("The has_block function is deprecated and " + "will be removed in 0.16.0. " + "Use `in matrix.axis_blocks` in the future.") + return self.block_spaces(block) is not None + + def block_spaces(self, block): + warnings.warn("The block_spaces function is deprecated and " + "will be removed in 0.16.0. " + "Use `matrix.axis_spaces[block]` in the future.") + return { + "g": self.axis_spaces.get("gs", None), + "s": self.axis_spaces.get("ph", None), + "d": self.axis_spaces.get("pphh", None), + "t": self.axis_spaces.get("ppphhh", None), + }[block] + + @property #non-qed def axis_blocks(self): """ Return the blocks used along one of the axes of the ADC matrix @@ -175,11 +1045,18 @@ def axis_blocks(self): """ return list(self.axis_spaces.keys()) + #@property #qed + #def axis_blocks(self): + #print(list(self.axis_spaces.keys()) + list(self.axis_spaces.keys())) + # return list(self.axis_spaces.keys()) + list(self.axis_spaces.keys()) + def diagonal(self, block=None): """Return the diagonal of the ADC matrix""" if block is not None: warnings.warn("Support for the block argument will be dropped " "in 0.16.0.") + if block == "g": + return self.__diagonal.gs #this is just a float, but it could be e.g. omega, for which I dont know if required data is given in this function if block == "s": return self.__diagonal.ph if block == "d": @@ -189,10 +1066,11 @@ def diagonal(self, block=None): def compute_apply(self, block, tensor): warnings.warn("The compute_apply function is deprecated and " "will be removed in 0.16.0.") - if block in ("ss", "sd", "ds", "dd"): + if block in ("gg", "gs", "sg" ,"ss", "sd", "ds", "dd"): warnings.warn("The singles-doubles interface is deprecated and " "will be removed in 0.16.0.") - block = {"ss": "ph_ph", "sd": "ph_pphh", + block = {"gg": "gs_gs", "gs": "gs_ph", "sg": "ph_gs", + "ss": "ph_ph", "sd": "ph_pphh", "ds": "pphh_ph", "dd": "pphh_pphh"}[block] return self.block_apply(block, tensor) @@ -319,6 +1197,11 @@ def reduce_index(n_orbsa, idx): # Sort first by spatial, then by spin return (spatial, is_beta) + if "gs" in axis_blocks: + ret_g = [] + ret_g.append([(0, 0), 1]) + ret.extend(ret_g) + if "ph" in axis_blocks: ret_s = [] sp_s = self.axis_spaces["ph"] @@ -371,12 +1254,12 @@ def sortfctn(x): ret_d.sort(key=sortfctn) ret.extend(ret_d) - if any(b not in ("ph", "pphh") for b in self.axis_blocks): - raise NotImplementedError("Blocks other than ph and pphh " + if any(b not in ("gs" ,"ph", "pphh") for b in self.axis_blocks): + raise NotImplementedError("Blocks other than gs, ph and pphh " "not implemented") return ret - def to_ndarray(self, out=None): + def to_ndarray(self, out=None): # this is not adapted to gs """ Return the ADC matrix object as a dense numpy array. Converts the sparse internal representation of the ADC matrix to a dense matrix and return @@ -462,6 +1345,7 @@ def to_ndarray(self, out=None): return out + class AdcBlockView(AdcMatrix): def __init__(self, fullmatrix, block): warnings.warn("The AdcBlockView class got deprecated and will be " diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index 0e858ef3..85c196da 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -22,6 +22,9 @@ ## --------------------------------------------------------------------- import warnings +from numpy.lib.function_base import blackman +import numpy as np + class AmplitudeVector(dict): def __init__(self, *args, **kwargs): @@ -152,6 +155,448 @@ def __matmul__(self, other): return self.dot(other) return NotImplemented + def __forward_to_blocks(self, fname, other): + if isinstance(other, AmplitudeVector): + if sorted(other.blocks_ph) != sorted(self.blocks_ph): + #print(self.blocks_ph, other.blocks_ph) + #print(other.pphh) + raise ValueError("Blocks of both AmplitudeVector objects " + f"need to agree to perform {fname}") + ret = {k: getattr(tensor, fname)(other[k]) + for k, tensor in self.items()} + else: + ret = {k: getattr(tensor, fname)(other) for k, tensor in self.items()} + if any(r == NotImplemented for r in ret.values()): + return NotImplemented + else: + return AmplitudeVector(**ret) + + def __mul__(self, other): + return self.__forward_to_blocks("__mul__", other) + + def __rmul__(self, other): + return self.__forward_to_blocks("__rmul__", other) + + def __sub__(self, other): + return self.__forward_to_blocks("__sub__", other) + + def __rsub__(self, other): + return self.__forward_to_blocks("__rsub__", other) + + def __truediv__(self, other): + return self.__forward_to_blocks("__truediv__", other) + + def __imul__(self, other): + return self.__forward_to_blocks("__imul__", other) + + def __iadd__(self, other): + return self.__forward_to_blocks("__iadd__", other) + + def __isub__(self, other): + return self.__forward_to_blocks("__isub__", other) + + def __itruediv__(self, other): + return self.__forward_to_blocks("__itruediv__", other) + + def __repr__(self): + return "AmplitudeVector(" + "=..., ".join(self.blocks_ph) + "=...)" + + # __add__ is special because we want to be able to add AmplitudeVectors + # with missing blocks + def __add__(self, other): + if isinstance(other, AmplitudeVector): + #print("__add__") + allblocks = sorted(set(self.blocks_ph).union(other.blocks_ph)) + #print(allblocks) + #for k in allblocks: + #print(self.get(k, 0), other.get(k, 0)) + ret = {k: self.get(k, 0) + other.get(k, 0) for k in allblocks} + #print(ret.items()) + ret = {k: v for k, v in ret.items() if v != 0} + else: + #print("__add__ else") + ret = {k: tensor + other for k, tensor in self.items()} + return AmplitudeVector(**ret) + + def __radd__(self, other): + if isinstance(other, AmplitudeVector): + return other.__add__(self) + else: + #print("__radd__ else") + ret = {k: other + tensor for k, tensor in self.items()} + return AmplitudeVector(**ret) + + +class QED_AmplitudeVector: + + #def __init__(self, gs=None, elec=None, gs1=None, phot=None): + def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None): + #if (elec is None and gs is None) and (phot is None and gs1 is None): + # raise ValueError("Either electronic or photonic part must be given.") + + self.gs = gs_vec(gs, is_elec=True) + self.gs1 = gs_vec(gs1, is_elec=False) + if pphh != None: + self.elec = AmplitudeVector(ph=ph, pphh=pphh) + self.phot = AmplitudeVector(ph=ph1, pphh=pphh1) + else: + self.elec = AmplitudeVector(ph=ph) + self.phot = AmplitudeVector(ph=ph1) + try: + self.ph = ph + self.ph1 = ph1 + except AttributeError: + pass + try: + self.pphh = pphh + self.pphh1 = pphh1 + except AttributeError: + # there are no doubles terms --> ADC(0) or ADC(1) + pass + + self.elec0 = self.gs # so the rest of the class doesn't have to be rewritten + self.phot0 = self.gs1 # so the rest of the class doesn't have to be rewritten + #if self.phot is None: + # self.phot = self.elec.zeros_like() #zeros_like(self.elec) + # self.phot0 = 0 + # if self.elec0 is None: + # self.elec0 = 0 + #if self.elec is None: + # self.elec = self.phot.zeros_like() #zeros_like(self.phot) + # self.elec0 = 0 + # if self.phot0 is None: + # self.phot0 = 0 + + #def blocks_ph(self): + # return sorted(self.keys()) + + + + def dot(self, invec): + def dot_(self, invec): + if "pphh" in self.elec.blocks_ph: + return (self.elec0 * invec.elec0 + self.elec.ph.dot(invec.elec.ph) + self.elec.pphh.dot(invec.elec.pphh) + + self.phot0 * invec.phot0 + self.phot.ph.dot(invec.phot.ph) + self.phot.pphh.dot(invec.phot.pphh)) + else: + return self.elec0 * invec.elec0 + self.elec.ph.dot(invec.elec.ph) + self.phot0 * invec.phot0 + self.phot.ph.dot(invec.phot.ph) + if isinstance(invec, list): + list_temp = [] # to return a np.array with different dimensions (gs,ph,pphh), we have to fill them into a list and then convert the list ??? + # even though this list should only be appended by floats, the lower approach didnt work out + #ret = np.array([]) + #(np.append(ret, dot_(self, elem)) for elem in invec) + for elem in invec: + list_temp.append(dot_(self, elem)) + return np.array(list_temp) + else: + return dot_(self, invec) + + #def __matmul__(self, invec): + # return self.dot(invec) + + def __matmul__(self, other): + if isinstance(other, QED_AmplitudeVector): + return self.dot(other) + if isinstance(other, list): + #print("list given in QED matmul") + if all(isinstance(elem, QED_AmplitudeVector) for elem in other): + return self.dot(other) + return NotImplemented + + #def __add__(self, invec): # special add function, see AmplitudeVector __add__ + # # this is only used for the summation of gs and gs1 blocks in matvec from AdcMatrix, because the other blocks are covered by AmplitudeVector + # if isinstance(invec, QED_AmplitudeVector): + # + # else: + # return NotImplementedError("__add__ in QED_AmplitudeVector only with QED_AmplitudeVector!") + + #def __add__(self, invec): + # return QED_AmplitudeVector(self.elec0 + invec.elec0, self.elec.__add__(invec.elec), self.phot0 + invec.phot0, self.phot.__add__(invec.phot)) + + #def __radd__(self, invec): + # return QED_AmplitudeVector(self.elec0 + invec.elec0, self.elec.__add__(invec.elec), self.phot0 + invec.phot0, self.phot.__add__(invec.phot)) + + def __sub__(self, invec): + if isinstance(invec, QED_AmplitudeVector): + return QED_AmplitudeVector(self.elec0 - invec.elec0, self.elec.__sub__(invec.elec), self.phot0 - invec.phot0, self.phot.__sub__(invec.phot)) + elif isinstance(invec, (float, int)): # for diagonal - shift in preconditioner.py + # this results in a scalar in block pphh, if pphh is originally None + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(gs=self.elec0 - invec, ph=self.elec.ph.__sub__(invec), pphh=self.elec.pphh.__sub__(invec), + gs1=self.phot0 - invec, ph1=self.phot.ph.__sub__(invec), pphh1=self.phot.pphh.__sub__(invec)) + else: + return QED_AmplitudeVector(gs=self.elec0 - invec, ph=self.elec.ph.__sub__(invec), gs1=self.phot0 - invec, ph1=self.phot.ph.__sub__(invec)) + + def __mul__(self, scalar): + return QED_AmplitudeVector(scalar * self.elec0, self.elec.__mul__(scalar), scalar * self.phot0, self.phot.__mul__(scalar)) + + def __rmul__(self, scalar): + return QED_AmplitudeVector(scalar * self.elec0, self.elec.__rmul__(scalar), scalar * self.phot0, self.phot.__rmul__(scalar)) + + def __truediv__(self, other): + #print(type(self.elec), self.elec.ph) + #print(type(other.elec), other.elec.ph) + #if "pphh" in self.elec.blocks_ph: + # return QED_AmplitudeVector(gs=self.elec0 / other.elec0, ph=self.elec.__truediv__(other.elec), + # gs1=self.phot0 / other.phot0, ph1=self.phot.__truediv__(other.phot)) + #else: + if isinstance(other, QED_AmplitudeVector): + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(gs=self.elec0 / other.elec0, ph=self.elec.ph.__truediv__(other.elec.ph), pphh=self.elec.pphh.__truediv__(other.elec.pphh), + gs1=self.phot0 / other.phot0, ph1=self.phot.ph.__truediv__(other.phot.ph), pphh1=self.phot.pphh.__truediv__(other.phot.pphh)) + else: + return QED_AmplitudeVector(gs=self.elec0 / other.elec0, ph=self.elec.ph.__truediv__(other.elec.ph), + gs1=self.phot0 / other.phot0, ph1=self.phot.ph.__truediv__(other.phot.ph)) + elif isinstance(other, (float, int)): + #return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), + # gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other)) + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), pphh=self.elec.pphh.__truediv__(other), + gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other), pphh1=self.phot.pphh.__truediv__(other)) + else: + return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), + gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other)) + + def zeros_like(self): + return QED_AmplitudeVector(0, self.elec.zeros_like(), 0, self.phot.zeros_like()) + + def empty_like(self): + return QED_AmplitudeVector([], self.elec.empty_like(), [], self.phot.empty_like()) + + def copy(self): + return QED_AmplitudeVector(self.elec0, self.elec.copy(), self.phot0, self.phot.copy()) + + def evaluate(self): + #print(self.elec0, self.elec, self.phot0, self.phot) + self.elec.evaluate() + self.phot.evaluate() + try: + self.elec0.evaluate() + self.phot0.evaluate() + except: + self.elec0 + self.phot0 + return self + + + + + +class gs_vec: + # this class only exists, to forward gs and gs1 to the relevant operators + def __init__(self, val, is_elec): + #print("uses gs_vec init") + self.val = val + #print(type(self.val)) + if isinstance(self.val, AmplitudeVector): + TypeError("gs_vec got invoked with AmplitudeVector type") + #try: + # self.val = float(arg) + #except: + # raise NotImplementedError("gs_vec needs to be given just one number," + # "which has to be float convertible") + def __mul__(self, invec): + if isinstance(invec, (float, int)): + #print("uses gs_vec __mul__") + return self.val * invec + elif isinstance(invec, AmplitudeVector): + return invec.__mul__(self.val) + elif isinstance(invec, gs_vec): + return self.val * invec.val + + def __rmul__(self, scalar): + #print("uses gs_vec __rmul__") + return self.val * scalar + + def __add__(self, scalar): + if self.val == None or scalar == None: + return self.val + else: + #print("uses add gs_vec", self.val, scalar) + #print(type(self.val)) + return self.val + scalar + + def __radd__(self, scalar): + return self.__add__(scalar) + #if self.val == None: + # return scalar + #else: + # #print("uses radd gs_vec", self.val, scalar) + # return self.val + scalar + + def __sub__(self, scalar): + if isinstance(scalar, gs_vec): + return self.val - scalar.val + elif isinstance(scalar, (float, int)): + return self.val - scalar + + def __truediv__(self, scalar): + if isinstance(scalar, gs_vec): + return self.val / scalar.val + elif isinstance(scalar, (float, int)): + return self.val / scalar + + def as_float(self): + return self.val + + + + + + + +""" +class QED_AmplitudeVector(dict): + # This vector contains the structure (ph, ph1) or (ph, pphh, ph1, pphh1), where the + # zero excitation space is included at the beginning of each ph (see matrix.py). + # The upper and the lower half of the vector have photonic intermediate states 0 and 1, + # respectively, and we abbreviate ph0 and pphh0 by ph and pphh. + + # we first implement the <0|0> block, but with the groundstate terms, gs = grounstate + def __init__(self, *args, **kwargs): + """ + #Construct an AmplitudeVector. Typical use cases are + #``AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles)``. +""" + if args: + warnings.warn("Using the list interface of AmplitudeVector is " + "deprecated and will be removed in version 0.16.0. Use " + "AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles) " + "instead.") + if len(args) == 2: + super().__init__(gs=args[0], ph=args[1]) + elif len(args) == 4: + super().__init__(gs=args[0], ph=args[1], pphh1=args[2]) + else: + super().__init__(**kwargs) + + def __getattr__(self, key): + if self.__contains__(key): + return self.__getitem__(key) + raise AttributeError + + def __setattr__(self, key, item): + if self.__contains__(key): + return self.__setitem__(key, item) + raise AttributeError + + @property + def blocks(self): + warnings.warn("The blocks attribute will change behaviour in 0.16.0.") + if sorted(self.blocks_ph) == ["gs", "ph", "pphh"]: + return ["g", "s", "d"] + if sorted(self.blocks_ph) == ["gs", "ph"]: + return ["g", "s"] + if sorted(self.blocks_ph) == ["pphh"]: + return ["d"] + elif sorted(self.blocks_ph) == ["ph"]: + return ["s"] + elif sorted(self.blocks_ph) == ["gs"]: + return ["g"] + elif sorted(self.blocks_ph) == []: + return [] + else: + raise NotImplementedError(self.blocks_ph) + + @property + def blocks_ph(self): # despite the name, this function can be applied to all blocks + """ + #Return the blocks which are used inside the vector. + #Note: This is a temporary name. The attribute will be removed in 0.16.0. +""" + return sorted(self.keys()) + + def __getitem__(self, index): + if index in (0, 1, 2, "g", "s", "d"): + warnings.warn("Using the list interface of AmplitudeVector is " + "deprecated and will be removed in version 0.16.0. Use " + "block labels like 'ph', 'pphh' instead.") + if index in (0, "g"): + return self.__getitem__("gs") + elif index in (1, "s"): + return self.__getitem__("ph") + elif index in (2, "d"): + return self.__getitem__("pphh") + #elif index in (3, "d1"): + # return self.__getitem__("pphh1") + else: + raise KeyError(index) + else: + return super().__getitem__(index) + + def __setitem__(self, index, item): + if index in (0, 1, 2, "g", "s", "d"): + warnings.warn("Using the list interface of AmplitudeVector is " + "deprecated and will be removed in version 0.16.0. Use " + "block labels like 'ph', 'pphh' instead.") + if index in (0, "g"): + return self.__setitem__("gs", item) + elif index in (1, "s"): + return self.__setitem__("ph", item) + elif index in (2, "d"): + return self.__setitem__("pphh", item) + #elif index in (3, "d1"): + # return self.__setitem__("pphh1", item) + else: + raise KeyError(index) + else: + return super().__setitem__(index, item) + + def copy(self): + """#Return a copy of the AmplitudeVector +""" + return AmplitudeVector(**{k: t.copy() for k, t in self.items()}) + + def evaluate(self): + for t in self.values(): + t.evaluate() + return self + + def ones_like(self): + """#Return an empty AmplitudeVector of the same shape and symmetry +""" + return AmplitudeVector(**{k: t.ones_like() for k, t in self.items()}) + + def empty_like(self): + """#Return an empty AmplitudeVector of the same shape and symmetry +""" + return AmplitudeVector(**{k: t.empty_like() for k, t in self.items()}) + + def nosym_like(self): + """#Return an empty AmplitudeVector of the same shape and symmetry +""" + return AmplitudeVector(**{k: t.nosym_like() for k, t in self.items()}) + + def zeros_like(self): + """#Return an AmplitudeVector of the same shape and symmetry with + #all elements set to zero +""" + return AmplitudeVector(**{k: t.zeros_like() for k, t in self.items()}) + + def set_random(self): + for t in self.values(): + t.set_random() + return self + + def dot(self, other): + """#Return the dot product with another AmplitudeVector + #or the dot products with a list of AmplitudeVectors. + #In the latter case a np.ndarray is returned. +""" + if isinstance(other, list): + # Make a list where the first index is all singles parts, + # the second is all doubles parts and so on + return sum(self[b].dot([av[b] for av in other]) for b in self.keys()) + else: + return sum(self[b].dot(other[b]) for b in self.keys()) + + def __matmul__(self, other): + if isinstance(other, AmplitudeVector): + return self.dot(other) + if isinstance(other, list): + if all(isinstance(elem, AmplitudeVector) for elem in other): + return self.dot(other) + return NotImplemented + def __forward_to_blocks(self, fname, other): if isinstance(other, AmplitudeVector): if sorted(other.blocks_ph) != sorted(self.blocks_ph): @@ -213,3 +658,5 @@ def __radd__(self, other): else: ret = {k: other + tensor for k, tensor in self.items()} return AmplitudeVector(**ret) + +""" \ No newline at end of file diff --git a/adcc/DataHfProvider.py b/adcc/DataHfProvider.py index f6c431d3..fa7eb2c3 100644 --- a/adcc/DataHfProvider.py +++ b/adcc/DataHfProvider.py @@ -233,6 +233,7 @@ def get_conv_tol(self): return get_scalar_value(self.data, "threshold") def fill_occupation_f(self, out): + print("DataHfProvider.py is used") out[:] = self.data["occupation_f"] def fill_orbcoeff_fb(self, out): diff --git a/adcc/HfCounterData.py b/adcc/HfCounterData.py index 2a158812..4ea9a1c0 100644 --- a/adcc/HfCounterData.py +++ b/adcc/HfCounterData.py @@ -115,6 +115,7 @@ def fill_orbcoeff_fb(self, out): + self.get_b_range()[None, :]) def fill_occupation_f(self, out): + print("HfCounterData.py is used") n_oa = self.__n_orbs_alpha out[:] = np.zeros(2 * n_oa) out[:self.__n_alpha] = 1. diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index ae8d4f9c..a46ed0da 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -47,6 +47,12 @@ def __init__(self, hf): self.mospaces = hf.mospaces self.timer = Timer() self.has_core_occupied_space = hf.has_core_occupied_space + #for qed mp2 + self.get_qed_total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + self.get_qed_total_dip.oo = hf.get_qed_total_dip(b.oo) + self.get_qed_total_dip.ov = hf.get_qed_total_dip(b.ov) + self.get_qed_total_dip.vv = hf.get_qed_total_dip(b.vv) + self.get_qed_omega = hf.get_qed_omega def __getattr__(self, attr): # Shortcut some quantities, which are needed most often @@ -195,18 +201,124 @@ def dipole_moment(self, level=2): raise NotImplementedError("Only dipole moments for level 1 and 2" " are implemented.") + @cached_member_function + def qed_t1_df(self, space): + #if space != b.ov: + #raise NotImplementedError("qed_t1 term not implemented " + # f"for space {space}.") + total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + if space == b.oo: + total_dip.oo = self.get_qed_total_dip.oo + return total_dip.oo + elif space == b.ov: + total_dip.ov = self.get_qed_total_dip.ov + return total_dip.ov + elif space == b.vv: + total_dip.vv = self.get_qed_total_dip.vv + return total_dip.vv + #return total_dip.ov #/ self.df(b.ov) + + @cached_member_function + def qed_t1(self, space): + """ Return new electronic singly excited amplitude in the first order correction to the wavefunction for qed for N=1 """ + if space != b.ov: + raise NotImplementedError("qed_t1 term not implemented " + f"for space {space}.") + return self.qed_t1_df(b.ov) / self.df(b.ov) #einsum("kc,kc->kc", self.qed_t1(b.ov), self.df(b.ov)) + + @cached_member_function + def qed_t0_df(self, space): + total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + total_dip.oo = self.get_qed_total_dip.oo + total_dip.ov = self.get_qed_total_dip.ov + total_dip.vv = self.get_qed_total_dip.vv + if space == b.ov: + occ_sum = einsum("ka,ki->ia", total_dip.ov, total_dip.oo) + virt_sum = einsum("ac,ic->ia", total_dip.vv, total_dip.ov) + #return einsum("ka,ki->ia", total_dip.ov, total_dip.oo) - einsum("ac,ic->ia", total_dip.vv, total_dip.ov) + #print(total_dip.ov) + #return einsum("ia,ia->ia", occ_sum, virt_sum) + elif space == b.oo: + occ_sum = einsum("ki,kj->ij", total_dip.oo, total_dip.oo) + virt_sum = einsum("ic,jc->ij", total_dip.ov, total_dip.ov) + elif space == b.vv: + occ_sum = einsum("ka,kb->ab", total_dip.ov, total_dip.ov) + virt_sum = einsum("ac,bc->ab", total_dip.vv, total_dip.vv) + return occ_sum - virt_sum + + @cached_member_function + def qed_t0(self, space): + """ Return new electronic singly excited amplitude in the first order correction to the wavefunction for qed for N=0 """ + if space != b.ov: + raise NotImplementedError("qed_t0 term not implemented " + f"for space {space}.") + return self.qed_t0_df(b.ov) / self.df(b.ov) + + @cached_member_function + def diff_df(self, space): + if space == b.ov: + raise NotImplementedError("This would not make sense to construct!!!") + elif space == b.vv: # this returns (eps_a - eps_b) + return einsum("ia,ib->ab", self.df(b.ov), - self.df(b.ov)) + elif space == b.oo: # this returns (- eps_i + eps_j) + return einsum("ia,ja->ij", self.df(b.ov), - self.df(b.ov)) + + + @cached_member_function def energy_correction(self, level=2): """Obtain the MP energy correction at a particular level""" + qed_mp2_correction = 0 if level > 3: raise NotImplementedError(f"MP({level}) energy correction " "not implemented.") - if level < 2: + if level < 2: #for qed_mp1 from non-qed-hf also first corrections come into play...for now done in mp2 part here return 0.0 hf = self.reference_state is_cvs = self.has_core_occupied_space if level == 2 and not is_cvs: terms = [(1.0, hf.oovv, self.t2oo)] + mp2_correction = sum( + -0.25 * pref * eri.dot(t2) + for pref, eri, t2 in terms + ) + if hasattr(hf, "coupling"): + print("mp2 energy with two electron qed perturbation " + str(mp2_correction)) + #check if qed-hf (psi4.core.Wavefunction) input or non-qed-hf input (standard hf) is given + total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + omega, total_dip.ov = ReferenceState.get_qed_omega(hf), self.get_qed_total_dip.ov + qed_terms = [(omega/2, total_dip.ov, self.qed_t1(b.ov))] + qed_mp2_correction_1 = sum( + -pref * lambda_dip.dot(qed_t) + for pref, lambda_dip, qed_t in qed_terms + ) + if hasattr(hf, "qed_hf"): + print("full qed MP2 energy correction (qed-hf) " + str(mp2_correction + qed_mp2_correction_1)) + else: + #total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + #omega, total_dip.ov = ReferenceState.get_qed_omega(hf), self.get_qed_total_dip.ov + #qed_terms_1 = [(omega/2, total_dip.ov, self.qed_t1(b.ov))] + #qed_mp2_correction_1 = sum( + # -pref * lambda_dip.dot(qed_t) + # for pref, lambda_dip, qed_t in qed_terms_1 + #) + qed_terms_0 = [(1.0, self.qed_t0(b.ov), self.qed_t0_df(b.ov))] + qed_mp2_correction_0 = sum( + -0.25 * pref * ampl_t0.dot(ampl_t0_df) + for pref, ampl_t0, ampl_t0_df in qed_terms_0 + ) + #mp1 terms: + qed_mp1_additional_terms = [(0.5, total_dip.ov)] + qed_mp1_correction = sum( + pref * lambda_dip.dot(lambda_dip) + for pref, lambda_dip in qed_mp1_additional_terms + ) + #print(self.qed_t0(b.ov)) + #print(self.qed_t0_df(b.ov)) + #print(qed_mp2_correction_0) + print("full qed MP2 energy correction (standard hf) " + + str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0)) + print("qed-mp1 correction, due to standard hf input " + str(qed_mp1_correction)) elif level == 2 and is_cvs: terms = [(1.0, hf.oovv, self.t2oo), (2.0, hf.ocvv, self.t2oc), @@ -218,7 +330,7 @@ def energy_correction(self, level=2): return sum( -0.25 * pref * eri.dot(t2) for pref, eri, t2 in terms - ) + ) + qed_mp2_correction def energy(self, level=2): """ diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index a74e79c8..f56fd0de 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -28,6 +28,7 @@ from .backends import import_scf_results from .OperatorIntegrals import OperatorIntegrals from .OneParticleOperator import OneParticleOperator, product_trace +from .misc import cached_member_function import libadcc @@ -164,9 +165,93 @@ def __getattr__(self, attr): if attr.startswith("f"): return self.fock(b.__getattr__(attr[1:])) + elif attr.startswith("get_qed_total_dip"): + return self.get_qed_total_dip(b.__getattr__(attr)) + elif attr.startswith("get_qed_omega"): + return self.get_qed_omega else: return self.eri(b.__getattr__(attr)) + @cached_member_function + def get_qed_total_dip(self, block): + if hasattr(self, "coupling"): + from . import block as b + dips = self.operators.electric_dipole + #import ctypes + #dips_id_int = int(dips, base=16) + #print(ctypes.cast(dips_id_int, ctypes.py_object).value) + #print(dips[block]) + couplings = self.coupling + freqs = self.frequency + #test_el_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + #omega_square = x**2 for x in freqs + #omega = np.linalg.norm(freqs) + total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + for coupling, freq, dip in zip(couplings, freqs, dips): + total_dip += coupling * np.sqrt(2 * freq) * dip + #test_el_dip = dip + total_dip.evaluate() + #test_el_dip.evaluate() + #print(str(block)) + #print(test_el_dip[block]) + #print(total_dip[block]) + return total_dip[block] + + @cached_member_function + def get_qed_omega(self): + if hasattr(self, "coupling"): + freqs = self.frequency + omega = np.linalg.norm(freqs) + return omega + + @cached_member_function + def qed_D_object(self, block): + if hasattr(self, "coupling"): + from . import block as b + from .functions import einsum + total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + omega = ReferenceState.get_qed_omega(self) + total_dip.oo = ReferenceState.get_qed_total_dip(self, b.oo) + total_dip.ov = ReferenceState.get_qed_total_dip(self, b.ov) + total_dip.vv = ReferenceState.get_qed_total_dip(self, b.vv) + # We have to define all the blocks from the D_{pqrs} = d_{pr} d_{qs} - d_{ps} d_{qr} object, which has the + # same symmetry properties as the ERI object + # Actually in b.ovov: second term: ib,ja would be ib,aj , but d_{ia} = d_{ai}, and d.ov is implemented (as usual) + ds = { + b.oooo: einsum('ik,jl->ijkl', total_dip.oo, total_dip.oo) - einsum('il,jk->ijkl', total_dip.oo, total_dip.oo), + b.ooov: einsum('ik,ja->ijka', total_dip.oo, total_dip.ov) - einsum('ia,jk->ijka', total_dip.ov, total_dip.oo), + b.oovv: einsum('ia,jb->ijab', total_dip.ov, total_dip.ov) - einsum('ib,ja->ijab', total_dip.ov, total_dip.ov), + b.ovvv: einsum('ib,ac->iabc', total_dip.ov, total_dip.vv) - einsum('ic,ab->iabc', total_dip.ov, total_dip.vv), + b.ovov: einsum('ij,ab->iajb', total_dip.oo, total_dip.vv) - einsum('ib,ja->iajb', total_dip.ov, total_dip.ov), + b.vvvv: einsum('ac,bd->abcd', total_dip.vv, total_dip.vv) - einsum('ad,bc->abcd', total_dip.vv, total_dip.vv), + } + return ds[block] + + def eri(self, block): + if hasattr(self, "coupling"): + from . import block as b + from .functions import einsum + ds_init = OneParticleOperator(self.mospaces, is_symmetric=True) #Since there is no TwoParticleOperator we do this + ds = { + b.oooo: einsum('ik,jl->ijkl', ds_init.oo, ds_init.oo), + b.ooov: einsum('ik,ja->ijka', ds_init.oo, ds_init.ov), + b.oovv: einsum('ia,jb->ijab', ds_init.ov, ds_init.ov), + b.ovvv: einsum('ib,ac->iabc', ds_init.ov, ds_init.vv), + b.ovov: einsum('ij,ab->iajb', ds_init.oo, ds_init.vv), + b.vvvv: einsum('ac,bd->abcd', ds_init.vv, ds_init.vv), + } + ds[block] = ReferenceState.qed_D_object(self, block) + return super().eri(block) + ds[block] + else: + #raise InvalidReference( + #"Please define the attribute coupling and frequency to the Psi4 wfn object, before importing it in adcc!" + #"e.g. : refstate = adcc.ReferenceState(wfn)" + #"refstate.coupling = [x, y, z] # Just as you did in hilbert" + #"refstate.frequency = [x, y, z] # Just as you did in hilbert" + #"state = adcc.adc2(refstate, n_singlets=3)" + #) + return super().eri(block) + @property def mospaces(self): return self._mospaces diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index f4892a37..dde65599 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -22,11 +22,15 @@ ## --------------------------------------------------------------------- from math import sqrt from collections import namedtuple +import numpy as np +from numpy.lib.function_base import blackman from adcc import block as b from adcc.functions import direct_sum, einsum, zeros_like from adcc.Intermediates import Intermediates, register_as_intermediate -from adcc.AmplitudeVector import AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from adcc.ReferenceState import ReferenceState +from adcc.OneParticleOperator import OneParticleOperator __all__ = ["block"] @@ -85,24 +89,233 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): return globals()[fn](reference_state, ground_state, intermediates) + +# Since we already have all ph and pphh routines at hand, we will construct 4 dirrefent matrices, +# which will be evaluated by the original AdcMatrix, which is now the submatrix. +# We will construct the matrix as follows: +# elec phot_couple +# elec_couple phot +# where we wont write elec explicitly. +# These blocks can then be forwarded to the AdcMatrix_submatrix, except for the gs part, which has to be +# treated separately. In the matvec we then construct the vector as: +# elec +# phot +# We therefore build an AmplitudeVector for the ph and pphh blocks, while inserting QED_AmplitudeVector.ph or .pphh, +# which are also AmplitudeVector classes +# It also seems smart, to insert ph_gs and pphh_gs into the ph_ph and pphh_ph blocks, respectively. This should also be fine with +# construct_symmetrisation_for_blocks, which enforces the correct symmetry for the doubles block, which does probably not (?) +# make a difference for the gs_pphh blocks, since they reduce to gs, so it should be fine to do so. -> It should be fine, +# since after using the Jacobi-davidson preconditioner by dividing the residuals with the shifted matrix-diagonal, the symmetry could +# be lost (?), but in the gs part, which is already only one value, the symmetry is still enforced. +# Maybe its also a good idea to pack gs_gs, gs_ph and gs_pphh into one block, which returns one number for apply and the gs_gs part +# for the diagonal. +# Using both of these "block in other block" ideas would leave one with 24 total blocks, instead of 36, for each order +# (original 4 blocks + ph_gs and pphh_gs) per "ADC submatrix" +# maybe return float from ph/pphh_gs blocks, instead of QED_AmplitudeVector.gs/.gs1 + + + + # # 0th order main # -def block_ph_ph_0(hf, mp, intermediates): - fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), - fCC.diagonal())) + +""" +def block_gs_gs_0(hf, mp, intermediates): # this is zero, but we want to give some test value here + omega = float(ReferenceState.get_qed_omega(hf)) + #diagonal = QED_AmplitudeVector(gs=omega) def apply(ampl): - return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph, hf.fvv) - - einsum("IJ,Ja->Ia", fCC, ampl.ph) - )) + #print("printing type(ampl)") + #print(type(ampl), ampl) + #print(type(ampl.gs)) + #print(ampl.gs) + return QED_AmplitudeVector(gs=(0 * ampl.gs)) + return AdcBlock(apply, 0) + +def block_gs_gs_0_couple(hf, mp, intermediates): + def apply(ampl): + return QED_AmplitudeVector(gs=(0 * ampl.gs)) + return AdcBlock(apply, 0) + +def block_gs_gs_0_phot_couple(hf, mp, intermediates): + def apply(ampl): + return QED_AmplitudeVector(gs1=(0 * ampl.gs1)) + return AdcBlock(apply, 0) + +#block_gs_gs_0_phot = block_gs_gs_0 + +def block_gs_gs_0_phot(hf, mp, intermediates): # this is zero, but we want to give some test value here + omega = float(ReferenceState.get_qed_omega(hf)) + #diagonal = QED_AmplitudeVector(gs=omega) + def apply(ampl): + #print(type(ampl.gs1), ampl) + #print(ampl.gs) + return QED_AmplitudeVector(gs1=(0 * ampl.gs1)) + return AdcBlock(apply, 0) + +def block_gs_ph_0(hf, mp, intermediates): # this is zero, but we want to give some test value here + #return AdcBlock(lambda ampl: 0, 0) + #omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + terms = [(mp.df(b.ov), ampl.ph)] + return QED_AmplitudeVector(gs=sum(mpdf.dot(amplph) + for mpdf, amplph in terms)) + return AdcBlock(apply, 0) + +def block_gs_ph_0_couple(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + #def apply(ampl): + # return QED_AmplitudeVector(ph=(omega * ampl.ph)) + #return AdcBlock(apply, 0) + +def block_gs_ph_0_phot_couple(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +def block_gs_ph_0_phot(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +def block_ph_gs_0(hf, mp, intermediates): # this is zero, but we want to give some test value here + #return AdcBlock(lambda ampl: 0, 0) + #omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(ph=(mp.df(b.ov) * ampl.gs)) + return AdcBlock(apply, 0) + +def block_ph_gs_0_couple(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +def block_ph_gs_0_phot_couple(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +def block_ph_gs_0_phot(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) +""" + +# +# 0th order gs blocks (gs_ph blocks in ph_ph, which are zero for this order) +# + +def block_ph_gs_0(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +block_ph_gs_0_couple = block_ph_gs_0_phot_couple = block_ph_gs_0_phot = block_ph_gs_0 + + +def block_pphh_gs_0(hf, mp, intermediates): + return AdcBlock(lambda ampl:0, 0) + +block_pphh_gs_0_couple = block_pphh_gs_0_phot_couple = block_pphh_gs_0_phot = block_pphh_gs_0 + +""" +def block_gs_ph_0(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=(0 * ampl.ph)) + return AdcBlock(apply, 0) + +def block_gs_ph_0_phot_couple(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=(0 * ampl.ph)) + return AdcBlock(apply, 0) + +def block_gs_ph_0_couple(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=(0 * ampl.ph1)) + return AdcBlock(apply, 0) + +def block_gs_ph_0_phot(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=(0 * ampl.ph1)) + return AdcBlock(apply, 0) +""" +#def block_pphh_gs_0(hf, mp, intermediates): +# return AdcBlock(0, 0) + +#block_pphh_gs_0_couple = block_pphh_gs_0_phot_couple = block_pphh_gs_0_phot = block_pphh_gs_0 + +# +# 0th order main +# + +def block_ph_ph_0(hf, mp, intermediates): + fCC = hf.fcc if hf.has_core_occupied_space else hf.foo + if hasattr(hf, "coupling"):# and hasattr(hf, "qed_hf"): + diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), #change to QED_AmplitudeVector + fCC.diagonal())) + + #np.insert(diagonal, 0, 0.) + #diagonal = np.zeros(np.add(diagonal_.shape, np.array([0]))) + #diagonal[1:,1:] = diagonal_ + print("new stuff works???") + #print(diagonal) + + def apply(ampl): + mvprod = AmplitudeVector(ph=( #change to QED_AmplitudeVector + + einsum("ib,ab->ia", ampl.ph, hf.fvv) + - einsum("IJ,Ja->Ia", fCC, ampl.ph) + )) + #np.insert(mvprod, 0, 0.) + #mvprod = np.zeros(np.add(mv_prod_.shape, np.array([0]))) + #mvprod[1:,1:] = mvprod_ + return mvprod + else: + diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), + fCC.diagonal())) + + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph, hf.fvv) + - einsum("IJ,Ja->Ia", fCC, ampl.ph) + )) return AdcBlock(apply, diagonal) block_cvs_ph_ph_0 = block_ph_ph_0 +def block_ph_ph_0_couple(hf, mp, intermediates): # we also give these blocks zero diagonals, so the submatrix routine does not require adjustments + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + return AdcBlock(lambda ampl: 0, diagonal) +# def apply(ampl): +# return AmplitudeVector(ph=(0 * ampl.ph)) +# return AdcBlock(apply, diagonal) + +block_ph_ph_0_phot_couple = block_ph_ph_0_couple + +#def block_ph_ph_0_phot_couple(hf, mp, intermediates): +# diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) +# def apply(ampl): +# return AmplitudeVector(ph=(0 * ampl.ph1)) +# return AdcBlock(apply, diagonal) + + +def block_ph_ph_0_phot(hf, mp, intermediates): + fCC = hf.fcc if hf.has_core_occupied_space else hf.foo + if hasattr(hf, "coupling"):# and hasattr(hf, "qed_hf"): + diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), + fCC.diagonal())) + + #np.insert(diagonal, 0, 0.) + print("new stuff works???") + #print(diagonal) + + def apply(ampl): + mvprod = AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph1, hf.fvv) + - einsum("IJ,Ja->Ia", fCC, ampl.ph1) + )) + #np.insert(mvprod, 0, 0.) + return mvprod + else: + raise NotImplementedError("coupling needs to be given to reference wavefunction in input file for QED-ADC") + return AdcBlock(apply, diagonal) + +#def block_ph_ph1_0(hf, mp, intermediates): +# return AdcBlock(lambda ampl: 0, 0) + +#def block_ph1_ph_0(hf, mp, intermediates): +# return AdcBlock(lambda ampl: 0, 0) + + def diagonal_pphh_pphh_0(hf): # Note: adcman similarly does not symmetrise the occupied indices @@ -123,6 +336,22 @@ def apply(ampl): return AdcBlock(apply, diagonal_pphh_pphh_0(hf)) +def block_pphh_pphh_0_couple(hf, mp, intermediates): + diagonal = AmplitudeVector(pphh=(mp.t2oo.zeros_like())) + return AdcBlock(lambda ampl: 0, diagonal) + + +block_pphh_pphh_0_phot_couple = block_pphh_pphh_0_couple + +def block_pphh_pphh_0_phot(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(pphh=( + + 2 * einsum("ijac,bc->ijab", ampl.pphh1, hf.fvv).antisymmetrise(2, 3) + - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh1).antisymmetrise(0, 1) + )) + return AdcBlock(apply, diagonal_pphh_pphh_0(hf)) + + def block_cvs_pphh_pphh_0(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pphh=( @@ -147,6 +376,63 @@ def block_pphh_ph_0(hf, mp, intermediates): block_cvs_ph_pphh_0 = block_ph_pphh_0 block_cvs_pphh_ph_0 = block_pphh_ph_0 +block_pphh_ph_0_couple = block_pphh_ph_0_phot_couple = block_pphh_ph_0_phot = block_pphh_ph_0 +block_ph_pphh_0_couple = block_ph_pphh_0_phot_couple = block_ph_pphh_0_phot = block_ph_pphh_0 + + +# +# 1st order gs blocks (gs_ph blocks in ph_ph) +# + +def block_ph_gs_1(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +def block_ph_gs_1_phot(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return omega * ampl.gs1 + return AdcBlock(apply, omega) + +#def block_ph_gs_1_phot_couple(hf, mp, intermediates): +# return AdcBlock(lambda ampl: 0, 0) +block_ph_gs_1_phot_couple = block_ph_gs_1 + +def block_ph_gs_1_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return (-1) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph) + return AdcBlock(apply, 0) + #return AdcBlock(lambda ampl: 0, 0) + + +def block_pphh_gs_1(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +block_pphh_gs_1_couple = block_pphh_gs_1_phot_couple = block_pphh_gs_1_phot = block_pphh_gs_1 + + +""" +def block_gs_ph_1(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=(0 * ampl.ph)) + return AdcBlock(apply, 0) + +def block_gs_ph_1_phot(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=(0 * ampl.ph1)) + return AdcBlock(apply, 0) + +def block_gs_ph_1_phot_couple(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=(mp.qed_t1_df(b.ov) * (-ampl.gs1.as_float()))) + return AdcBlock(apply, 0) + +def block_gs_ph_1_couple(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=(0 * ampl.ph)) + return AdcBlock(apply, 0) +""" + # # 1st order main @@ -154,23 +440,172 @@ def block_pphh_ph_0(hf, mp, intermediates): def block_ph_ph_1(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - - einsum("IaIa->Ia", CvCv) # order 1 - )) + omega = float(ReferenceState.get_qed_omega(hf)) # only for test purposes + if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): + #diag_qed_term = einsum("klkl->", hf.oooo) + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 + #+ (1/2) * einsum("klkl->", hf.oooo) + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) + #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) #reintroduced (actually canceled from -E_0 (1)) + )) - def apply(ampl): - return AmplitudeVector(ph=( # PT order - + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 - - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 - - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 + #d_vv = zeros_like(hf.fvv) + #d_vv.set_mask("aa", 1.0) + + def apply(ampl): + return AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 + #+ (1/2) * einsum("klkl->", hf.oooo) * ampl.ph + + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph) + - (1/2) * einsum("ib,ab->ia", ampl.ph, mp.qed_t0_df(b.vv)) + #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #reintroduced (actually canceled from -E_0 (1) + )) + elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): + omega = float(ReferenceState.get_qed_omega(hf)) # omega is only here for a test...actually omega does not appear in this block + diagonal = AmplitudeVector(ph=( #change to QED_AmplitudeVector + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 + )) + #print(diagonal.set_random()) + #np.insert(diagonal, 0, 0) + #diagonal = np.zeros(np.add(diagonal_.shape, np.array([0]))) + #diagonal[1:,1:] = diagonal_ + + def apply(ampl): + mvprod = AmplitudeVector(ph=( #change to QED_AmplitudeVector # PT order + + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 + )) + #np.insert(mvprod, 0, 0) + #mvprod = np.zeros(np.add(mv_prod_.shape, np.array([0]))) + #mvprod[1:,1:] = mvprod_ + return mvprod + else: + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 )) + + def apply(ampl): + return AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 + )) return AdcBlock(apply, diagonal) block_cvs_ph_ph_1 = block_ph_ph_1 +def block_ph_ph_1_phot(hf, mp, intermediates): + fCC = hf.fcc if hf.has_core_occupied_space else hf.foo + CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov + if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): + #diag_qed_term = einsum("klkl->", hf.oooo) + omega = float(ReferenceState.get_qed_omega(hf)) + + # Build two Kronecker deltas + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 + #+ (1/2) * einsum("klkl->", hf.oooo) + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) + #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) #reintroduced (actually canceled from -E_0 (1)) + + einsum("ii,aa->ia", d_oo, d_vv) * omega + )) + + def apply(ampl): + return AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 + #+ (1/2) * einsum("klkl->", hf.oooo) * ampl.ph + + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) + - (1/2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) + #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #reintroduced (actually canceled from -E_0 (1) + + omega * ampl.ph1 + )) + elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): + omega = float(ReferenceState.get_qed_omega(hf)) + + # Build two Kronecker deltas + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 + + einsum("ii,aa->ia", d_oo, d_vv) * omega + )) + #np.insert(diagonal, 0, omega) + + def apply(ampl): + mvprod = AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 + + omega * ampl.ph1 + )) + #np.insert(mvprod, 0, omega) + return mvprod + else: + raise NotImplementedError("and not hasattr(hf, qed_hf)") + return AdcBlock(apply, diagonal) + + +def block_ph_ph_1_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): + def apply(ampl): + return AmplitudeVector(ph=( + sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) + )) + + #add_axis1 = - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov) + + #np.insert(mvprod, 0, - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov), axis=1) #ground to excited state coupling + #also insert axis0 zeros, so that final matvecproduct has dimesions (i+1) x (a+1) + #else: + # raise NotImplementedError("and not hasattr(hf, qed_hf)") + return AdcBlock(apply, diagonal) + + +def block_ph_ph_1_phot_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): + def apply(ampl): + return AmplitudeVector(ph=( + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) + - mp.qed_t1_df(b.ov) * ampl.gs1.as_float()) # gs_ph block + )) + + #add_axis0 = - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov) + + #np.insert(mvprod, 0, - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov), axis=0) #ground to excited state coupling + #also insert axis1 zeros, so that final matvecproduct has dimesions (i+1) x (a+1) + #else: + # raise NotImplementedError("and not hasattr(hf, qed_hf)") + return AdcBlock(apply, diagonal) + + + def diagonal_pphh_pphh_1(hf): # Fock matrix and ovov diagonal term (sometimes called "intermediate diagonal") dinterm_ov = (direct_sum("a-i->ia", hf.fvv.diagonal(), hf.foo.diagonal()) @@ -235,6 +670,14 @@ def apply(ampl): )) return AdcBlock(apply, 0) +def block_ph_pphh_1_phot(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("jkib,jkab->ia", hf.ooov, ampl.pphh1) + + einsum("ijbc,jabc->ia", ampl.pphh1, hf.ovvv) + )) + return AdcBlock(apply, 0) + def block_cvs_ph_pphh_1(hf, mp, intermediates): def apply(ampl): @@ -253,6 +696,14 @@ def apply(ampl): )) return AdcBlock(apply, 0) +def block_pphh_ph_1_phot(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(pphh=( + + einsum("ic,jcab->ijab", ampl.ph1, hf.ovvv).antisymmetrise(0, 1) + - einsum("ijka,kb->ijab", hf.ooov, ampl.ph1).antisymmetrise(2, 3) + )) + return AdcBlock(apply, 0) + def block_cvs_pphh_ph_1(hf, mp, intermediates): def apply(ampl): @@ -264,31 +715,289 @@ def apply(ampl): return AdcBlock(apply, 0) +def block_ph_pphh_1_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(ph=( + -4 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) + #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) + #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) + #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) + )) + return AdcBlock(apply, 0) + + +def block_pphh_ph_1_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(pphh=( + 4 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1(b.ov), einsum("ia,ia->ia", mp.df(b.ov), ampl.ph)).antisymmetrise(0,1).antisymmetrise(2,3) + #+ einsum("ia,jb,jb->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph) + #- einsum("ja,ib,ib->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph) + #- einsum("ib,ja,ja->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph)) + )) + return AdcBlock(apply, 0) + + +def block_ph_pphh_1_phot_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(ph=( + 4 * sqrt(omega/2) * einsum("kc,ia,ikac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) + #+ einsum("jb,ia,jiba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) + #- einsum("kb,ia,ikba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) + #- einsum("jc,ia,jiac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1)) + )) + return AdcBlock(apply, 0) + + +def block_pphh_ph_1_phot_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(pphh=( + -4 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) + #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) + #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) + #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) + )) + return AdcBlock(apply, 0) + + + +# +# 2nd order gs blocks (gs_ph blocks in ph_ph) for now these are zero for testing purposes +# + +def block_ph_gs_2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + def apply(ampl): + return (einsum("jb,jb->", ( + - 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + + 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) + - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + ampl.ph)) + return AdcBlock(apply, 0) + +def block_ph_gs_2_phot_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = - sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + def apply(ampl): + return ( sqrt(omega / 2) * einsum("jb,jb->", ( + - einsum("jckb,kc->jb", hf.ovov, mp.qed_t1(b.ov)) + + omega * mp.qed_t1(b.ov) + - 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) + + 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + - 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), + ampl.ph1)) + return AdcBlock(apply, diagonal) + +def block_ph_gs_2_couple(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = - sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + def apply(ampl): + return ( sqrt(omega / 2) * einsum("jb,jb->", ( + - 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), + ampl.ph)) + #maybe this whole block is just + einsum("kjbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) + return AdcBlock(apply, diagonal) + +def block_ph_gs_2_phot(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = - omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + def apply(ampl): + return (einsum("jb,jb->", ( + + 0.5 * omega * mp.qed_t0(b.ov) + - 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + + 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) + + sqrt(2) * ( + - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + ) + - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + ampl.ph1)) + #(omega * einsum("jb,jb->", einsum("jj,bb->jb", d_oo, d_vv), ampl.ph1) + #- (omega / 2) * (sqrt(2) - 1) * einsum("jb,jb->", (einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + # - einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + # - einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) + # + einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo))), + # ampl.ph1)) + return AdcBlock(apply, diagonal) + + + # # 2nd order main # def block_ph_ph_2(hf, mp, intermediates): + #omega = float(ReferenceState.get_qed_omega(hf)) i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 - diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) - - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - )) + #intermediates expect a dimensionality unequal zero, so qed_i0 cannot be cashed this way, but its just a sum over two indices + #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] + #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) + #qed_i1 = intermediates.adc2_qed_i1 + #qed_i2 = intermediates.adc2_qed_i2 + + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) - # Not used anywhere else, so kept as an anonymous intermediate term_t2_eri = ( + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) ).evaluate() - def apply(ampl): - return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph, i1) - - einsum("ij,ja->ia", i2, ampl.ph) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 - - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 + if hasattr(hf, "coupling"): + omega = float(ReferenceState.get_qed_omega(hf)) + #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] + #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) + qed_i1 = intermediates.adc2_qed_ph_ph_2_i1 + qed_i2 = intermediates.adc2_qed_ph_ph_2_i2 + #qed_i1 = intermediates.adc2_qed_i1 + #qed_i2 = intermediates.adc2_qed_i2 + #qed_i1_0 = intermediates.adc2_qed_i1_0 + #qed_i2_0 = intermediates.adc2_qed_i2_0 + qed_gs_part = intermediates.adc2_qed_ph_ph_2_gs_part + if hasattr(hf, "qed_hf"): + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + + (-omega/2) * (#qed_i0 #this term and the following are additional qed terms + - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + #+ (1/2) * einsum("iaia->ia", einsum("ia,jb->iajb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + # + einsum("jb,ia->iajb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)))) + + (1/2) * 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + )) + print(mp.energy_correction(2)) + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph, i1) + - einsum("ij,ja->ia", i2, ampl.ph) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 + + (-omega/2) * (#qed_i0 * ampl.ph #this term and the following are additional qed terms + - einsum("ib,ab->ia", ampl.ph, qed_i1) + - einsum("ij,ja->ia", qed_i2, ampl.ph) + #+ (1/2) * einsum("iajb,jb->ia", einsum("ia,jb->iajb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + # + einsum("jb,ia->iajb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)), ampl.ph)) + + (1/2) * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph) + + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph))) + )) + else: + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + + direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + (-omega/2) * ( + #- direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + (1/2) * 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + #+ direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) + - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) + - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) + #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) + #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) + )) + print(mp.energy_correction(2)) + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph, i1) + - einsum("ij,ja->ia", i2, ampl.ph) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 + + einsum("ib,ab->ia", ampl.ph, qed_i1) + + einsum("ij,ja->ia", qed_i2, ampl.ph) + + (-omega/2) * ( + #- einsum("ib,ab->ia", ampl.ph, qed_i1) + #- einsum("ij,ja->ia", qed_i2, ampl.ph) + + (1/2) * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph) + + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph))) + #+ einsum("ib,ab->ia", ampl.ph, qed_i1_0) + #+ einsum("ij,ja->ia", qed_i2_0, ampl.ph) + #+ (1/2) * (einsum("ka,jkib,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph) #this can be done by symmetrize a,b + # + einsum("kb,jkia,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph)) + #+ (1/2) * (einsum("ic,jabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph) #this can be done by symmetrize i,j + # + einsum("jc,iabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph)) + + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) + + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph) + + qed_gs_part * ampl.gs.as_float() + #+ (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution + # - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + # - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + # #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + # + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + # #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + # - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) * ampl.gs.as_float() + #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph # reintroduced (actually canceled from -E_0 (2)) + #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) * ampl.ph + #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) * ampl.ph + )) + else: + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) )) + + # Not used anywhere else, so kept as an anonymous intermediate + #term_t2_eri = ( + # + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + # + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) + #).evaluate() + + def apply(ampl): + #print("using non-qed matrix vector products") + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph, i1) + - einsum("ij,ja->ia", i2, ampl.ph) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 + #+ (-omega/2) * (qed_i0 * ampl.ph #this term and the following are additional qed terms + #- einsum("ib,ab->ia", ampl.ph, qed_i1) + #- einsum("ij,ja->ia", qed_i2, ampl.ph) + #+ einsum("ijab,jb->ia", einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)), ampl.ph)) + )) return AdcBlock(apply, diagonal) @@ -308,6 +1017,204 @@ def apply(ampl): return AdcBlock(apply, diagonal) +def block_ph_ph_2_couple(hf, mp, intermediates): #one could cash some of the terms here + if hasattr(hf, "qed_hf"): + raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + omega = float(ReferenceState.get_qed_omega(hf)) + gs_part = intermediates.adc2_qed_ph_ph_2_couple_gs_part + qed_i1 = intermediates.adc2_qed_couple_i1 + qed_i2 = intermediates.adc2_qed_couple_i2 + + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) + + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + def apply(ampl): + return AmplitudeVector(ph=(0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), + direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph) + - einsum("kb,ka,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph) + einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph) # this is different from mp.diff_df, but why??? + - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) + einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph)) # this is different from mp.diff_df, but why??? + + einsum("ib,ab->ia", ampl.ph, qed_i1) + + einsum("ij,ja->ia", qed_i2, ampl.ph) + - 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph + #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed + #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed + + sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) + + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) + + gs_part * ampl.gs.as_float() + #+ sqrt(omega / 2) * ( # gs_ph contribution + # - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) + # + omega * mp.qed_t1(b.ov) + # - 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) + # + 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + # #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + # + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + # #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + # + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + # * ampl.gs.as_float() + )) + return AdcBlock(apply, diagonal) + +#def block_ph_ph_2_couple(hf, mp, intermediates): #for testing +# if hasattr(hf, "qed_hf"): +# raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +# #omega = float(ReferenceState.get_qed_omega(hf)) +# diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) +# return AdcBlock(lambda ampl: 0, diagonal) + + +def block_ph_ph_2_phot_couple(hf, mp, intermediates): #one could cash some of the terms here + if hasattr(hf, "qed_hf"): + raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + omega = float(ReferenceState.get_qed_omega(hf)) + gs_part = intermediates.adc2_qed_ph_ph_2_phot_couple_gs_part + qed_i1 = intermediates.adc2_qed_phot_couple_i1 + qed_i2 = intermediates.adc2_qed_phot_couple_i2 + + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) + + + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + def apply(ampl): + return AmplitudeVector(ph=(0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), + direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph1) + - einsum("ka,kb,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph1) + einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? + - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) + einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? + + einsum("ib,ab->ia", ampl.ph1, qed_i1) + + einsum("ij,ja->ia", qed_i2, ampl.ph1) + - 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 + #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) + #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) + + sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part + + gs_part * ampl.gs1.as_float() + #+ sqrt(omega / 2) * ( #gs_ph part + # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) + # + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + # + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) + # - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + # + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + # * ampl.gs1.as_float() + )) + return AdcBlock(apply, diagonal) + +#def block_ph_ph_2_phot_couple(hf, mp, intermediates): #for testing +# if hasattr(hf, "qed_hf"): +# raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +# #omega = float(ReferenceState.get_qed_omega(hf)) +# diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) +# return AdcBlock(lambda ampl: 0, diagonal) + + + + +def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms here + if hasattr(hf, "qed_hf"): + raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + omega = float(ReferenceState.get_qed_omega(hf)) + i1 = intermediates.adc2_i1 + i2 = intermediates.adc2_i2 + #intermediates expect a dimensionality unequal zero, so qed_i0 cannot be cashed this way, but its just a sum over two indices + #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] + #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) + #qed_i1 = intermediates.adc2_qed_i1 + #qed_i2 = intermediates.adc2_qed_i2 + + term_t2_eri = ( + + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) + ).evaluate() + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + #omega = float(ReferenceState.get_qed_omega(hf)) + #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] + #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) + qed_i1 = intermediates.adc2_qed_i1 + qed_i2 = intermediates.adc2_qed_i2 + qed_i1_0 = intermediates.adc2_qed_i1_0 + qed_i2_0 = intermediates.adc2_qed_i2_0 + gs_part = intermediates.adc2_qed_ph_ph_2_phot_gs_part + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + + (-omega/2) * (sqrt(2) - 0.5) * ( # instead of 1 should be sqrt(2) + - 2 * direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + + direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) + - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) + - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + + (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) + #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) + #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) + #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) + )) + + #print("with d_vv", d_vv * mp.qed_t1_df(b.vv)) + #print("without d_vv", mp.qed_t1_df(b.vv)) + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph1, i1) + - einsum("ij,ja->ia", i2, ampl.ph1) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 + + (-omega/2) * (sqrt(2) - 0.5) * ( # instead of 1 should be sqrt(2) + - 2 * einsum("ib,ab->ia", ampl.ph1, qed_i1) + - 2 * einsum("ij,ja->ia", qed_i2, ampl.ph1) + + (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) + + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph1))) + + einsum("ib,ab->ia", ampl.ph1, qed_i1_0) + + einsum("ij,ja->ia", qed_i2_0, ampl.ph1) + #+ (1/2) * (einsum("ka,jkib,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph1) #this can be done by symmetrize a,b + # + einsum("kb,jkia,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph1)) + #+ (1/2) * (einsum("ic,jabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph1) #this can be done by symmetrize i,j + # + einsum("jc,iabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph1)) + + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) + + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph1) + + (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 + #+ omega * einsum("ii,aa->ia", d_oo, d_vv) * ampl.gs1.as_float() #this (and following) is from the gs_ph contribution + #- (omega / 2) * (sqrt(2) - 1) * (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + # - einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + # - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) + # + einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo))) + # * ampl.gs1.as_float() + + gs_part * ampl.gs1.as_float() + #+ (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution + # - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + # + sqrt(2) * ( + # - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + # + (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + # + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) + # - (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + # ) + # - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) + # + 0.5 * omega * mp.qed_t0(b.ov)) * ampl.gs1.as_float() + #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph # reintroduced (actually canceled from -E_0 (2)) + #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) * ampl.ph + #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) * ampl.ph + )) + return AdcBlock(apply, diagonal) + + # # 2nd order coupling # @@ -402,6 +1309,156 @@ def adc2_i2(hf, mp, intermediates): # This definition differs from libadc. It additionally has the hf.foo term. return hf.foo - 0.5 * einsum("ikab,jkab->ij", mp.t2oo, hf.oovv).symmetrise() +# qed intermediates for adc2, without the factor of (omega/2), which is added in the actual matrix builder +@register_as_intermediate +def adc2_qed_i1(hf, mp, intermediates): + #return (1/2) * einsum("kb,ka->ab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) + return (1/2) * (einsum("kb,ka->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + + einsum("ka,kb->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + + +@register_as_intermediate +def adc2_qed_i2(hf, mp, intermediates): + #return (1/2) * einsum("jc,ic->ij", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) + return (1/2) * (einsum("jc,ic->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + + einsum("ic,jc->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + +#qed intermediates for adc2, for non-qed-hf input +@register_as_intermediate +def adc2_qed_i1_0(hf, mp, intermediates): + #return (1/2) * einsum("kb,ka->ab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) + return ((1/8) * (einsum("kb,ka->ab", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) + + einsum("ka,kb->ab", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov))) + + einsum("kc,kacb->ab", mp.qed_t0(b.ov), hf.ovvv)) + + +@register_as_intermediate +def adc2_qed_i2_0(hf, mp, intermediates): + #return (1/2) * einsum("jc,ic->ij", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) + return ((1/8) * (einsum("jc,ic->ij", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) + + einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov))) + + einsum("kc,kjic->ij", mp.qed_t0(b.ov), hf.ooov)) + +@register_as_intermediate +def adc2_qed_ph_ph_2_i1(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + return (omega / 2) * intermediates.adc2_qed_i1.evaluate() + intermediates.adc2_qed_i1_0.evaluate() + +@register_as_intermediate +def adc2_qed_ph_ph_2_i2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + return (omega / 2) * intermediates.adc2_qed_i2.evaluate() + intermediates.adc2_qed_i2_0.evaluate() + + +@register_as_intermediate +def adc2_qed_ph_ph_2_gs_part(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + return (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution + - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) + + +@register_as_intermediate +def adc2_qed_ph_ph_2_couple_gs_part(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + return sqrt(omega / 2) * ( # gs_ph contribution + - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) + + omega * mp.qed_t1(b.ov) + - 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) + + 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + + +@register_as_intermediate +def adc2_qed_ph_ph_2_phot_couple_gs_part(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + return sqrt(omega / 2) * ( #gs_ph part + - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) + + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) + - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + + +@register_as_intermediate +def adc2_qed_ph_ph_2_phot_gs_part(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + return (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution + - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + + sqrt(2) * ( + - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + + (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) + - (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + ) + - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) + + 0.5 * omega * mp.qed_t0(b.ov)) + + +@register_as_intermediate +def adc2_qed_couple_i1(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + return ( sqrt(omega / 2) * (0.5 * einsum("ka,kb->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + + einsum("kc,kacb->ab", mp.qed_t1(b.ov), hf.ovvv))) + + +@register_as_intermediate +def adc2_qed_couple_i2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + return ( sqrt(omega / 2) * (0.5 * einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + + einsum("kc,kjic->ij", mp.qed_t1(b.ov), hf.ooov))) + + + + +@register_as_intermediate +def adc2_qed_phot_couple_i1(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + return ( sqrt(omega / 2) * (0.5 * einsum("kb,ka->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + + einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) + + +@register_as_intermediate +def adc2_qed_phot_couple_i2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + return ( sqrt(omega / 2) * (0.5 * einsum("jc,ic->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + + einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) + + + + def adc3_i1(hf, mp, intermediates): # Used for both CVS and general diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index 111a1f89..5f1e3b40 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -27,7 +27,7 @@ from adcc.AdcMethod import AdcMethod from adcc.functions import einsum from adcc.Intermediates import Intermediates -from adcc.AmplitudeVector import AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector from adcc.OneParticleOperator import OneParticleOperator from .util import check_doubles_amplitudes, check_singles_amplitudes @@ -133,8 +133,8 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): method = AdcMethod(method) if not isinstance(ground_state, LazyMp): raise TypeError("ground_state should be a LazyMp object.") - if not isinstance(amplitude, AmplitudeVector): - raise TypeError("amplitude should be an AmplitudeVector object.") + if not isinstance(amplitude, (AmplitudeVector, QED_AmplitudeVector)): + raise TypeError("amplitude should be an AmplitudeVector or QED_AmplitudeVector object.") if intermediates is None: intermediates = Intermediates(ground_state) @@ -142,5 +142,17 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): raise NotImplementedError("transition_dm is not implemented " f"for {method.name}.") else: - ret = DISPATCH[method.name](ground_state, amplitude, intermediates) + if isinstance(amplitude, QED_AmplitudeVector): + # for QED_result we are interested in .elec part only, which grants the electronic transitions. + # We also dont need .gs since it should be zero...this has to be checked!!! + ampl_elec_norm = amplitude.elec @ amplitude.elec + print("care, that .gs is not used for transition_dm, but only .elec (check transition_dm.py)") + print("norm squared of amplitude.elec", ampl_elec_norm) + print("norm squared of amplitude", amplitude @ amplitude) + print("beware, that we normalize .elec, before giving it to the oscillator strength routine") + print("amplitude.gs is {} and amplitude.gs1 is {}".format(amplitude.gs.as_float(), amplitude.gs1.as_float())) + normalized_ampl = amplitude.elec / sqrt(ampl_elec_norm) + ret = DISPATCH[method.name](ground_state, normalized_ampl, intermediates) + else: + ret = DISPATCH[method.name](ground_state, amplitude, intermediates) return ret.evaluate() diff --git a/adcc/backends/__init__.py b/adcc/backends/__init__.py index 025e8ae8..b6192e4d 100644 --- a/adcc/backends/__init__.py +++ b/adcc/backends/__init__.py @@ -113,7 +113,7 @@ def import_scf_results(res): import psi4 from . import psi4 as backend_psi4 - if isinstance(res, psi4.core.HF): + if isinstance(res, (psi4.core.HF, psi4.core.Wavefunction)): return backend_psi4.import_scf(res) from libadcc import HartreeFockSolution_i diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index c9714b93..24237e9f 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -30,6 +30,12 @@ from .EriBuilder import EriBuilder from ..exceptions import InvalidReference +#global qed_from_qed_hf_input +#qed_from_qed_hf_input = False + +#if isinstance(wfn, psi4.core.Wavefunction): +# #We need this global variable later, to either perform QED-ADC from qed-hf input or standard non-qed-hf input +# qed_from_qed_hf_input = True class Psi4OperatorIntegralProvider: def __init__(self, wfn): @@ -150,17 +156,26 @@ def fill_orbcoeff_fb(self, out): np.hstack((mo_coeff[0], mo_coeff[1])) ) - def fill_occupation_f(self, out): - out[:] = np.hstack(( - np.asarray(self.wfn.occupation_a()), - np.asarray(self.wfn.occupation_b()) - )) - def fill_orben_f(self, out): orben_a = np.asarray(self.wfn.epsilon_a()) orben_b = np.asarray(self.wfn.epsilon_b()) out[:] = np.hstack((orben_a, orben_b)) + def fill_occupation_f(self, out): + if isinstance(self.wfn, psi4.core.Wavefunction): + num_of_orbs = self.wfn.nmo() + nalpha_elec = self.wfn.nalpha() + nbeta_elec = self.wfn.nbeta() + occ_array_a = occ_array_b = np.zeros(num_of_orbs) + np.put(occ_array_a, np.arange(nalpha_elec), 1) + np.put(occ_array_b, np.arange(nbeta_elec), 1) + out[:] = np.hstack((occ_array_a, occ_array_b)) + else: + out[:] = np.hstack(( + np.asarray(self.wfn.occupation_a()), + np.asarray(self.wfn.occupation_b()) + )) + def fill_fock_ff(self, slices, out): diagonal = np.empty(self.n_orbs) self.fill_orben_f(diagonal) @@ -180,7 +195,7 @@ def flush_cache(self): def import_scf(wfn): - if not isinstance(wfn, psi4.core.HF): + if not isinstance(wfn, (psi4.core.HF, psi4.core.Wavefunction)): raise InvalidReference( "Only psi4.core.HF and its subtypes are supported references in " "backends.psi4.import_scf. This indicates that you passed an " @@ -188,16 +203,17 @@ def import_scf(wfn): "unrestricted HF calculation." ) - if not isinstance(wfn, (psi4.core.RHF, psi4.core.UHF)): + if not isinstance(wfn, (psi4.core.RHF, psi4.core.UHF, psi4.core.Wavefunction)): raise InvalidReference("Right now only RHF and UHF references are " "supported for Psi4.") + # TODO This is not fully correct, because the core.Wavefunction object # has an internal, but py-invisible Options structure, which contains # the actual set of options ... theoretically they could differ scf_type = psi4.core.get_global_option('SCF_TYPE') # CD = Choleski, DF = density-fitting - unsupported_scf_types = ["CD", "DISK_DF", "MEM_DF"] + unsupported_scf_types = ["CD"]#, "DISK_DF", "MEM_DF"] if scf_type in unsupported_scf_types: raise InvalidReference("Unsupported Psi4 SCF_TYPE, should not be one " f"of {unsupported_scf_types}") diff --git a/adcc/functions.py b/adcc/functions.py index ff036efa..4a030fff 100644 --- a/adcc/functions.py +++ b/adcc/functions.py @@ -24,7 +24,7 @@ import opt_einsum -from .AmplitudeVector import AmplitudeVector +from .AmplitudeVector import AmplitudeVector, QED_AmplitudeVector def dot(a, b): @@ -121,7 +121,32 @@ def lincomb(coefficients, tensors, evaluate=False): block: lincomb(coefficients, [ten[block] for ten in tensors], evaluate=evaluate) for block in tensors[0].blocks_ph - }) + }) # rewrite this for QED_AmplitudeVector, where ph,pphh,ph1,pphh1 still undergo libadcc.Tensor stuff, while gs and gs1 have to be treated separately + elif isinstance(tensors[0], QED_AmplitudeVector): + #for qed_vec in tensors: + #print(tensors.gs, tensors.elec, tensors.gs1, tensors.phot) + # give .elec and .phot to lin_comb_strict via AmplitudeVector instance + # then coeff[0] * .gs[from first tensor/vector] + coeff[1] * .gs[from second tensor/vector] + ... + # same for .gs1 + gs_part = 0 + gs1_part = 0 + elec_list = [] + phot_list = [] + for coeff_ind, ten in enumerate(tensors): + gs_part += coefficients[coeff_ind] * ten.gs + gs1_part += coefficients[coeff_ind] * ten.gs1 + elec_list.append(ten.elec) + phot_list.append(ten.phot) + elec_part = lincomb(coefficients, elec_list, evaluate=evaluate) + phot_part = lincomb(coefficients, phot_list, evaluate=evaluate) + if "pphh" in elec_part.blocks_ph: + return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh) + else: + return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None) + #print(qed_vec.ph) + #print(coefficients) + #print(tensors[0][block]) + #NotImplementedError("lincomb does not exist for list of QED_AmplitudeVectors yet") elif not isinstance(tensors[0], libadcc.Tensor): raise TypeError("Tensor type not supported") diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index 8a53a137..4191df21 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -84,6 +84,10 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, guessfunction = guesses_from_diagonal_singles elif block == "pphh": guessfunction = guesses_from_diagonal_doubles + elif block == "gs": + guessfunction = guesses_from_diagonal_gs + #elif block == "pphh_phot": + # guessfunction = guesses_from_diagonal_doubles_phot else: raise ValueError(f"Don't know how to generate guesses for block {block}") @@ -263,6 +267,13 @@ def telem_nospin(telem): return [evaluate(v / np.sqrt(v @ v)) for v in ret[:ivec]] +def guesses_from_diagonal_gs(matrix, n_guesses, spin_change=0, + spin_block_symmetrisation="none", + degeneracy_tolerance=1e-14): + print("care...guesses from diagonal still grant 0 for groundstate") + return 0 + + def guesses_from_diagonal_doubles(matrix, n_guesses, spin_change=0, spin_block_symmetrisation="none", degeneracy_tolerance=1e-14): diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 4819de69..5439377e 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -28,7 +28,7 @@ from adcc import evaluate, lincomb from adcc.AdcMatrix import AdcMatrixlike -from adcc.AmplitudeVector import AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector from .common import select_eigenpairs from .preconditioner import JacobiPreconditioner @@ -139,6 +139,7 @@ def callback(state, identifier): # The current subspace SS = state.subspace_vectors + #print("from davidson...SS[0].pphh", SS[0].pphh) # The matrix A projected into the subspace # as a continuous array. Only the view @@ -152,6 +153,9 @@ def callback(state, identifier): callback(state, "start") state.timer.restart("iteration") + #print(matrix.shape) + #print(SS) + with state.timer.record("projection"): # Initial application of A to the subspace Ax = evaluate(matrix @ SS) @@ -169,6 +173,8 @@ def callback(state, identifier): with state.timer.record("projection"): Ass = Ass_cont[:n_ss_vec, :n_ss_vec] # Increase the work view size for i in range(n_block): + #print(type(Ax[-n_block + i]), Ax[-n_block + i]) + #print(type(SS), SS) Ass[:, -n_block + i] = Ax[-n_block + i] @ SS Ass[-n_block:, :] = np.transpose(Ass[:, -n_block:]) @@ -187,6 +193,9 @@ def callback(state, identifier): # Form residuals, A * SS * v - λ * SS * v = Ax * v + SS * (-λ*v) def form_residual(rval, rvec): coefficients = np.hstack((rvec, -rval * rvec)) + #print(type(coefficients), coefficients) + #print(type(SS), SS) + #print(type(Ax), Ax) return lincomb(coefficients, Ax + SS, evaluate=True) residuals = [form_residual(rvals[i], v) for i, v in enumerate(np.transpose(rvecs))] @@ -197,6 +206,12 @@ def form_residual(rval, rvec): state.eigenvalues = rvals[epair_mask] state.residuals = [residuals[i] for i in epair_mask] state.residual_norms = np.array([r @ r for r in state.residuals]) + # building the eigenvectors here is just for debugging purposes + eigenvecs = [lincomb(v, SS, evaluate=True) + for i, v in enumerate(np.transpose(rvecs)) + if i in epair_mask] + for eigv in eigenvecs: + print("norm of eigenvector", np.sqrt(eigv @ eigv)) # TODO This is misleading ... actually residual_norms contains # the norms squared. That's also the used e.g. in adcman to # check for convergence, so using the norm squared is fine, @@ -264,6 +279,9 @@ def form_residual(rval, rvec): # Explicitly symmetrise the new vectors if requested if explicit_symmetrisation: + #if isinstance(preconds[0], QED_AmplitudeVector): + # + #else: explicit_symmetrisation.symmetrise(preconds) # Project the components of the preconditioned vectors away @@ -275,6 +293,8 @@ def form_residual(rval, rvec): pvec = preconds[i] # Project out the components of the current subspace # That is form (1 - SS * SS^T) * pvec = pvec + SS * (-SS^T * pvec) + #print("pvec SS", pvec, SS) + #print(pvec.elec.ph, SS[0].elec.ph) coefficients = np.hstack(([1], -(pvec @ SS))) pvec = lincomb(coefficients, [pvec] + SS, evaluate=True) pnorm = np.sqrt(pvec @ pvec) @@ -358,7 +378,9 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, if not isinstance(matrix, AdcMatrixlike): raise TypeError("matrix is not of type AdcMatrixlike") for guess in guesses: - if not isinstance(guess, AmplitudeVector): + if not isinstance(guess, (AmplitudeVector, QED_AmplitudeVector)): + #print(guess) + #print(guesses) raise TypeError("One of the guesses is not of type AmplitudeVector") if preconditioner is not None and isinstance(preconditioner, type): @@ -390,6 +412,7 @@ def convergence_test(state): "".format(conv_tol, matrix.shape[1] * np.finfo(float).eps) )) + #print("this is from davidson...guesses[0].pphh = ", guesses[0].pphh) state = DavidsonState(matrix, guesses) davidson_iterations(matrix, state, max_subspace, max_iter, n_ep=n_ep, is_converged=convergence_test, diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index c11325da..1527e319 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -23,7 +23,7 @@ from libadcc import amplitude_vector_enforce_spin_kind from adcc import evaluate -from adcc.AmplitudeVector import AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector # TODO # This interface is not that great and leads to duplicate information @@ -55,16 +55,34 @@ def symmetrise(self, new_vectors): Returns: The updated new_vectors """ - if isinstance(new_vectors, AmplitudeVector): - return self.symmetrise([new_vectors])[0] - for vec in new_vectors: + + def symm_subroutine(vec): if not isinstance(vec, AmplitudeVector): - raise TypeError("new_vectors has to be an " - "iterable of AmplitudeVector") + raise TypeError("new_vectors has to be an " + "iterable of AmplitudeVector") for b in vec.blocks_ph: if b not in self.symmetrisation_functions: continue vec[b] = evaluate(self.symmetrisation_functions[b](vec[b])) + return vec + + if isinstance(new_vectors, AmplitudeVector): + return self.symmetrise([new_vectors])[0] + elif isinstance(new_vectors[0], QED_AmplitudeVector): + # we dont have to symmetrise the gs blocks...actually only the pphh blocks are symmetrised here + for vec in new_vectors: + vec.elec = symm_subroutine(vec.elec) + vec.phot = symm_subroutine(vec.phot) + elif isinstance(new_vectors[0], AmplitudeVector): + for vec in new_vectors: + vec = symm_subroutine(vec) + #if not isinstance(vec, AmplitudeVector): + # raise TypeError("new_vectors has to be an " + # "iterable of AmplitudeVector") + #for b in vec.blocks_ph: + # if b not in self.symmetrisation_functions: + # continue + # vec[b] = evaluate(self.symmetrisation_functions[b](vec[b])) return new_vectors diff --git a/adcc/solver/preconditioner.py b/adcc/solver/preconditioner.py index 07cc1c0c..05b564e5 100644 --- a/adcc/solver/preconditioner.py +++ b/adcc/solver/preconditioner.py @@ -23,7 +23,7 @@ import numpy as np from adcc.AdcMatrix import AdcMatrixlike -from adcc.AmplitudeVector import AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector class PreconditionerIdentity: @@ -63,19 +63,32 @@ def update_shifts(self, shifts): self.shifts = shifts def apply(self, invecs): + #print(invecs) if isinstance(invecs, AmplitudeVector): if not isinstance(self.shifts, (float, np.number)): raise TypeError("Can only apply JacobiPreconditioner " "to a single vector if shifts is " "only a single number.") return invecs / (self.diagonal - self.shifts) - elif isinstance(invecs, list): + elif isinstance(invecs, QED_AmplitudeVector): # i dont think this is used anywhere + if not isinstance(self.shifts, (float, np.number)): + raise TypeError("Can only apply JacobiPreconditioner " + "to a single vector if shifts is " + "only a single number.") + return invecs / (self.diagonal - self.shifts) + elif isinstance(invecs, list): # either list of AmplitudeVectors or QED_AmplitudeVectors if len(self.shifts) != len(invecs): raise ValueError("Number of vectors passed does not agree " "with number of shifts stored inside " "precoditioner. Update using the " "'update_shifts' method.") - + #print(self.diagonal, self.shifts) + #for i, v in enumerate(invecs): + #test_temp = invecs[0].ph / (self.diagonal.ph - self.shifts[0]) + #print("ph/ph", test_temp) + #print(invecs[0]) + #print(self.diagonal.gs, self.diagonal.ph, self.diagonal.gs1) + #print("from precond invec.pphh is ", invecs[0].pphh) return [v / (self.diagonal - self.shifts[i]) for i, v in enumerate(invecs)] else: diff --git a/adcc/workflow.py b/adcc/workflow.py index 37d78774..921b2dd9 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -22,6 +22,7 @@ ## --------------------------------------------------------------------- import sys import warnings +import numpy as np from libadcc import ReferenceState @@ -38,6 +39,7 @@ from .solver.davidson import jacobi_davidson from .solver.explicit_symmetrisation import (IndexSpinSymmetrisation, IndexSymmetrisation) +from .AmplitudeVector import QED_AmplitudeVector __all__ = ["run_adc"] @@ -378,8 +380,11 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", if guesses is None: if n_guesses is None: n_guesses = estimate_n_guesses(matrix, n_states, n_guesses_per_state) - guesses = obtain_guesses_by_inspection(matrix, n_guesses, kind, + #guesses = obtain_guesses_by_inspection(matrix, n_guesses, kind, # guesses are obtained here + # n_guesses_doubles) + guesses = obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, # guesses are obtained here n_guesses_doubles) + #print(guesses) else: if len(guesses) < n_states: raise InputError("Less guesses provided via guesses (== {}) " @@ -392,6 +397,7 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", warnings.warn("Ignoring n_guesses_doubles parameter, since guesses " "are explicitly provided.") + #print("from workflow...guesses[0].pphh = ", guesses[0].pphh) solverargs.setdefault("which", "SA") return run_eigensolver(matrix, guesses, n_ep=n_states, conv_tol=conv_tol, callback=callback, @@ -452,6 +458,7 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None guess_function = {"any": guesses_any, "singlet": guesses_singlet, "triplet": guesses_triplet, "spin_flip": guesses_spin_flip}[kind] + # Determine number of singles guesses to request n_guess_singles = n_guesses @@ -476,6 +483,48 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None return total_guesses +def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles=None): # sadly we cannot do this, since later functions + # require the matrix (AdcMatrix), instead of just the diagonal, which is a QED_Amplitude + guesses_elec = obtain_guesses_by_inspection(matrix.elec, n_guesses, kind, n_guesses_doubles) + guesses_phot = obtain_guesses_by_inspection(matrix.phot, n_guesses, kind, n_guesses_doubles) + #print("this is from obtain_guess_qed from workflow: guesses_elec[0].pphh", guesses_elec[0].pphh) + if len(guesses_elec) != len(guesses_phot): + raise InputError("amount of guesses for electronic and photonic must be equal, but are" + "{} electronic and {} photonic guesses".format(len(guesses_elec), len(guesses_phot))) + # for now we make gs guess zero + print("groundstate guesses are still zero, see workflow.py func obtain_guesses_by_inspection_qed") + guess_elec0 = np.zeros(len(guesses_elec)) + guess_phot0 = np.zeros(len(guesses_phot)) # using omega (actual diagonal 1st order) results in strange behaviour of davidson solver + guesses_tmp = [] + try: + dummy_var = guesses_elec[0].pphh + #print(dummy_var) + contains_doubles = True + except AttributeError: + contains_doubles = False + for guess_index in np.arange(len(guesses_elec)): # build QED_AmplitudeVectors from AmplitudeVector guesses + #if hasattr(guesses_elec[0], "pphh"): + if contains_doubles: + print("doubles guesses are set up") + guesses_tmp.append(QED_AmplitudeVector(guess_elec0[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, + guess_phot0[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh)) + else: + print("singles guesses are set up") + guesses_tmp.append(QED_AmplitudeVector(guess_elec0[guess_index], guesses_elec[guess_index].ph, None, + guess_phot0[guess_index], guesses_phot[guess_index].ph, None)) + + # guesses_tmp needs to be normalized then + #for vec in guesses_tmp: + # print(type(vec.gs), vec.gs) + # print(type(vec.ph), vec.ph) + # print(type(vec.gs1), vec.gs1) + # print(type(vec.ph1), vec.ph1) + #print("from workflow guess_qed...guesses_tmp[0].pphh = ", guesses_tmp[0].pphh) + #normalized_guesses = [vec / np.sqrt(vec @ vec) for vec in guesses_tmp] + #print("after normalization guesses[0].pphh is ", normalized_guesses[0].pphh) + return [vec / np.sqrt(vec @ vec) for vec in guesses_tmp] + #return guesses + def setup_solver_printing(solmethod_name, matrix, kind, default_print, output=None): """ From b94d13f8b56680b99bfab9cd54ab6cd07ca3785c Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Wed, 7 Jul 2021 15:35:36 +0200 Subject: [PATCH 02/64] qed-adc2 equations without adjustments to vector- and matrix class --- adcc/AmplitudeVector.py | 2 +- adcc/adc_pp/matrix.py | 568 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 564 insertions(+), 6 deletions(-) diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index 85c196da..adf71a0c 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -230,7 +230,7 @@ def __radd__(self, other): class QED_AmplitudeVector: #def __init__(self, gs=None, elec=None, gs1=None, phot=None): - def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None): + def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None):#, gs2=None, ph2=None, pphh2=None): #if (elec is None and gs is None) and (phot is None and gs1 is None): # raise ValueError("Either electronic or photonic part must be given.") diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index dde65599..4c2a57e1 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -113,6 +113,11 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): # (original 4 blocks + ph_gs and pphh_gs) per "ADC submatrix" # maybe return float from ph/pphh_gs blocks, instead of QED_AmplitudeVector.gs/.gs1 +# For QED-ADC(2) we then require also the 2 photon block, so we introduce the naming convention: +# elec phot_couple phot_couple_outer +# elec_couple phot phot_couple_inner +# elec_couple_edge elec_couple_inner phot2 + @@ -200,12 +205,16 @@ def block_ph_gs_0(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) block_ph_gs_0_couple = block_ph_gs_0_phot_couple = block_ph_gs_0_phot = block_ph_gs_0 +block_ph_gs_0_couple_edge = block_ph_gs_0_phot_couple_edge = block_ph_gs_0_phot2 = block_ph_gs_0 +block_ph_gs_0_couple_inner = block_ph_gs_0_phot_couple_inner = block_ph_gs_0 def block_pphh_gs_0(hf, mp, intermediates): return AdcBlock(lambda ampl:0, 0) block_pphh_gs_0_couple = block_pphh_gs_0_phot_couple = block_pphh_gs_0_phot = block_pphh_gs_0 +block_pphh_gs_0_couple_edge = block_pphh_gs_0_phot_couple_edge = block_pphh_gs_0_phot2 = block_pphh_gs_0 +block_pphh_gs_0_couple_inner = block_pphh_gs_0_phot_couple_inner = block_pphh_gs_0 """ def block_gs_ph_0(hf, mp, intermediates): @@ -280,6 +289,7 @@ def block_ph_ph_0_couple(hf, mp, intermediates): # we also give these blocks zer # return AdcBlock(apply, diagonal) block_ph_ph_0_phot_couple = block_ph_ph_0_couple +block_ph_ph_0_phot_couple_edge = block_ph_ph_0_phot_couple_inner = block_ph_ph_0_couple_edge = block_ph_ph_0_couple_inner = block_ph_ph_0_couple #def block_ph_ph_0_phot_couple(hf, mp, intermediates): # diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) @@ -309,6 +319,27 @@ def apply(ampl): raise NotImplementedError("coupling needs to be given to reference wavefunction in input file for QED-ADC") return AdcBlock(apply, diagonal) +def block_ph_ph_0_phot2(hf, mp, intermediates): + fCC = hf.fcc if hf.has_core_occupied_space else hf.foo + if hasattr(hf, "coupling"):# and hasattr(hf, "qed_hf"): + diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), + fCC.diagonal())) + + #np.insert(diagonal, 0, 0.) + print("new stuff works???") + #print(diagonal) + + def apply(ampl): + mvprod = AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph2, hf.fvv) + - einsum("IJ,Ja->Ia", fCC, ampl.ph2) + )) + #np.insert(mvprod, 0, 0.) + return mvprod + else: + raise NotImplementedError("coupling needs to be given to reference wavefunction in input file for QED-ADC") + return AdcBlock(apply, diagonal) + #def block_ph_ph1_0(hf, mp, intermediates): # return AdcBlock(lambda ampl: 0, 0) @@ -342,6 +373,7 @@ def block_pphh_pphh_0_couple(hf, mp, intermediates): block_pphh_pphh_0_phot_couple = block_pphh_pphh_0_couple +block_pphh_pphh_0_phot_couple_edge = block_pphh_pphh_0_phot_couple_inner = block_pphh_pphh_0_couple_edge = block_pphh_pphh_0_couple_inner = block_pphh_pphh_0_couple def block_pphh_pphh_0_phot(hf, mp, intermediates): def apply(ampl): @@ -362,6 +394,15 @@ def apply(ampl): return AdcBlock(apply, diagonal_pphh_pphh_0(hf)) +def block_pphh_pphh_0_phot2(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(pphh=( + + 2 * einsum("ijac,bc->ijab", ampl.pphh2, hf.fvv).antisymmetrise(2, 3) + - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh2).antisymmetrise(0, 1) + )) + return AdcBlock(apply, diagonal_pphh_pphh_0(hf)) + + # # 0th order coupling # @@ -377,7 +418,10 @@ def block_pphh_ph_0(hf, mp, intermediates): block_cvs_pphh_ph_0 = block_pphh_ph_0 block_pphh_ph_0_couple = block_pphh_ph_0_phot_couple = block_pphh_ph_0_phot = block_pphh_ph_0 +block_pphh_ph_0_couple_edge = block_pphh_ph_0_couple_inner = block_pphh_ph_0_phot_couple_edge = block_pphh_ph_0_phot_couple_inner = block_pphh_ph_0_phot2 = block_pphh_ph_0 block_ph_pphh_0_couple = block_ph_pphh_0_phot_couple = block_ph_pphh_0_phot = block_ph_pphh_0 +block_ph_pphh_0_couple_edge = block_ph_pphh_0_couple_inner = block_ph_pphh_0_phot_couple_edge = block_ph_pphh_0_phot_couple_inner = block_ph_pphh_0_phot2 = block_ph_pphh_0 + # @@ -396,6 +440,7 @@ def apply(ampl): #def block_ph_gs_1_phot_couple(hf, mp, intermediates): # return AdcBlock(lambda ampl: 0, 0) block_ph_gs_1_phot_couple = block_ph_gs_1 +block_ph_gs_1_phot_couple_edge = block_ph_gs_1_couple_edge = block_ph_gs_1 def block_ph_gs_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -404,11 +449,32 @@ def apply(ampl): return AdcBlock(apply, 0) #return AdcBlock(lambda ampl: 0, 0) +def block_ph_gs_1_phot2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return 2 * omega * ampl.gs2 + return AdcBlock(apply, omega) + +def block_ph_gs_1_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return (-1) * sqrt(omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1) + return AdcBlock(apply, 0) + +def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) + return AdcBlock(apply, 0) + + def block_pphh_gs_1(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) block_pphh_gs_1_couple = block_pphh_gs_1_phot_couple = block_pphh_gs_1_phot = block_pphh_gs_1 +block_pphh_gs_1_couple_edge = block_pphh_gs_1_couple_inner = block_pphh_gs_1_phot_couple_edge = block_pphh_gs_1_phot_couple_inner = block_pphh_gs_1_phot2 = block_pphh_gs_1 + """ @@ -605,6 +671,74 @@ def apply(ampl): return AdcBlock(apply, diagonal) +def block_ph_ph_1_couple_edge(hf, mp, intermediates): + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + return AdcBlock(lambda ampl: 0, diagonal) + +block_ph_ph_1_phot_couple_edge = block_ph_ph_1_couple_edge + +def block_ph_ph_1_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): + def apply(ampl): + return AmplitudeVector(ph=( + sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) + + (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part + )) + return AdcBlock(apply, diagonal) + +def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): + def apply(ampl): + return AmplitudeVector(ph=( + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) + - mp.qed_t1_df(b.ov) * ampl.gs2.as_float()) # gs_ph block + )) + return AdcBlock(apply, diagonal) + +def block_ph_ph_1_phot2(hf, mp, intermediates): + fCC = hf.fcc if hf.has_core_occupied_space else hf.foo + CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov + if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): + #diag_qed_term = einsum("klkl->", hf.oooo) + omega = float(ReferenceState.get_qed_omega(hf)) + + # Build two Kronecker deltas + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 + #+ (1/2) * einsum("klkl->", hf.oooo) + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) + #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) #reintroduced (actually canceled from -E_0 (1)) + + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 + )) + + def apply(ampl): + return AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 + #+ (1/2) * einsum("klkl->", hf.oooo) * ampl.ph + + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) + - (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) + #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #reintroduced (actually canceled from -E_0 (1) + + 2 * omega * ampl.ph2 + )) + else: + raise NotImplementedError("and not hasattr(hf, qed_hf)") + return AdcBlock(apply, diagonal) + + def diagonal_pphh_pphh_1(hf): # Fock matrix and ovov diagonal term (sometimes called "intermediate diagonal") @@ -678,6 +812,15 @@ def apply(ampl): )) return AdcBlock(apply, 0) +def block_ph_pphh_1_phot2(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("jkib,jkab->ia", hf.ooov, ampl.pphh2) + + einsum("ijbc,jabc->ia", ampl.pphh2, hf.ovvv) + )) + return AdcBlock(apply, 0) + + def block_cvs_ph_pphh_1(hf, mp, intermediates): def apply(ampl): @@ -704,6 +847,14 @@ def apply(ampl): )) return AdcBlock(apply, 0) +def block_pphh_ph_1_phot2(hf, mp, intermediates): + def apply(ampl): + return AmplitudeVector(pphh=( + + einsum("ic,jcab->ijab", ampl.ph2, hf.ovvv).antisymmetrise(0, 1) + - einsum("ijka,kb->ijab", hf.ooov, ampl.ph2).antisymmetrise(2, 3) + )) + return AdcBlock(apply, 0) + def block_cvs_pphh_ph_1(hf, mp, intermediates): def apply(ampl): @@ -727,6 +878,18 @@ def apply(ampl): return AdcBlock(apply, 0) +def block_ph_pphh_1_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(ph=( + -4 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) + #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) + #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) + #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) + )) + return AdcBlock(apply, 0) + + def block_pphh_ph_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): @@ -739,18 +902,46 @@ def apply(ampl): return AdcBlock(apply, 0) +def block_pphh_ph_1_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(pphh=( + 4 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1(b.ov), einsum("ia,ia->ia", mp.df(b.ov), ampl.ph1)).antisymmetrise(0,1).antisymmetrise(2,3) + + (1 - sqrt(2)) * 4 * sqrt(omega / 2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) + #+ einsum("ia,jb,jb->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph) + #- einsum("ja,ib,ib->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph) + #- einsum("ib,ja,ja->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph)) + )) + return AdcBlock(apply, 0) + + + def block_ph_pphh_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( - 4 * sqrt(omega/2) * einsum("kc,ia,ikac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) - #+ einsum("jb,ia,jiba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) - #- einsum("kb,ia,ikba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) - #- einsum("jc,ia,jiac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1)) + sqrt(omega/2) * einsum("kc,ia,ikac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) + + einsum("jb,ia,jiba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) + - einsum("kb,ia,ikba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) + - einsum("jc,ia,jiac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) )) return AdcBlock(apply, 0) +def block_ph_pphh_1_phot_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(ph=( + sqrt(omega/2) * einsum("kc,ia,ikac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh2) + + einsum("jb,ia,jiba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh2) + - einsum("kb,ia,ikba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh2) + - einsum("jc,ia,jiac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh2) + + 4 * (1 - sqrt(2)) * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh2) + )) + return AdcBlock(apply, 0) + + + def block_pphh_ph_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): @@ -763,6 +954,29 @@ def apply(ampl): return AdcBlock(apply, 0) +def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): + return AmplitudeVector(pphh=( + -4 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) + #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) + #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) + #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) + )) + return AdcBlock(apply, 0) + + +def block_pphh_ph_1_couple_edge(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge + +def block_ph_pphh_1_couple_edge(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) + +block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge + + # # 2nd order gs blocks (gs_ph blocks in ph_ph) for now these are zero for testing purposes @@ -811,6 +1025,8 @@ def apply(ampl): ampl.ph1)) return AdcBlock(apply, diagonal) + + def block_ph_gs_2_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -861,6 +1077,95 @@ def apply(ampl): # ampl.ph1)) return AdcBlock(apply, diagonal) +def block_ph_gs_2_phot2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = - omega * (sqrt(3) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + def apply(ampl): + return (einsum("jb,jb->", ( + + omega * mp.qed_t0(b.ov) + - 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + + 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) + + sqrt(3) * ( + - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + ) + - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + ampl.ph2)) + return AdcBlock(apply, diagonal) + + +def block_ph_gs_2_phot_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = - sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + def apply(ampl): + return ( sqrt(omega / 2) * einsum("jb,jb->", ( + - einsum("jckb,kc->jb", hf.ovov, mp.qed_t1(b.ov)) + + 2 * omega * mp.qed_t1(b.ov) + - 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) + + 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + - 0.5 * sqrt(2) * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + 0.5 * sqrt(2) * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + sqrt(2) * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), + ampl.ph2)) + return AdcBlock(apply, diagonal) + + + +def block_ph_gs_2_couple_inner(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = - sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + def apply(ampl): + return ( sqrt(omega) * einsum("jb,jb->", ( + - 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), + ampl.ph1)) + #maybe this whole block is just + einsum("kjbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) + return AdcBlock(apply, diagonal) + + +def block_ph_gs_2_phot_couple_edge(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + + def apply(ampl): + return (- 0.5 * omega * sqrt(2) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + - einsum("kb,jk,jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2))) + + return AdcBlock(apply, diagonal) + +def block_ph_gs_2_couple_edge(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + + return AdcBlock(lambda ampl: 0, diagonal) + # @@ -1177,7 +1482,7 @@ def apply(ampl): - einsum("ij,ja->ia", i2, ampl.ph1) - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 - + (-omega/2) * (sqrt(2) - 0.5) * ( # instead of 1 should be sqrt(2) + + (-omega/2) * (sqrt(2) - 0.5) * ( - 2 * einsum("ib,ab->ia", ampl.ph1, qed_i1) - 2 * einsum("ij,ja->ia", qed_i2, ampl.ph1) + (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) @@ -1215,6 +1520,198 @@ def apply(ampl): return AdcBlock(apply, diagonal) +def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the terms here + if hasattr(hf, "qed_hf"): + raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + omega = float(ReferenceState.get_qed_omega(hf)) + i1 = intermediates.adc2_i1 + i2 = intermediates.adc2_i2 + + term_t2_eri = ( + + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) + ).evaluate() + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + qed_i1 = intermediates.adc2_qed_i1 + qed_i2 = intermediates.adc2_qed_i2 + qed_i1_0 = intermediates.adc2_qed_i1_0 + qed_i2_0 = intermediates.adc2_qed_i2_0 + gs_part = intermediates.adc2_qed_ph_ph_2_phot2_gs_part + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + + (-omega/2) * (sqrt(3) - 0.5) * ( + - 2 * direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + + direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) + - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) + - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + + (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) + )) + + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph2, i1) + - einsum("ij,ja->ia", i2, ampl.ph2) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 + + (-omega/2) * (sqrt(3) - 0.5) * ( + - 2 * einsum("ib,ab->ia", ampl.ph2, qed_i1) + - 2 * einsum("ij,ja->ia", qed_i2, ampl.ph2) + + (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph2) + + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph2))) + + einsum("ib,ab->ia", ampl.ph2, qed_i1_0) + + einsum("ij,ja->ia", qed_i2_0, ampl.ph2) + + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) + + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph2) + + (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 + + gs_part * ampl.gs2.as_float() + )) + return AdcBlock(apply, diagonal) + + +def block_ph_ph_2_couple_inner(hf, mp, intermediates): #one could cash some of the terms here + if hasattr(hf, "qed_hf"): + raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + omega = float(ReferenceState.get_qed_omega(hf)) + gs_part = intermediates.adc2_qed_ph_ph_2_couple_inner_gs_part + qed_i1 = intermediates.adc2_qed_couple_i1 + qed_i2 = intermediates.adc2_qed_couple_i2 + + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) + + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + def apply(ampl): + return AmplitudeVector(ph=(0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), + direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph1) + - einsum("kb,ka,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph) + einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? + - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) + einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? + + sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) + - 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 + #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed + #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed + + sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + + gs_part * ampl.gs1.as_float() + + (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) + - (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( + (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph1 + - einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) + - einsum("kb,ka,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) + - einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) + - einsum("jc,ic,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) + + einsum("jb,ia,jb->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) + + einsum("jb,ia,jb->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) + ) + )) + return AdcBlock(apply, diagonal) + +#def block_ph_ph_2_couple(hf, mp, intermediates): #for testing +# if hasattr(hf, "qed_hf"): +# raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +# #omega = float(ReferenceState.get_qed_omega(hf)) +# diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) +# return AdcBlock(lambda ampl: 0, diagonal) + + +def block_ph_ph_2_phot_couple_inner(hf, mp, intermediates): #one could cash some of the terms here + if hasattr(hf, "qed_hf"): + raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + omega = float(ReferenceState.get_qed_omega(hf)) + gs_part = intermediates.adc2_qed_ph_ph_2_phot_couple_inner_gs_part + qed_i1 = intermediates.adc2_qed_phot_couple_i1 + qed_i2 = intermediates.adc2_qed_phot_couple_i2 + + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) + + + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + def apply(ampl): + return AmplitudeVector(ph=(0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), + direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph2) + - einsum("ka,kb,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph1) + einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2) # this is different from mp.diff_df, but why??? + - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) + einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2)) # this is different from mp.diff_df, but why??? + + einsum("ib,ab->ia", ampl.ph2, qed_i1) + + einsum("ij,ja->ia", qed_i2, ampl.ph2) + - 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 + #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) + #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) + + sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) + + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) + #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part + + gs_part * ampl.gs2.as_float() + #+ sqrt(omega / 2) * ( #gs_ph part + # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) + # + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + # + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) + # - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + # + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + # * ampl.gs1.as_float() + + (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) * mp.qed_t0(b.ov) + - (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( + (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph2 + - einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + - einsum("ka,kb,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) + - einsum("ic,ij,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + - einsum("ic,jc,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) + + einsum("ia,jb,jb->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + + einsum("ia,jb,jb->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) + ) + )) + return AdcBlock(apply, diagonal) + + +def block_ph_ph_2_couple_edge(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + def apply(ampl): + return - (omega/2) * sqrt(2) * ( + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph + - einsum("ka,kb,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) + - einsum("ic,jc,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) + + einsum("ia,jb,jb->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) + + (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) * ampl.gs.as_float() # gs-part + ) + return AdcBlock(apply, diagonal) + + + +def block_ph_ph_2_phot_couple_edge(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + def apply(ampl): + return - (omega/2) * sqrt(2) * ( + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 + - einsum("kb,ka,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + - einsum("jc,ic,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + + einsum("jb,ia,jb->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + ) + return AdcBlock(apply, diagonal) + + + # # 2nd order coupling # @@ -1389,6 +1886,29 @@ def adc2_qed_ph_ph_2_couple_gs_part(hf, mp, intermediates): + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) +@register_as_intermediate +def adc2_qed_ph_ph_2_couple_inner_gs_part(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + return sqrt(omega / 2) * ( # gs_ph contribution + - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) + + 2 * omega * mp.qed_t1(b.ov) + - 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) + + 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + + sqrt(2) * ( + - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)))) + + + @register_as_intermediate def adc2_qed_ph_ph_2_phot_couple_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -1406,6 +1926,23 @@ def adc2_qed_ph_ph_2_phot_couple_gs_part(hf, mp, intermediates): + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) +@register_as_intermediate +def adc2_qed_ph_ph_2_phot_couple_inner_gs_part(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + return sqrt(omega) * ( #gs_ph part + - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) + + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) + - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + + @register_as_intermediate def adc2_qed_ph_ph_2_phot_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -1427,6 +1964,27 @@ def adc2_qed_ph_ph_2_phot_gs_part(hf, mp, intermediates): + 0.5 * omega * mp.qed_t0(b.ov)) +@register_as_intermediate +def adc2_qed_ph_ph_2_phot2_gs_part(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + return (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution + - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + + sqrt(3) * ( + - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + + (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) + - (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + ) + - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) + + omega * mp.qed_t0(b.ov)) + + @register_as_intermediate def adc2_qed_couple_i1(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) From 28235eb5fb69308479f09dbb8b4664a8fd053392 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 9 Jul 2021 16:02:40 +0200 Subject: [PATCH 03/64] added the lower order terms to all second order blocks --- adcc/AdcMatrix.py | 96 +++++++++++++++++++++++++++++++++++++++++-- adcc/adc_pp/matrix.py | 51 ++++++++++++++++++----- adcc/workflow.py | 4 +- 3 files changed, 134 insertions(+), 17 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 52a83d85..39b75d60 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -210,6 +210,52 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): #self.__init_space_data_0(self.__diagonal_1) """ + if method.base_method.name == "adc2": + self.phot2 = AdcMatrix_submatrix(method, hf_or_mp, "phot2") + + self.blocks_ph_22 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot2", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + + self.elec_couple_inner = AdcMatrix_submatrix(method, hf_or_mp, "elec_couple_inner") + + self.blocks_ph_21 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_couple_inner", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + + self.elec_couple_edge = AdcMatrix_submatrix(method, hf_or_mp, "elec_couple_edge") + + self.blocks_ph_20 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_couple_edge", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + + self.phot_couple_inner = AdcMatrix_submatrix(method, hf_or_mp, "phot_couple_inner") + + self.blocks_ph_12 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot_couple_inner", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + + self.phot_couple_edge = AdcMatrix_submatrix(method, hf_or_mp, "phot_couple_edge") + + self.blocks_ph_02 = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot_couple_edge", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + # build QED_AmplitudeVector self.blocks_ph_1_temp = {} @@ -221,10 +267,17 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): self.blocks_ph_1_temp[key + "_phot"] = self.blocks_ph_11[key] self.blocks_ph = {**self.blocks_ph_00, **self.blocks_ph_1_temp} - self.__diagonal_gs = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph - if "gs_gs" in block and not block.endswith("phot")) # coupling gs_gs blocks have diagonal = 0 + self.__diagonal_gs = 0 #sum(self.blocks_ph[block].diagonal for block in self.blocks_ph + # if "ph_gs" in block and not block.endswith("phot")) # coupling gs_gs blocks have diagonal = 0 self.__diagonal_gs1 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph - if "gs_gs" in block and block.endswith("phot")) + if "ph_gs" in block and block.endswith("phot")) + + #print("following should be the blocks, that make up the diagonal for gs_gs") + #for block in self.blocks_ph: + # if "ph_gs" in block and block.endswith("phot"): + # print("this is the block ", block) + #print(self.__diagonal_gs) + #print(self.__diagonal_gs1) #self.blocks_ph = {**self.elec.blocks_ph, **self.blocks_ph_1_temp} #self.blocks_ph = QED_AmplitudeVector(0, self.elec.blocks_ph, 0, self.phot.blocks_ph) #self.__diagonal = QED_AmplitudeVector(0, self.elec.diagonal, 0, self.phot.diagonal) @@ -273,7 +326,7 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): # since the guesses need the whole matrix object as input, istead of just the diagonals, it is useful to define # self.elec and self.phot as the the corresponding original matrices # this is done in one class, since we also require the full matrix for the solver - + def __init_space_data_qed(self, diagonal0): self.__init_space_data(diagonal0) @@ -897,6 +950,41 @@ def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates= variant=variant) for block, order in block_orders.items() if order is not None } + elif subblock == "phot2": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot2", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + elif subblock == "phot_couple_inner": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot_couple_inner", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + elif subblock == "phot_couple_edge": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_phot_couple_edge", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + elif subblock == "elec_couple_inner": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_couple_inner", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } + elif subblock == "elec_couple_edge": + self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + block: ppmatrix.block(self.ground_state, block.split("_"), + order=str(order) + "_couple_edge", intermediates=self.intermediates, + variant=variant) + for block, order in block_orders.items() if order is not None + } else: ValueError("Using this class you have to give the parameter elec_or_phot") diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 4c2a57e1..2a707bfb 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -453,7 +453,7 @@ def block_ph_gs_1_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return 2 * omega * ampl.gs2 - return AdcBlock(apply, omega) + return AdcBlock(apply, 2 * omega) def block_ph_gs_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -1043,7 +1043,8 @@ def apply(ampl): + 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), - ampl.ph)) + ampl.ph) + - sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph)) # 1. order #maybe this whole block is just + einsum("kjbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) return AdcBlock(apply, diagonal) @@ -1054,8 +1055,8 @@ def block_ph_gs_2_phot(hf, mp, intermediates): d_vv = zeros_like(hf.fvv) d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - - diagonal = - omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + # 1. order + diagonal = - omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + omega def apply(ampl): return (einsum("jb,jb->", ( + 0.5 * omega * mp.qed_t0(b.ov) @@ -1068,7 +1069,8 @@ def apply(ampl): #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) ) - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - ampl.ph1)) + ampl.ph1) + + omega * ampl.gs1) # 1. order #(omega * einsum("jb,jb->", einsum("jj,bb->jb", d_oo, d_vv), ampl.ph1) #- (omega / 2) * (sqrt(2) - 1) * einsum("jb,jb->", (einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) # - einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) @@ -1084,8 +1086,8 @@ def block_ph_gs_2_phot2(hf, mp, intermediates): d_vv = zeros_like(hf.fvv) d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - - diagonal = - omega * (sqrt(3) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + # 1. order + diagonal = - omega * (sqrt(3) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + 2 * omega def apply(ampl): return (einsum("jb,jb->", ( + omega * mp.qed_t0(b.ov) @@ -1098,7 +1100,8 @@ def apply(ampl): #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) ) - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - ampl.ph2)) + ampl.ph2) + + 2 * omega * ampl.gs2) # 1. order return AdcBlock(apply, diagonal) @@ -1122,7 +1125,8 @@ def apply(ampl): + 0.5 * sqrt(2) * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + sqrt(2) * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), - ampl.ph2)) + ampl.ph2) + + (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2)) # 1. order return AdcBlock(apply, diagonal) @@ -1143,7 +1147,8 @@ def apply(ampl): + 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), - ampl.ph1)) + ampl.ph1) + - sqrt(omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1)) # 1. order #maybe this whole block is just + einsum("kjbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) return AdcBlock(apply, diagonal) @@ -1240,6 +1245,7 @@ def apply(ampl): #+ direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) @@ -1267,6 +1273,8 @@ def apply(ampl): + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph) + qed_gs_part * ampl.gs.as_float() + + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph) # 1. order + - (1/2) * einsum("ib,ab->ia", ampl.ph, mp.qed_t0_df(b.vv)) # 1. order #+ (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution # - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) # - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) @@ -1353,6 +1361,8 @@ def apply(ampl): + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) + gs_part * ampl.gs.as_float() + + sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) # 1. order #+ sqrt(omega / 2) * ( # gs_ph contribution # - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) # + omega * mp.qed_t1(b.ov) @@ -1408,6 +1418,9 @@ def apply(ampl): + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part + gs_part * ampl.gs1.as_float() + + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs1.as_float()) # gs_ph block # 1. order #+ sqrt(omega / 2) * ( #gs_ph part # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) # + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) @@ -1462,13 +1475,15 @@ def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * (sqrt(2) - 0.5) * ( # instead of 1 should be sqrt(2) + + (-omega/2) * (sqrt(2) - 0.5) * ( - 2 * direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + + einsum("ii,aa->ia", d_oo, d_vv) * omega # 1. order #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) @@ -1503,6 +1518,9 @@ def apply(ampl): # + einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo))) # * ampl.gs1.as_float() + gs_part * ampl.gs1.as_float() + + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) # 1. order + - (1/2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) # 1. order + + omega * ampl.ph1 # 1. order #+ (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution # - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) # + sqrt(2) * ( @@ -1553,6 +1571,8 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the term - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 # 1. order )) def apply(ampl): @@ -1572,6 +1592,9 @@ def apply(ampl): + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph2) + (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 + gs_part * ampl.gs2.as_float() + + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) # 1. order + - (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) # 1. order + + 2 * omega * ampl.ph2 # 1. order )) return AdcBlock(apply, diagonal) @@ -1617,6 +1640,9 @@ def apply(ampl): + einsum("jb,ia,jb->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) + einsum("jb,ia,jb->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) ) + + sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) # 1. order + + (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part # 1. order )) return AdcBlock(apply, diagonal) @@ -1678,6 +1704,9 @@ def apply(ampl): + einsum("ia,jb,jb->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + einsum("ia,jb,jb->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) ) + + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs2.as_float()) # gs_ph block # 1. order )) return AdcBlock(apply, diagonal) diff --git a/adcc/workflow.py b/adcc/workflow.py index 921b2dd9..fa208e63 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -505,11 +505,11 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= for guess_index in np.arange(len(guesses_elec)): # build QED_AmplitudeVectors from AmplitudeVector guesses #if hasattr(guesses_elec[0], "pphh"): if contains_doubles: - print("doubles guesses are set up") + #print("doubles guesses are set up") guesses_tmp.append(QED_AmplitudeVector(guess_elec0[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, guess_phot0[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh)) else: - print("singles guesses are set up") + #print("singles guesses are set up") guesses_tmp.append(QED_AmplitudeVector(guess_elec0[guess_index], guesses_elec[guess_index].ph, None, guess_phot0[guess_index], guesses_phot[guess_index].ph, None)) From c64a6d611c9557c40105f5f4e2d0817e6117084d Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 13 Jul 2021 14:15:19 +0200 Subject: [PATCH 04/64] full qed-adc2 converges for few states of all tested molecules and basis sets, but performance is garbage --- adcc/AdcMatrix.py | 72 +++++++++++++++++++++-- adcc/AmplitudeVector.py | 107 +++++++++++++++++++++++----------- adcc/adc_pp/matrix.py | 43 +++++++++----- adcc/functions.py | 9 ++- adcc/solver/preconditioner.py | 2 +- adcc/workflow.py | 15 +++-- 6 files changed, 186 insertions(+), 62 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 39b75d60..bfa71fb5 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -265,12 +265,25 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): self.blocks_ph_1_temp[key + "_phot_couple"] = self.blocks_ph_01[key] for key in self.blocks_ph_11: self.blocks_ph_1_temp[key + "_phot"] = self.blocks_ph_11[key] + + if hasattr(self.elec.diagonal(), "pphh"): + for key in self.blocks_ph_20: + self.blocks_ph_1_temp[key + "_couple_edge"] = self.blocks_ph_20[key] + for key in self.blocks_ph_21: + self.blocks_ph_1_temp[key + "_couple_inner"] = self.blocks_ph_21[key] + for key in self.blocks_ph_02: + self.blocks_ph_1_temp[key + "_phot_couple_edge"] = self.blocks_ph_02[key] + for key in self.blocks_ph_12: + self.blocks_ph_1_temp[key + "_phot_couple_inner"] = self.blocks_ph_12[key] + for key in self.blocks_ph_22: + self.blocks_ph_1_temp[key + "_phot2"] = self.blocks_ph_22[key] self.blocks_ph = {**self.blocks_ph_00, **self.blocks_ph_1_temp} self.__diagonal_gs = 0 #sum(self.blocks_ph[block].diagonal for block in self.blocks_ph # if "ph_gs" in block and not block.endswith("phot")) # coupling gs_gs blocks have diagonal = 0 self.__diagonal_gs1 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph if "ph_gs" in block and block.endswith("phot")) + #print("following should be the blocks, that make up the diagonal for gs_gs") #for block in self.blocks_ph: @@ -285,8 +298,12 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): if hasattr(self.elec.diagonal(), "pphh"): # this needs to be adapted, so that the diagonals for gs and gs1 are actually those from matrix.py, # but if this is only for the guesses, this should only be a perfomance issue and not relevant for the precision #print("from adcmatrix init diagonal has pphh part") + self.__diagonal_gs2 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph + if "ph_gs" in block and block.endswith("phot2")) + self.__diagonal = QED_AmplitudeVector(self.__diagonal_gs, self.elec.diagonal().ph, self.elec.diagonal().pphh, - self.__diagonal_gs1, self.phot.diagonal().ph, self.phot.diagonal().pphh) + self.__diagonal_gs1, self.phot.diagonal().ph, self.phot.diagonal().pphh, + self.__diagonal_gs2, self.phot2.diagonal().ph, self.phot2.diagonal().pphh) else: #print("from adcmatrix init diagonal has no pphh part!!!!!!!!!!!!!!!!!!!") self.__diagonal = QED_AmplitudeVector(self.__diagonal_gs, self.elec.diagonal().ph, None, @@ -333,7 +350,10 @@ def __init_space_data_qed(self, diagonal0): axis_spaces0 = self.axis_spaces # this (and the following) will be assigned in the submatrix class as well axis_lengths0 = self.axis_lengths shape0 = self.shape - self.shape = ((shape0[0]+1) * 2, (shape0[0]+1) * 2) + if hasattr(diagonal0, "pphh"): + self.shape = ((shape0[0]+1) * 3, (shape0[0]+1) * 3) + else: + self.shape = ((shape0[0]+1) * 2, (shape0[0]+1) * 2) self.axis_spaces["gs"] = ["o0", "v0"] self.axis_lengths["gs"] = 1 # axis_spaces and axis_lengths both are dicts, referring to "ph", "pphh" as a key, so either make extra blocks here, or probably better @@ -407,7 +427,7 @@ def diagonal(self, block=None): warnings.warn("Support for the block argument will be dropped " "in 0.16.0.") if block == "g": - return self.__diagonal.gs #this is just a float, but it could be e.g. omega, for which I dont know if required data is given in this function + return self.__diagonal.gs if block == "s": return self.__diagonal.ph if block == "d": @@ -461,16 +481,57 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude # maybe also gs_ph and gs_pphh blocks can be included here, by adding them to the ph_ph and pphh_pphh blocks, respectively. # this should be possible, since we give v as ampl in matrix.py, so we can select e.g. ampl.gs1 in the ph_ph block. #print("this is v.pphh from matvec ", v.pphh) + #try: + # self.phot_couple.matvec(v).ph + self.phot_couple_edge.matvec(v) + # print("works yeaaaaaaaaaaaaah") + #except: + # print("god damn") + #print("shape of phot couple edge", self.phot_couple_edge, type(self.phot_couple_edge)) + #print("shape of phot couple inner", self.phot_couple_inner.matvec(v), type(self.phot_couple_inner.matvec(v))) + + phot_couple_edge_with_doubles = AmplitudeVector(ph=self.phot_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) + elec_couple_edge_with_doubles = AmplitudeVector(ph=self.elec_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) + + elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + phot_couple_edge_with_doubles #self.phot_couple_edge.matvec(v) + phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) + #phot2_part = self.elec_couple_edge.matvec(v) + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + phot2_part = elec_couple_edge_with_doubles + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + gs_part = 0 + gs1_part = 0 + gs2_part = 0 + + """ elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) gs_part = 0 gs1_part = 0 + if "pphh" in elec_part.blocks_ph: + gs2_part = 0 + elec_part = elec_part + self.phot_couple_edge.matvec(v) + phot_part = phot_part + self.phot_couple_inner.matvec(v) + phot2_part = self.elec_couple_edge.matvec(v) + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + """ + #else: + # elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + # phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + for block in self.blocks_ph: if "gs" in block and not block.startswith("gs"): #print(block, type(self.blocks_ph[block].apply(v)), self.blocks_ph[block].apply(v)) + if "pphh" in elec_part.blocks_ph: + if "phot2" in block: + gs2_part += self.blocks_ph[block].apply(v) + if "phot_couple_edge" in block: + gs_part += self.blocks_ph[block].apply(v) + if "phot_couple_inner" in block: + gs1_part += self.blocks_ph[block].apply(v) + if "couple_edge" in block: + gs2_part += self.blocks_ph[block].apply(v) + if "couple_inner" in block: + gs2_part += self.blocks_ph[block].apply(v) if "phot_couple" in block: - gs_part += self.blocks_ph[block].apply(v) + gs_part += self.blocks_ph[block].apply(v) # check whether the if statements always grant the correct blocks!!!!!!!!!!!!!!!!!!!!!!!!! elif "phot" in block: gs1_part += self.blocks_ph[block].apply(v) elif "couple" in block: @@ -586,7 +647,8 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude """ #print(res.gs, res.elec, res.gs1, res.phot) if "pphh" in elec_part.blocks_ph: - return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh) + return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, + gs2_part, phot2_part.ph, phot2_part.pphh) else: return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None) diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index adf71a0c..9994c06d 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -227,35 +227,57 @@ def __radd__(self, other): return AmplitudeVector(**ret) -class QED_AmplitudeVector: +class QED_AmplitudeVector: # it seems all operations, without further specification of pphh part, are unused and can be omitted #def __init__(self, gs=None, elec=None, gs1=None, phot=None): - def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None):#, gs2=None, ph2=None, pphh2=None): - #if (elec is None and gs is None) and (phot is None and gs1 is None): - # raise ValueError("Either electronic or photonic part must be given.") + def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, gs2=None, ph2=None, pphh2=None): # also write this class with *args, **kwargs + # maybe do this via a list e.g. ph=[ph, ph1, ph2] + + self.gs = gs_vec(gs) + self.gs1 = gs_vec(gs1) + + self.gs2 = gs_vec(gs2) - self.gs = gs_vec(gs, is_elec=True) - self.gs1 = gs_vec(gs1, is_elec=False) if pphh != None: self.elec = AmplitudeVector(ph=ph, pphh=pphh) self.phot = AmplitudeVector(ph=ph1, pphh=pphh1) + self.phot2 = AmplitudeVector(ph=ph2, pphh=pphh2) else: self.elec = AmplitudeVector(ph=ph) self.phot = AmplitudeVector(ph=ph1) try: self.ph = ph self.ph1 = ph1 + self.ph2 = ph2 except AttributeError: pass try: self.pphh = pphh self.pphh1 = pphh1 + self.pphh2 = pphh2 except AttributeError: # there are no doubles terms --> ADC(0) or ADC(1) pass - self.elec0 = self.gs # so the rest of the class doesn't have to be rewritten - self.phot0 = self.gs1 # so the rest of the class doesn't have to be rewritten + #self.elec0 = self.gs # so the rest of the class doesn't have to be rewritten + #self.phot0 = self.gs1 # so the rest of the class doesn't have to be rewritten + + """ + if len(args) != 0: + if len(args) != 3: + raise AttributeError + else: + self.phot2 = AmplitudeVector(ph=args[1], pphh=args[2]) + self.gs2 = gs_vec(args[0]) + try: + self.ph2 = args[1] + self.pphh2 = args[2] + except AttributeError: + pass + """ + + + #if self.phot is None: # self.phot = self.elec.zeros_like() #zeros_like(self.elec) # self.phot0 = 0 @@ -275,10 +297,11 @@ def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None): def dot(self, invec): def dot_(self, invec): if "pphh" in self.elec.blocks_ph: - return (self.elec0 * invec.elec0 + self.elec.ph.dot(invec.elec.ph) + self.elec.pphh.dot(invec.elec.pphh) - + self.phot0 * invec.phot0 + self.phot.ph.dot(invec.phot.ph) + self.phot.pphh.dot(invec.phot.pphh)) + return (self.gs * invec.gs + self.elec.ph.dot(invec.elec.ph) + self.elec.pphh.dot(invec.elec.pphh) + + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + self.phot.pphh.dot(invec.phot.pphh) + + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) + self.phot2.pphh.dot(invec.phot2.pphh)) else: - return self.elec0 * invec.elec0 + self.elec.ph.dot(invec.elec.ph) + self.phot0 * invec.phot0 + self.phot.ph.dot(invec.phot.ph) + return self.gs * invec.gs + self.elec.ph.dot(invec.elec.ph) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) if isinstance(invec, list): list_temp = [] # to return a np.array with different dimensions (gs,ph,pphh), we have to fill them into a list and then convert the list ??? # even though this list should only be appended by floats, the lower approach didnt work out @@ -317,20 +340,21 @@ def __matmul__(self, other): def __sub__(self, invec): if isinstance(invec, QED_AmplitudeVector): - return QED_AmplitudeVector(self.elec0 - invec.elec0, self.elec.__sub__(invec.elec), self.phot0 - invec.phot0, self.phot.__sub__(invec.phot)) + return QED_AmplitudeVector(self.gs - invec.gs, self.elec.__sub__(invec.elec), self.gs1 - invec.gs1, self.phot.__sub__(invec.phot)) elif isinstance(invec, (float, int)): # for diagonal - shift in preconditioner.py # this results in a scalar in block pphh, if pphh is originally None if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(gs=self.elec0 - invec, ph=self.elec.ph.__sub__(invec), pphh=self.elec.pphh.__sub__(invec), - gs1=self.phot0 - invec, ph1=self.phot.ph.__sub__(invec), pphh1=self.phot.pphh.__sub__(invec)) + return QED_AmplitudeVector(gs=self.gs - invec, ph=self.elec.ph.__sub__(invec), pphh=self.elec.pphh.__sub__(invec), + gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), pphh1=self.phot.pphh.__sub__(invec), + gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec), pphh2=self.phot2.pphh.__sub__(invec)) else: - return QED_AmplitudeVector(gs=self.elec0 - invec, ph=self.elec.ph.__sub__(invec), gs1=self.phot0 - invec, ph1=self.phot.ph.__sub__(invec)) + return QED_AmplitudeVector(gs=self.gs - invec, ph=self.elec.ph.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec)) def __mul__(self, scalar): - return QED_AmplitudeVector(scalar * self.elec0, self.elec.__mul__(scalar), scalar * self.phot0, self.phot.__mul__(scalar)) + return QED_AmplitudeVector(scalar * self.gs, self.elec.__mul__(scalar), scalar * self.gs1, self.phot.__mul__(scalar)) def __rmul__(self, scalar): - return QED_AmplitudeVector(scalar * self.elec0, self.elec.__rmul__(scalar), scalar * self.phot0, self.phot.__rmul__(scalar)) + return QED_AmplitudeVector(scalar * self.gs, self.elec.__rmul__(scalar), scalar * self.gs1, self.phot.__rmul__(scalar)) def __truediv__(self, other): #print(type(self.elec), self.elec.ph) @@ -341,40 +365,57 @@ def __truediv__(self, other): #else: if isinstance(other, QED_AmplitudeVector): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(gs=self.elec0 / other.elec0, ph=self.elec.ph.__truediv__(other.elec.ph), pphh=self.elec.pphh.__truediv__(other.elec.pphh), - gs1=self.phot0 / other.phot0, ph1=self.phot.ph.__truediv__(other.phot.ph), pphh1=self.phot.pphh.__truediv__(other.phot.pphh)) + return QED_AmplitudeVector(gs=self.gs / other.gs, ph=self.elec.ph.__truediv__(other.elec.ph), pphh=self.elec.pphh.__truediv__(other.elec.pphh), + gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), pphh1=self.phot.pphh.__truediv__(other.phot.pphh), + gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph), pphh2=self.phot2.pphh.__truediv__(other.phot2.pphh)) else: - return QED_AmplitudeVector(gs=self.elec0 / other.elec0, ph=self.elec.ph.__truediv__(other.elec.ph), - gs1=self.phot0 / other.phot0, ph1=self.phot.ph.__truediv__(other.phot.ph)) + return QED_AmplitudeVector(gs=self.gs / other.gs, ph=self.elec.ph.__truediv__(other.elec.ph), + gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph)) elif isinstance(other, (float, int)): #return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), # gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other)) if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), pphh=self.elec.pphh.__truediv__(other), - gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other), pphh1=self.phot.pphh.__truediv__(other)) + return QED_AmplitudeVector(gs=self.gs / other, ph=self.elec.ph.__truediv__(other), pphh=self.elec.pphh.__truediv__(other), + gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), pphh1=self.phot.pphh.__truediv__(other), + gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other), pphh2=self.phot2.pphh.__truediv__(other)) else: - return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), - gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other)) + return QED_AmplitudeVector(gs=self.gs / other, ph=self.elec.ph.__truediv__(other), + gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other)) def zeros_like(self): - return QED_AmplitudeVector(0, self.elec.zeros_like(), 0, self.phot.zeros_like()) + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(0, self.elec.zeros_like(), 0, self.phot.zeros_like(), 0, self.phot2.zeros_like()) + else: + return QED_AmplitudeVector(0, self.elec.zeros_like(), 0, self.phot.zeros_like()) def empty_like(self): - return QED_AmplitudeVector([], self.elec.empty_like(), [], self.phot.empty_like()) + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector([], self.elec.empty_like(), [], self.phot.empty_like(), [], self.phot2.empty_like()) + else: + return QED_AmplitudeVector([], self.elec.empty_like(), [], self.phot.empty_like()) def copy(self): - return QED_AmplitudeVector(self.elec0, self.elec.copy(), self.phot0, self.phot.copy()) + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(self.gs, self.elec.copy(), self.gs1, self.phot.copy(), self.gs2, self.phot2.copy()) + else: + return QED_AmplitudeVector(self.gs, self.elec.copy(), self.gs1, self.phot.copy()) def evaluate(self): #print(self.elec0, self.elec, self.phot0, self.phot) self.elec.evaluate() self.phot.evaluate() try: - self.elec0.evaluate() - self.phot0.evaluate() + self.gs.evaluate() + self.gs1.evaluate() except: - self.elec0 - self.phot0 + self.gs + self.gs1 + if "pphh" in self.elec.blocks_ph: + self.phot2.evaluate() + try: + self.gs2.evaluate() + except: + self.gs2 return self @@ -383,7 +424,7 @@ def evaluate(self): class gs_vec: # this class only exists, to forward gs and gs1 to the relevant operators - def __init__(self, val, is_elec): + def __init__(self, val): #print("uses gs_vec init") self.val = val #print(type(self.val)) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 2a707bfb..a6268121 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -249,7 +249,7 @@ def apply(ampl): def block_ph_ph_0(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo if hasattr(hf, "coupling"):# and hasattr(hf, "qed_hf"): - diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), #change to QED_AmplitudeVector + diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) #np.insert(diagonal, 0, 0.) @@ -373,7 +373,8 @@ def block_pphh_pphh_0_couple(hf, mp, intermediates): block_pphh_pphh_0_phot_couple = block_pphh_pphh_0_couple -block_pphh_pphh_0_phot_couple_edge = block_pphh_pphh_0_phot_couple_inner = block_pphh_pphh_0_couple_edge = block_pphh_pphh_0_couple_inner = block_pphh_pphh_0_couple +block_pphh_pphh_0_phot_couple_edge = block_pphh_pphh_0_phot_couple_inner = block_pphh_pphh_0_couple +block_pphh_pphh_0_couple_edge = block_pphh_pphh_0_couple_inner = block_pphh_pphh_0_couple def block_pphh_pphh_0_phot(hf, mp, intermediates): def apply(ampl): @@ -418,9 +419,11 @@ def block_pphh_ph_0(hf, mp, intermediates): block_cvs_pphh_ph_0 = block_pphh_ph_0 block_pphh_ph_0_couple = block_pphh_ph_0_phot_couple = block_pphh_ph_0_phot = block_pphh_ph_0 -block_pphh_ph_0_couple_edge = block_pphh_ph_0_couple_inner = block_pphh_ph_0_phot_couple_edge = block_pphh_ph_0_phot_couple_inner = block_pphh_ph_0_phot2 = block_pphh_ph_0 +block_pphh_ph_0_couple_edge = block_pphh_ph_0_couple_inner = block_pphh_ph_0 +block_pphh_ph_0_phot_couple_edge = block_pphh_ph_0_phot_couple_inner = block_pphh_ph_0_phot2 = block_pphh_ph_0 block_ph_pphh_0_couple = block_ph_pphh_0_phot_couple = block_ph_pphh_0_phot = block_ph_pphh_0 -block_ph_pphh_0_couple_edge = block_ph_pphh_0_couple_inner = block_ph_pphh_0_phot_couple_edge = block_ph_pphh_0_phot_couple_inner = block_ph_pphh_0_phot2 = block_ph_pphh_0 +block_ph_pphh_0_couple_edge = block_ph_pphh_0_couple_inner = block_ph_pphh_0 +block_ph_pphh_0_phot_couple_edge = block_ph_pphh_0_phot_couple_inner = block_ph_pphh_0_phot2 = block_ph_pphh_0 @@ -473,7 +476,8 @@ def block_pphh_gs_1(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) block_pphh_gs_1_couple = block_pphh_gs_1_phot_couple = block_pphh_gs_1_phot = block_pphh_gs_1 -block_pphh_gs_1_couple_edge = block_pphh_gs_1_couple_inner = block_pphh_gs_1_phot_couple_edge = block_pphh_gs_1_phot_couple_inner = block_pphh_gs_1_phot2 = block_pphh_gs_1 +block_pphh_gs_1_couple_edge = block_pphh_gs_1_couple_inner = block_pphh_gs_1 +block_pphh_gs_1_phot_couple_edge = block_pphh_gs_1_phot_couple_inner = block_pphh_gs_1_phot2 = block_pphh_gs_1 @@ -967,11 +971,17 @@ def apply(ampl): def block_pphh_ph_1_couple_edge(hf, mp, intermediates): + #def apply(ampl): + # return AmplitudeVector(pphh=mp.t2oo.zeros_like()) + #return AdcBlock(apply, 0) return AdcBlock(lambda ampl: 0, 0) block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge def block_ph_pphh_1_couple_edge(hf, mp, intermediates): + #def apply(ampl): + # return AmplitudeVector(ph=mp.df(b.ov).zeros_like()) + #return AdcBlock(apply, 0) return AdcBlock(lambda ampl: 0, 0) block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge @@ -1159,8 +1169,8 @@ def block_ph_gs_2_phot_couple_edge(hf, mp, intermediates): diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) def apply(ampl): - return (- 0.5 * omega * sqrt(2) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - - einsum("kb,jk,jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2))) + return (- 0.5 * omega * sqrt(2) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv), ampl.ph2) + - einsum("kb,jk,jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo), ampl.ph2))) return AdcBlock(apply, diagonal) @@ -1637,8 +1647,8 @@ def apply(ampl): - einsum("kb,ka,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) - einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) - einsum("jc,ic,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) - + einsum("jb,ia,jb->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) - + einsum("jb,ia,jb->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) + + einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) + + einsum("jb,jb->", mp.qed_t0_df(b.ov), ampl.ph1) * mp.qed_t1(b.ov) ) + sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) # 1. order @@ -1699,10 +1709,10 @@ def apply(ampl): (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph2 - einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - einsum("ka,kb,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) - - einsum("ic,ij,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + - einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - einsum("ic,jc,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) - + einsum("ia,jb,jb->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - + einsum("ia,jb,jb->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) + + einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) * mp.qed_t0(b.ov) + + einsum("jb,jb->", mp.qed_t1(b.ov), ampl.ph2) * mp.qed_t0_df(b.ov) ) + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order @@ -1719,9 +1729,9 @@ def apply(ampl): einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph - einsum("ka,kb,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - einsum("ic,jc,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - + einsum("ia,jb,jb->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - + (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) - - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) * ampl.gs.as_float() # gs-part + + einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph) * mp.qed_t1(b.ov) + + (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo))) * ampl.gs.as_float() # gs-part ) return AdcBlock(apply, diagonal) @@ -1735,7 +1745,8 @@ def apply(ampl): einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 - einsum("kb,ka,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - einsum("jc,ic,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - + einsum("jb,ia,jb->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + #+ einsum("jb,ia,jb->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + + einsum("jb,jb->", mp.qed_t1(b.ov), ampl.ph2) * mp.qed_t1_df(b.ov) ) return AdcBlock(apply, diagonal) diff --git a/adcc/functions.py b/adcc/functions.py index 4a030fff..b67885be 100644 --- a/adcc/functions.py +++ b/adcc/functions.py @@ -140,7 +140,14 @@ def lincomb(coefficients, tensors, evaluate=False): elec_part = lincomb(coefficients, elec_list, evaluate=evaluate) phot_part = lincomb(coefficients, phot_list, evaluate=evaluate) if "pphh" in elec_part.blocks_ph: - return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh) + gs2_part = 0 + phot2_list = [] + for coeff_ind, ten in enumerate(tensors): + gs2_part += coefficients[coeff_ind] * ten.gs2 + phot2_list.append(ten.phot2) + phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) + return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, + gs2_part, phot2_part.ph, phot2_part.pphh) else: return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None) #print(qed_vec.ph) diff --git a/adcc/solver/preconditioner.py b/adcc/solver/preconditioner.py index 05b564e5..ab4385c6 100644 --- a/adcc/solver/preconditioner.py +++ b/adcc/solver/preconditioner.py @@ -70,7 +70,7 @@ def apply(self, invecs): "to a single vector if shifts is " "only a single number.") return invecs / (self.diagonal - self.shifts) - elif isinstance(invecs, QED_AmplitudeVector): # i dont think this is used anywhere + elif isinstance(invecs, QED_AmplitudeVector): # I dont think this is used anywhere if not isinstance(self.shifts, (float, np.number)): raise TypeError("Can only apply JacobiPreconditioner " "to a single vector if shifts is " diff --git a/adcc/workflow.py b/adcc/workflow.py index fa208e63..26044a41 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -493,25 +493,28 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= "{} electronic and {} photonic guesses".format(len(guesses_elec), len(guesses_phot))) # for now we make gs guess zero print("groundstate guesses are still zero, see workflow.py func obtain_guesses_by_inspection_qed") - guess_elec0 = np.zeros(len(guesses_elec)) - guess_phot0 = np.zeros(len(guesses_phot)) # using omega (actual diagonal 1st order) results in strange behaviour of davidson solver + guess_gs = np.zeros(len(guesses_elec)) + guess_gs1 = np.zeros(len(guesses_phot)) # using omega (actual diagonal 1st order) results in strange behaviour of davidson solver guesses_tmp = [] try: dummy_var = guesses_elec[0].pphh #print(dummy_var) contains_doubles = True + guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) + guess_gs2 = np.zeros(len(guesses_phot2)) except AttributeError: contains_doubles = False for guess_index in np.arange(len(guesses_elec)): # build QED_AmplitudeVectors from AmplitudeVector guesses #if hasattr(guesses_elec[0], "pphh"): if contains_doubles: #print("doubles guesses are set up") - guesses_tmp.append(QED_AmplitudeVector(guess_elec0[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, - guess_phot0[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh)) + guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, + guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, + guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) else: #print("singles guesses are set up") - guesses_tmp.append(QED_AmplitudeVector(guess_elec0[guess_index], guesses_elec[guess_index].ph, None, - guess_phot0[guess_index], guesses_phot[guess_index].ph, None)) + guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, None, + guess_gs1[guess_index], guesses_phot[guess_index].ph, None)) # guesses_tmp needs to be normalized then #for vec in guesses_tmp: From c906dc96870b23f7049ca8467d73260d4e8e79de Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Sat, 17 Jul 2021 16:52:55 +0200 Subject: [PATCH 05/64] adc1 expanded to also include doubly excited photonic contributions --- adcc/AdcMatrix.py | 104 +++++++++++++++++++++-------------- adcc/AmplitudeVector.py | 28 ++++++---- adcc/adc_pp/matrix.py | 6 ++ adcc/adc_pp/transition_dm.py | 3 +- adcc/functions.py | 19 ++++--- adcc/solver/davidson.py | 5 +- adcc/workflow.py | 9 ++- 7 files changed, 112 insertions(+), 62 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index bfa71fb5..aea94f1c 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -210,7 +210,7 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): #self.__init_space_data_0(self.__diagonal_1) """ - if method.base_method.name == "adc2": + if method.base_method.name == "adc2" or method.base_method.name == "adc1": self.phot2 = AdcMatrix_submatrix(method, hf_or_mp, "phot2") self.blocks_ph_22 = { # TODO Rename to self.block in 0.16.0 @@ -266,23 +266,25 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): for key in self.blocks_ph_11: self.blocks_ph_1_temp[key + "_phot"] = self.blocks_ph_11[key] - if hasattr(self.elec.diagonal(), "pphh"): - for key in self.blocks_ph_20: - self.blocks_ph_1_temp[key + "_couple_edge"] = self.blocks_ph_20[key] - for key in self.blocks_ph_21: - self.blocks_ph_1_temp[key + "_couple_inner"] = self.blocks_ph_21[key] - for key in self.blocks_ph_02: - self.blocks_ph_1_temp[key + "_phot_couple_edge"] = self.blocks_ph_02[key] - for key in self.blocks_ph_12: - self.blocks_ph_1_temp[key + "_phot_couple_inner"] = self.blocks_ph_12[key] - for key in self.blocks_ph_22: - self.blocks_ph_1_temp[key + "_phot2"] = self.blocks_ph_22[key] + #if hasattr(self.elec.diagonal(), "pphh"): + for key in self.blocks_ph_20: + self.blocks_ph_1_temp[key + "_couple_edge"] = self.blocks_ph_20[key] + for key in self.blocks_ph_21: + self.blocks_ph_1_temp[key + "_couple_inner"] = self.blocks_ph_21[key] + for key in self.blocks_ph_02: + self.blocks_ph_1_temp[key + "_phot_couple_edge"] = self.blocks_ph_02[key] + for key in self.blocks_ph_12: + self.blocks_ph_1_temp[key + "_phot_couple_inner"] = self.blocks_ph_12[key] + for key in self.blocks_ph_22: + self.blocks_ph_1_temp[key + "_phot2"] = self.blocks_ph_22[key] self.blocks_ph = {**self.blocks_ph_00, **self.blocks_ph_1_temp} self.__diagonal_gs = 0 #sum(self.blocks_ph[block].diagonal for block in self.blocks_ph # if "ph_gs" in block and not block.endswith("phot")) # coupling gs_gs blocks have diagonal = 0 self.__diagonal_gs1 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph if "ph_gs" in block and block.endswith("phot")) + self.__diagonal_gs2 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph + if "ph_gs" in block and block.endswith("phot2")) #print("following should be the blocks, that make up the diagonal for gs_gs") @@ -298,8 +300,8 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): if hasattr(self.elec.diagonal(), "pphh"): # this needs to be adapted, so that the diagonals for gs and gs1 are actually those from matrix.py, # but if this is only for the guesses, this should only be a perfomance issue and not relevant for the precision #print("from adcmatrix init diagonal has pphh part") - self.__diagonal_gs2 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph - if "ph_gs" in block and block.endswith("phot2")) + #self.__diagonal_gs2 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph + # if "ph_gs" in block and block.endswith("phot2")) self.__diagonal = QED_AmplitudeVector(self.__diagonal_gs, self.elec.diagonal().ph, self.elec.diagonal().pphh, self.__diagonal_gs1, self.phot.diagonal().ph, self.phot.diagonal().pphh, @@ -307,7 +309,8 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): else: #print("from adcmatrix init diagonal has no pphh part!!!!!!!!!!!!!!!!!!!") self.__diagonal = QED_AmplitudeVector(self.__diagonal_gs, self.elec.diagonal().ph, None, - self.__diagonal_gs1, self.phot.diagonal().ph, None) + self.__diagonal_gs1, self.phot.diagonal().ph, None, + self.__diagonal_gs2, self.phot2.diagonal().ph, None) self.__init_space_data_qed(self.elec.diagonal()) # here we need to adapt the attributes, defined via this function # namely self.axis_spaces, self.axis_lengths, self.shape self.__diagonal.evaluate() @@ -350,10 +353,11 @@ def __init_space_data_qed(self, diagonal0): axis_spaces0 = self.axis_spaces # this (and the following) will be assigned in the submatrix class as well axis_lengths0 = self.axis_lengths shape0 = self.shape - if hasattr(diagonal0, "pphh"): - self.shape = ((shape0[0]+1) * 3, (shape0[0]+1) * 3) - else: - self.shape = ((shape0[0]+1) * 2, (shape0[0]+1) * 2) + #if hasattr(diagonal0, "pphh"): + # self.shape = ((shape0[0]+1) * 3, (shape0[0]+1) * 3) + #else: + # self.shape = ((shape0[0]+1) * 2, (shape0[0]+1) * 2) + self.shape = ((shape0[0]+1) * 3, (shape0[0]+1) * 3) self.axis_spaces["gs"] = ["o0", "v0"] self.axis_lengths["gs"] = 1 # axis_spaces and axis_lengths both are dicts, referring to "ph", "pphh" as a key, so either make extra blocks here, or probably better @@ -489,13 +493,23 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude #print("shape of phot couple edge", self.phot_couple_edge, type(self.phot_couple_edge)) #print("shape of phot couple inner", self.phot_couple_inner.matvec(v), type(self.phot_couple_inner.matvec(v))) - phot_couple_edge_with_doubles = AmplitudeVector(ph=self.phot_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) - elec_couple_edge_with_doubles = AmplitudeVector(ph=self.elec_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) + - elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + phot_couple_edge_with_doubles #self.phot_couple_edge.matvec(v) phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) - #phot2_part = self.elec_couple_edge.matvec(v) + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) - phot2_part = elec_couple_edge_with_doubles + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + + if "pphh" in phot_part.blocks_ph: + phot_couple_edge_with_doubles = AmplitudeVector(ph=self.phot_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) + elec_couple_edge_with_doubles = AmplitudeVector(ph=self.elec_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) + elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + phot_couple_edge_with_doubles #self.phot_couple_edge.matvec(v) + #phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) + #phot2_part = self.elec_couple_edge.matvec(v) + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + phot2_part = elec_couple_edge_with_doubles + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + else: + #phot_couple_edge_with_singles = AmplitudeVector(ph=v.ph.zeros_like()) + #elec_couple_edge_with_singles = AmplitudeVector(ph=v.ph.zeros_like()) + elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + phot2_part = self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + gs_part = 0 gs1_part = 0 gs2_part = 0 @@ -518,25 +532,35 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude for block in self.blocks_ph: if "gs" in block and not block.startswith("gs"): + #print(block) #print(block, type(self.blocks_ph[block].apply(v)), self.blocks_ph[block].apply(v)) - if "pphh" in elec_part.blocks_ph: - if "phot2" in block: - gs2_part += self.blocks_ph[block].apply(v) - if "phot_couple_edge" in block: - gs_part += self.blocks_ph[block].apply(v) - if "phot_couple_inner" in block: - gs1_part += self.blocks_ph[block].apply(v) - if "couple_edge" in block: - gs2_part += self.blocks_ph[block].apply(v) - if "couple_inner" in block: - gs2_part += self.blocks_ph[block].apply(v) - if "phot_couple" in block: - gs_part += self.blocks_ph[block].apply(v) # check whether the if statements always grant the correct blocks!!!!!!!!!!!!!!!!!!!!!!!!! - elif "phot" in block: + #if "pphh" in elec_part.blocks_ph: + if block.endswith("phot2"): + #print("phot2") + gs2_part += self.blocks_ph[block].apply(v) + elif block.endswith("phot_couple_edge"): + #print("phot_couple_edge") + gs_part += self.blocks_ph[block].apply(v) + elif block.endswith("phot_couple_inner"): + #print("phot_couple_inner") gs1_part += self.blocks_ph[block].apply(v) - elif "couple" in block: + elif block.endswith("couple_edge"): + #print("couple_edge") + gs2_part += self.blocks_ph[block].apply(v) + elif block.endswith("couple_inner"): + #print("couple_inner") + gs2_part += self.blocks_ph[block].apply(v) + elif block.endswith("phot_couple"): + #print("phot_couple") + gs_part += self.blocks_ph[block].apply(v) + elif block.endswith("phot"): + #print("phot") + gs1_part += self.blocks_ph[block].apply(v) + elif block.endswith("couple"): + #print("couple") gs1_part += self.blocks_ph[block].apply(v) else: # elec + #print("elec") gs_part += self.blocks_ph[block].apply(v) """ elif "gs" in block and block.startswith("gs"): @@ -650,7 +674,7 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None) + return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index 9994c06d..f058cf84 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -245,6 +245,7 @@ def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, else: self.elec = AmplitudeVector(ph=ph) self.phot = AmplitudeVector(ph=ph1) + self.phot2 = AmplitudeVector(ph=ph2) try: self.ph = ph self.ph1 = ph1 @@ -301,7 +302,8 @@ def dot_(self, invec): + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + self.phot.pphh.dot(invec.phot.pphh) + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) + self.phot2.pphh.dot(invec.phot2.pphh)) else: - return self.gs * invec.gs + self.elec.ph.dot(invec.elec.ph) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + return (self.gs * invec.gs + self.elec.ph.dot(invec.elec.ph) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) ) if isinstance(invec, list): list_temp = [] # to return a np.array with different dimensions (gs,ph,pphh), we have to fill them into a list and then convert the list ??? # even though this list should only be appended by floats, the lower approach didnt work out @@ -348,7 +350,8 @@ def __sub__(self, invec): gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), pphh1=self.phot.pphh.__sub__(invec), gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec), pphh2=self.phot2.pphh.__sub__(invec)) else: - return QED_AmplitudeVector(gs=self.gs - invec, ph=self.elec.ph.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec)) + return QED_AmplitudeVector(gs=self.gs - invec, ph=self.elec.ph.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), + gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec)) def __mul__(self, scalar): return QED_AmplitudeVector(scalar * self.gs, self.elec.__mul__(scalar), scalar * self.gs1, self.phot.__mul__(scalar)) @@ -370,7 +373,8 @@ def __truediv__(self, other): gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph), pphh2=self.phot2.pphh.__truediv__(other.phot2.pphh)) else: return QED_AmplitudeVector(gs=self.gs / other.gs, ph=self.elec.ph.__truediv__(other.elec.ph), - gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph)) + gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), + gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph)) elif isinstance(other, (float, int)): #return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), # gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other)) @@ -380,7 +384,8 @@ def __truediv__(self, other): gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other), pphh2=self.phot2.pphh.__truediv__(other)) else: return QED_AmplitudeVector(gs=self.gs / other, ph=self.elec.ph.__truediv__(other), - gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other)) + gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), + gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) def zeros_like(self): if "pphh" in self.elec.blocks_ph: @@ -404,18 +409,21 @@ def evaluate(self): #print(self.elec0, self.elec, self.phot0, self.phot) self.elec.evaluate() self.phot.evaluate() + self.phot2.evaluate() try: self.gs.evaluate() self.gs1.evaluate() + self.gs2.evaluate() except: self.gs self.gs1 - if "pphh" in self.elec.blocks_ph: - self.phot2.evaluate() - try: - self.gs2.evaluate() - except: - self.gs2 + self.gs2 + #if "pphh" in self.elec.blocks_ph: + # self.phot2.evaluate() + # try: + # self.gs2.evaluate() + # except: + # self.gs2 return self diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index a6268121..f428e9a5 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -452,6 +452,7 @@ def apply(ampl): return AdcBlock(apply, 0) #return AdcBlock(lambda ampl: 0, 0) + def block_ph_gs_1_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): @@ -472,6 +473,8 @@ def apply(ampl): + + def block_pphh_gs_1(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) @@ -681,6 +684,8 @@ def block_ph_ph_1_couple_edge(hf, mp, intermediates): block_ph_ph_1_phot_couple_edge = block_ph_ph_1_couple_edge + + def block_ph_ph_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) @@ -744,6 +749,7 @@ def apply(ampl): + def diagonal_pphh_pphh_1(hf): # Fock matrix and ovov diagonal term (sometimes called "intermediate diagonal") dinterm_ov = (direct_sum("a-i->ia", hf.fvv.diagonal(), hf.foo.diagonal()) diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index 5f1e3b40..3cbe4c5a 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -150,7 +150,8 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): print("norm squared of amplitude.elec", ampl_elec_norm) print("norm squared of amplitude", amplitude @ amplitude) print("beware, that we normalize .elec, before giving it to the oscillator strength routine") - print("amplitude.gs is {} and amplitude.gs1 is {}".format(amplitude.gs.as_float(), amplitude.gs1.as_float())) + print("amplitude.gs is {}, amplitude.gs1 is {} and amplitude.gs2 is {}".format(amplitude.gs.as_float(), + amplitude.gs1.as_float(), amplitude.gs2.as_float())) normalized_ampl = amplitude.elec / sqrt(ampl_elec_norm) ret = DISPATCH[method.name](ground_state, normalized_ampl, intermediates) else: diff --git a/adcc/functions.py b/adcc/functions.py index b67885be..a7b9fde0 100644 --- a/adcc/functions.py +++ b/adcc/functions.py @@ -130,26 +130,31 @@ def lincomb(coefficients, tensors, evaluate=False): # same for .gs1 gs_part = 0 gs1_part = 0 + gs2_part = 0 + phot2_list = [] elec_list = [] phot_list = [] for coeff_ind, ten in enumerate(tensors): gs_part += coefficients[coeff_ind] * ten.gs gs1_part += coefficients[coeff_ind] * ten.gs1 + gs2_part += coefficients[coeff_ind] * ten.gs2 elec_list.append(ten.elec) phot_list.append(ten.phot) + phot2_list.append(ten.phot2) elec_part = lincomb(coefficients, elec_list, evaluate=evaluate) phot_part = lincomb(coefficients, phot_list, evaluate=evaluate) + phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) if "pphh" in elec_part.blocks_ph: - gs2_part = 0 - phot2_list = [] - for coeff_ind, ten in enumerate(tensors): - gs2_part += coefficients[coeff_ind] * ten.gs2 - phot2_list.append(ten.phot2) - phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) + # gs2_part = 0 + # phot2_list = [] + # for coeff_ind, ten in enumerate(tensors): + # gs2_part += coefficients[coeff_ind] * ten.gs2 + # phot2_list.append(ten.phot2) + # phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None) + return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) #print(qed_vec.ph) #print(coefficients) #print(tensors[0][block]) diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 5439377e..5a992a8b 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -397,7 +397,10 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, if not max_subspace: # TODO Arnoldi uses this: # max_subspace = max(2 * n_ep + 1, 20) - max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) + # max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) # original + print("for qed-adc we use double the standard max_subspace, due to convergence problems," + "if the doubly excited photonic space contributes to the states") + max_subspace = max(12 * n_ep, 20, 10 * len(guesses)) def convergence_test(state): state.residuals_converged = state.residual_norms < conv_tol diff --git a/adcc/workflow.py b/adcc/workflow.py index 26044a41..ca2e5f17 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -487,6 +487,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= # require the matrix (AdcMatrix), instead of just the diagonal, which is a QED_Amplitude guesses_elec = obtain_guesses_by_inspection(matrix.elec, n_guesses, kind, n_guesses_doubles) guesses_phot = obtain_guesses_by_inspection(matrix.phot, n_guesses, kind, n_guesses_doubles) + guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) #print("this is from obtain_guess_qed from workflow: guesses_elec[0].pphh", guesses_elec[0].pphh) if len(guesses_elec) != len(guesses_phot): raise InputError("amount of guesses for electronic and photonic must be equal, but are" @@ -495,13 +496,14 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= print("groundstate guesses are still zero, see workflow.py func obtain_guesses_by_inspection_qed") guess_gs = np.zeros(len(guesses_elec)) guess_gs1 = np.zeros(len(guesses_phot)) # using omega (actual diagonal 1st order) results in strange behaviour of davidson solver + guess_gs2 = np.zeros(len(guesses_phot2)) guesses_tmp = [] try: dummy_var = guesses_elec[0].pphh #print(dummy_var) contains_doubles = True - guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) - guess_gs2 = np.zeros(len(guesses_phot2)) + #guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) + #guess_gs2 = np.zeros(len(guesses_phot2)) except AttributeError: contains_doubles = False for guess_index in np.arange(len(guesses_elec)): # build QED_AmplitudeVectors from AmplitudeVector guesses @@ -514,7 +516,8 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= else: #print("singles guesses are set up") guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, None, - guess_gs1[guess_index], guesses_phot[guess_index].ph, None)) + guess_gs1[guess_index], guesses_phot[guess_index].ph, None, + guess_gs2[guess_index], guesses_phot2[guess_index].ph, None)) # guesses_tmp needs to be normalized then #for vec in guesses_tmp: From adc869588b07b82944a3e664e3e374cdc9a3c7b9 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 12 Aug 2021 10:56:55 +0200 Subject: [PATCH 06/64] still trying to fix the symmetry problem, which is probably not only due to the doubles symmetry of the independent blocks --- adcc/AdcMatrix.py | 4 +++ adcc/AmplitudeVector.py | 23 ++++++++----- adcc/adc_pp/matrix.py | 6 ++++ adcc/solver/davidson.py | 31 ++++++++++++++++++ adcc/solver/explicit_symmetrisation.py | 45 +++++++++++++++++++++++--- adcc/workflow.py | 15 +++++++-- 6 files changed, 109 insertions(+), 15 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index aea94f1c..0ada844c 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -735,6 +735,7 @@ def construct_symmetrisation_for_blocks(self): Returns a dictionary block identifier -> function """ ret = {} + print("antisymm function is used in QED_Amplitudevector part of AdcMatrix function") if self.is_core_valence_separated: # CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a) ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)]) @@ -742,9 +743,11 @@ def construct_symmetrisation_for_blocks(self): def symmetrise_generic_adc_doubles(invec): # doubles part is antisymmetric wrt. (i,j,a,b) <-> (i,j,b,a) scratch = invec.antisymmetrise([(2, 3)]) + #print(type(invec), invec) # doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a) return scratch.symmetrise([(0, 1), (2, 3)]) ret["pphh"] = symmetrise_generic_adc_doubles + print(ret) return ret def dense_basis(self, axis_blocks=None, ordering="adcc"): @@ -1327,6 +1330,7 @@ def construct_symmetrisation_for_blocks(self): Returns a dictionary block identifier -> function """ ret = {} + print("antisymm function is used in Amplitudevector part of AdcMatrix function") if self.is_core_valence_separated: # CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a) ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)]) diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index f058cf84..5d1b834a 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -407,17 +407,21 @@ def copy(self): def evaluate(self): #print(self.elec0, self.elec, self.phot0, self.phot) + #print(type(self.elec), type(self.phot), type(self.phot2)) self.elec.evaluate() self.phot.evaluate() self.phot2.evaluate() - try: - self.gs.evaluate() - self.gs1.evaluate() - self.gs2.evaluate() - except: - self.gs - self.gs1 - self.gs2 + self.gs.as_float() + self.gs1.as_float() + self.gs2.as_float() + #try: + # self.gs.evaluate() + # self.gs1.evaluate() + # self.gs2.evaluate() + #except: + # self.gs + # self.gs1 + # self.gs2 #if "pphh" in self.elec.blocks_ph: # self.phot2.evaluate() # try: @@ -487,6 +491,9 @@ def __truediv__(self, scalar): def as_float(self): return self.val + def evaluate(self): + return self.val + diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index f428e9a5..ffabab98 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -976,6 +976,7 @@ def apply(ampl): return AdcBlock(apply, 0) + def block_pphh_ph_1_couple_edge(hf, mp, intermediates): #def apply(ampl): # return AmplitudeVector(pphh=mp.t2oo.zeros_like()) @@ -993,6 +994,10 @@ def block_ph_pphh_1_couple_edge(hf, mp, intermediates): block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge +#block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner = block_pphh_ph_1_phot_couple = block_pphh_ph_1_phot_couple_inner = block_pphh_ph_1_couple_edge +#block_ph_pphh_1_couple = block_ph_pphh_1_couple_inner = block_ph_pphh_1_phot_couple = block_ph_pphh_1_phot_couple_inner = block_ph_pphh_1_couple_edge + + # # 2nd order gs blocks (gs_ph blocks in ph_ph) for now these are zero for testing purposes @@ -1262,6 +1267,7 @@ def apply(ampl): - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 5a992a8b..e03b03e9 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -192,6 +192,8 @@ def callback(state, identifier): with state.timer.record("residuals"): # Form residuals, A * SS * v - λ * SS * v = Ax * v + SS * (-λ*v) def form_residual(rval, rvec): + #print("norm of residual vector = ", np.sqrt(rvec @ rvec)) + #rvec = rvec / np.sqrt(rvec @ rvec) coefficients = np.hstack((rvec, -rval * rvec)) #print(type(coefficients), coefficients) #print(type(SS), SS) @@ -212,6 +214,7 @@ def form_residual(rval, rvec): if i in epair_mask] for eigv in eigenvecs: print("norm of eigenvector", np.sqrt(eigv @ eigv)) + #print("eigenvector.ph = ", eigv.ph) # TODO This is misleading ... actually residual_norms contains # the norms squared. That's also the used e.g. in adcman to # check for convergence, so using the norm squared is fine, @@ -282,8 +285,36 @@ def form_residual(rval, rvec): #if isinstance(preconds[0], QED_AmplitudeVector): # #else: + #if "pphh" in preconds[0].elec.blocks_ph: + # explicit_symmetrisation.symmetrise. + # for ind, vec in enumerate(preconds): + # preconds[ind] = QED_AmplitudeVector(gs=vec.gs, ph=vec.elec.ph, pphh=evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)), + # gs1=vec.gs1, ph1=vec.phot.ph, pphh1=evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)), + # gs2=vec.gs2, ph2=vec.phot2.ph, pphh2=evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh))) + + #temp = preconds + #explicit_symmetrisation.symmetrise(preconds) + #new_temp = explicit_symmetrisation.symmetrise(temp) + #if new_temp[0].elec.pphh == preconds[0].elec.pphh: + # print("in davidson symmetrise yields no change in elec") + #else: + # print("in davidson symmetrise yields a change in elec") + #else: # this is unnecessary, since symmetrization only applies for double excitation space + #tmp = preconds + #explicit_symmetrisation.symmetrise(tmp) + #if tmp[0].elec.pphh == preconds[0].elec.pphh: + # print("nothing changed") + #else: + # print("something changed") + #tmp2 = preconds[0].elec + #explicit_symmetrisation.symmetrise(tmp2) + #if tmp2.pphh == preconds[0].elec.pphh: + # print("nothing changed for elec only") + #else: + # print("something changed for elec only") explicit_symmetrisation.symmetrise(preconds) + # Project the components of the preconditioned vectors away # which are already contained in the subspace. # Then add those, which have a significant norm to the subspace. diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index 1527e319..eb92dfdc 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -23,7 +23,8 @@ from libadcc import amplitude_vector_enforce_spin_kind from adcc import evaluate -from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector, gs_vec +import numpy as np # TODO # This interface is not that great and leads to duplicate information @@ -63,6 +64,7 @@ def symm_subroutine(vec): for b in vec.blocks_ph: if b not in self.symmetrisation_functions: continue + #print(b) vec[b] = evaluate(self.symmetrisation_functions[b](vec[b])) return vec @@ -70,9 +72,44 @@ def symm_subroutine(vec): return self.symmetrise([new_vectors])[0] elif isinstance(new_vectors[0], QED_AmplitudeVector): # we dont have to symmetrise the gs blocks...actually only the pphh blocks are symmetrised here - for vec in new_vectors: - vec.elec = symm_subroutine(vec.elec) - vec.phot = symm_subroutine(vec.phot) + if "pphh" in new_vectors[0].elec.blocks_ph: + test_list = new_vectors + for ind, vec in enumerate(new_vectors): + #if vec.elec.pphh != evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)): #vec.elec.pphh != symm_subroutine(vec.elec).pphh: + # print("something changed") + #else: + # print("nothing changed") + #vec = QED_AmplitudeVector(gs=vec.gs, ph=vec.elec.ph, pphh=evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)), + # gs1=vec.gs1, ph1=vec.phot.ph, pphh1=evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)), + # gs2=vec.gs2, ph2=vec.phot2.ph, pphh2=evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh))) + test_list[ind].elec = symm_subroutine(vec.elec) #evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)) + test_list[ind].phot = symm_subroutine(vec.phot) #evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)) + test_list[ind].phot2 = symm_subroutine(vec.phot2) #evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh)) + if test_list[0].elec.pphh == new_vectors[0].elec.pphh: + print("in symm nothing changed") + if new_vectors[0].elec.pphh != evaluate(self.symmetrisation_functions["pphh"](test_list[0].elec.pphh)): + print("but something changed for the symmetrization, which was not passed to the QED_AmplitudeVector") + else: + print("in symm something changed") + #if vec.elec.pphh == evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)): + # print("symm still yields no change") + #print(type(evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh))), evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)).shape) + #if new_vec.elec == symm_subroutine(vec.elec): + # print("correctly changed in symmetrise function") + #elif new_vec.elec == vec.elec: + # print("nothing changed in symmetrise function") + #else: + # print("not correctly changed in symmetrise function") + # diff = new_vec.elec - symm_subroutine(vec.elec) + # print("squared norm of difference with symmetrise = ", np.sqrt(diff @ diff)) + # diff2 = new_vec.elec - vec.elec + # print("squared norm of difference without symmetrise = ", np.sqrt(diff2 @ diff2)) + # diff3 = vec.elec - symm_subroutine(vec.elec) + # print("squared norm of difference between no symmetrise and symmetrise = ", np.sqrt(diff3 @ diff3)) # why is this zero?????????? + # #print(type(new_vec.elec.pphh), type(symm_subroutine(vec.elec).pphh)) + #vec.elec = symm_subroutine(vec.elec) + #vec.phot = symm_subroutine(vec.phot) + #vec.phot2 = symm_subroutine(vec.phot2) elif isinstance(new_vectors[0], AmplitudeVector): for vec in new_vectors: vec = symm_subroutine(vec) diff --git a/adcc/workflow.py b/adcc/workflow.py index ca2e5f17..8105db95 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -495,8 +495,12 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= # for now we make gs guess zero print("groundstate guesses are still zero, see workflow.py func obtain_guesses_by_inspection_qed") guess_gs = np.zeros(len(guesses_elec)) - guess_gs1 = np.zeros(len(guesses_phot)) # using omega (actual diagonal 1st order) results in strange behaviour of davidson solver - guess_gs2 = np.zeros(len(guesses_phot2)) + guess_gs1 = np.zeros(len(guesses_phot)) + guess_gs2 = np.zeros(len(guesses_phot2)) + # these guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a + # decoupled state. For gs2 this is a little less important, because the coupling is weaker. + guess_gs1[0] = 0#2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, + guess_gs2[1] = 0#5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. guesses_tmp = [] try: dummy_var = guesses_elec[0].pphh @@ -528,8 +532,13 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= #print("from workflow guess_qed...guesses_tmp[0].pphh = ", guesses_tmp[0].pphh) #normalized_guesses = [vec / np.sqrt(vec @ vec) for vec in guesses_tmp] #print("after normalization guesses[0].pphh is ", normalized_guesses[0].pphh) + + for guess in guesses_tmp: + print(guess.gs1.as_float(), guess.gs2.as_float()) return [vec / np.sqrt(vec @ vec) for vec in guesses_tmp] - #return guesses + #for vec in guesses_tmp: + # print("norm of guess vector = ", np.sqrt(vec @ vec)) + #return guesses_tmp def setup_solver_printing(solmethod_name, matrix, kind, default_print, output=None): From 3495f9be9da11f93317971b22970403f20447db8 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 19 Aug 2021 13:39:08 +0200 Subject: [PATCH 07/64] still trying to fix the symmetry problem --- adcc/AdcMatrix.py | 12 ++++++------ adcc/ExcitedStates.py | 7 +++++-- adcc/solver/davidson.py | 2 ++ adcc/solver/explicit_symmetrisation.py | 2 ++ adcc/workflow.py | 6 ++---- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 0ada844c..87e86dfe 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -747,7 +747,7 @@ def symmetrise_generic_adc_doubles(invec): # doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a) return scratch.symmetrise([(0, 1), (2, 3)]) ret["pphh"] = symmetrise_generic_adc_doubles - print(ret) + #print(ret) return ret def dense_basis(self, axis_blocks=None, ordering="adcc"): @@ -1173,8 +1173,8 @@ def __init_space_data(self, diagonal): self.axis_lengths[block] = np.prod([ self.mospaces.n_orbs(sp) for sp in self.axis_spaces[block] ]) - self.axis_spaces["gs"] = ["o0", "v0"] - self.axis_lengths["gs"] = 1 + #self.axis_spaces["gs"] = ["o0", "v0"] + #self.axis_lengths["gs"] = 1 self.shape = (sum(self.axis_lengths.values()), sum(self.axis_lengths.values())) @@ -1208,7 +1208,7 @@ def block_spaces(self, block): "will be removed in 0.16.0. " "Use `matrix.axis_spaces[block]` in the future.") return { - "g": self.axis_spaces.get("gs", None), + #"g": self.axis_spaces.get("gs", None), "s": self.axis_spaces.get("ph", None), "d": self.axis_spaces.get("pphh", None), "t": self.axis_spaces.get("ppphhh", None), @@ -1232,8 +1232,8 @@ def diagonal(self, block=None): if block is not None: warnings.warn("Support for the block argument will be dropped " "in 0.16.0.") - if block == "g": - return self.__diagonal.gs #this is just a float, but it could be e.g. omega, for which I dont know if required data is given in this function + #if block == "g": + # return self.__diagonal.gs #this is just a float, but it could be e.g. omega, for which I dont know if required data is given in this function if block == "s": return self.__diagonal.ph if block == "d": diff --git a/adcc/ExcitedStates.py b/adcc/ExcitedStates.py index 10164c04..716876a4 100644 --- a/adcc/ExcitedStates.py +++ b/adcc/ExcitedStates.py @@ -364,10 +364,13 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): to index relative on the HOMO / LUMO / HOCO orbitals. If ``None`` an automatic selection will be made. """ - eV = constants.value("Hartree energy in eV") - vector_format = FormatExcitationVector(self.matrix, tolerance=tolerance, + eV = constants.value("Hartree energy in eV") + vector_format = FormatExcitationVector(self.matrix.elec, tolerance=tolerance, # here self.matrix.elec, due to qed index_format=index_format) + #for qed + for i, vec in enumerate(self.excitation_vector): + self.excitation_vector[i] = vec.elec # Optimise the formatting by pre-inspecting all tensors for tensor in self.excitation_vector: vector_format.optimise_formatting(tensor) diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index e03b03e9..990a01e0 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -214,6 +214,8 @@ def form_residual(rval, rvec): if i in epair_mask] for eigv in eigenvecs: print("norm of eigenvector", np.sqrt(eigv @ eigv)) + #print("orthogonality check 2,3 , 2,4 and 3,4", np.sqrt(eigenvecs[1] @ eigenvecs[2]), + # np.sqrt(eigenvecs[1] @ eigenvecs[3]), np.sqrt(eigenvecs[2] @ eigenvecs[3])) #print("eigenvector.ph = ", eigv.ph) # TODO This is misleading ... actually residual_norms contains # the norms squared. That's also the used e.g. in adcman to diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index eb92dfdc..abf7cb25 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -46,6 +46,7 @@ def __init__(self, matrix): # for the respective block self.symmetrisation_functions = \ matrix.construct_symmetrisation_for_blocks() + print("index symm class init is used") def symmetrise(self, new_vectors): """ @@ -131,6 +132,7 @@ class IndexSpinSymmetrisation(IndexSymmetrisation): def __init__(self, matrix, enforce_spin_kind="singlet"): super().__init__(matrix) self.enforce_spin_kind = enforce_spin_kind + print("index spin symm class is used") def symmetrise(self, new_vectors): if isinstance(new_vectors, AmplitudeVector): diff --git a/adcc/workflow.py b/adcc/workflow.py index 8105db95..9514649e 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -492,15 +492,13 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= if len(guesses_elec) != len(guesses_phot): raise InputError("amount of guesses for electronic and photonic must be equal, but are" "{} electronic and {} photonic guesses".format(len(guesses_elec), len(guesses_phot))) - # for now we make gs guess zero - print("groundstate guesses are still zero, see workflow.py func obtain_guesses_by_inspection_qed") guess_gs = np.zeros(len(guesses_elec)) guess_gs1 = np.zeros(len(guesses_phot)) guess_gs2 = np.zeros(len(guesses_phot2)) # these guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a # decoupled state. For gs2 this is a little less important, because the coupling is weaker. - guess_gs1[0] = 0#2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, - guess_gs2[1] = 0#5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. + guess_gs1[0] = 2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, + guess_gs2[1] = 5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. guesses_tmp = [] try: dummy_var = guesses_elec[0].pphh From bdee15d9f2e25a1058d42692d869303d7fcfe9f3 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 24 Aug 2021 12:18:40 +0200 Subject: [PATCH 08/64] symmetry seems to work fine with restricted triples or singles --- adcc/solver/davidson.py | 13 +++++++ adcc/solver/explicit_symmetrisation.py | 48 +++++++++++++++++++------- adcc/workflow.py | 4 +++ 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 990a01e0..545109ef 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -23,6 +23,7 @@ import sys import warnings import numpy as np +from numpy.lib.function_base import blackman import scipy.linalg as la import scipy.sparse.linalg as sla @@ -284,6 +285,18 @@ def form_residual(rval, rvec): # Explicitly symmetrise the new vectors if requested if explicit_symmetrisation: + #tmp_list = [] + #tmp2 = [] + #for vec in preconds: + # tmp_list.append(vec.elec) + # tmp_list.append(vec.phot) + # tmp_list.append(vec.phot2) + #explicit_symmetrisation.symmetrise(tmp_list) + #for i, vec in enumerate(preconds): + # tmp2.append(QED_AmplitudeVector(gs=vec.gs, ph=vec.elec.ph, pphh=tmp_list[3*i].pphh, + # gs1=vec.gs1, ph1=vec.phot.ph, pphh1=tmp_list[3*i + 1].pphh, + # gs2=vec.gs2, ph2=vec.phot2.ph, pphh2=tmp_list[3*i + 2].pphh)) + #preconds = tmp2 #if isinstance(preconds[0], QED_AmplitudeVector): # #else: diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index abf7cb25..a4c9fb61 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -74,8 +74,13 @@ def symm_subroutine(vec): elif isinstance(new_vectors[0], QED_AmplitudeVector): # we dont have to symmetrise the gs blocks...actually only the pphh blocks are symmetrised here if "pphh" in new_vectors[0].elec.blocks_ph: - test_list = new_vectors - for ind, vec in enumerate(new_vectors): + #print("non explicit symm for qed vec is called") + for vec in new_vectors: + vec.elec = self.symmetrise([vec.elec])[0] + vec.phot = self.symmetrise([vec.phot])[0] + vec.phot2 = self.symmetrise([vec.phot2])[0] + #test_list = new_vectors + #for ind, vec in enumerate(new_vectors): #if vec.elec.pphh != evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)): #vec.elec.pphh != symm_subroutine(vec.elec).pphh: # print("something changed") #else: @@ -83,15 +88,17 @@ def symm_subroutine(vec): #vec = QED_AmplitudeVector(gs=vec.gs, ph=vec.elec.ph, pphh=evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)), # gs1=vec.gs1, ph1=vec.phot.ph, pphh1=evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)), # gs2=vec.gs2, ph2=vec.phot2.ph, pphh2=evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh))) - test_list[ind].elec = symm_subroutine(vec.elec) #evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)) - test_list[ind].phot = symm_subroutine(vec.phot) #evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)) - test_list[ind].phot2 = symm_subroutine(vec.phot2) #evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh)) - if test_list[0].elec.pphh == new_vectors[0].elec.pphh: - print("in symm nothing changed") - if new_vectors[0].elec.pphh != evaluate(self.symmetrisation_functions["pphh"](test_list[0].elec.pphh)): - print("but something changed for the symmetrization, which was not passed to the QED_AmplitudeVector") - else: - print("in symm something changed") + # test_list[ind].elec = self.symmetrise([vec.elec])[0] #evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)) + # test_list[ind].phot = self.symmetrise([vec.phot])[0] + # test_list[ind].phot2 = self.symmetrise([vec.phot2])[0] + #test_list[ind].phot = symm_subroutine(vec.phot) #evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)) + #test_list[ind].phot2 = symm_subroutine(vec.phot2) #evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh)) + #if test_list[0].elec.pphh == new_vectors[0].elec.pphh: + # print("in symm nothing changed") + # if new_vectors[0].elec.pphh != evaluate(self.symmetrisation_functions["pphh"](test_list[0].elec.pphh)): + # print("but something changed for the symmetrization, which was not passed to the QED_AmplitudeVector") + #else: + # print("in symm something changed") #if vec.elec.pphh == evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)): # print("symm still yields no change") #print(type(evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh))), evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)).shape) @@ -112,6 +119,7 @@ def symm_subroutine(vec): #vec.phot = symm_subroutine(vec.phot) #vec.phot2 = symm_subroutine(vec.phot2) elif isinstance(new_vectors[0], AmplitudeVector): + #print("non explicit symm for ampl vec is called") for vec in new_vectors: vec = symm_subroutine(vec) #if not isinstance(vec, AmplitudeVector): @@ -132,20 +140,34 @@ class IndexSpinSymmetrisation(IndexSymmetrisation): def __init__(self, matrix, enforce_spin_kind="singlet"): super().__init__(matrix) self.enforce_spin_kind = enforce_spin_kind - print("index spin symm class is used") + #print("index spin symm class is used") def symmetrise(self, new_vectors): if isinstance(new_vectors, AmplitudeVector): return self.symmetrise([new_vectors])[0] + new_vectors = super().symmetrise(new_vectors) # Enforce singlet (or other spin_kind) spin in the doubles block # of all amplitude vectors for vec in new_vectors: + if isinstance(vec, QED_AmplitudeVector): + if "pphh" in vec.elec.blocks_ph: + #print("explicit symm for qed vec is called") + amplitude_vector_enforce_spin_kind( + vec.elec.pphh, "d", self.enforce_spin_kind + ) + amplitude_vector_enforce_spin_kind( + vec.phot.pphh, "d", self.enforce_spin_kind + ) + amplitude_vector_enforce_spin_kind( + vec.phot2.pphh, "d", self.enforce_spin_kind + ) # Only work on the doubles part # the other blocks are not yet implemented # or nothing needs to be done ("ph" block) - if "pphh" in vec.blocks_ph: + elif "pphh" in vec.blocks_ph: + #print("explicit symm for ampl vec is called") # TODO: Note that the "d" is needed here because the C++ side # does not yet understand ph and pphh amplitude_vector_enforce_spin_kind( diff --git a/adcc/workflow.py b/adcc/workflow.py index 9514649e..cca325f0 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -512,6 +512,10 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= #if hasattr(guesses_elec[0], "pphh"): if contains_doubles: #print("doubles guesses are set up") + # what if this is not ok without restricting singlets/triplets only, because e.g. phot could be singlet and elec triplet ... doesnt seem to matter + #guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, + # guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, + # guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) From 744c2d629b2f413af82616d7e76b91eb9862c625 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 16 Sep 2021 15:34:04 +0200 Subject: [PATCH 09/64] Lanczos works for qed, applied massive improvement to guesses. Also Davidson intermediate results can now be given to Lanczos after e.g. max_iter is reached, for arbitrary convergence, since Davidson crashes for high omega and/or coupling. Triplet multiplicity for n_states calculation still occurs. --- adcc/AdcMatrix.py | 14 +++++++------- adcc/ExcitedStates.py | 9 +++++---- adcc/adc_pp/transition_dm.py | 3 ++- adcc/solver/LanczosIterator.py | 5 +++-- adcc/solver/davidson.py | 1 + adcc/solver/explicit_symmetrisation.py | 6 +++--- adcc/solver/lanczos.py | 11 +++++++++++ adcc/workflow.py | 19 ++++++++++++------- 8 files changed, 44 insertions(+), 24 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 87e86dfe..5dffff04 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -314,13 +314,13 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): self.__init_space_data_qed(self.elec.diagonal()) # here we need to adapt the attributes, defined via this function # namely self.axis_spaces, self.axis_lengths, self.shape self.__diagonal.evaluate() - print(self.elec.blocks_ph) - print(self.blocks_ph) - print(self.axis_spaces) - print(self.axis_lengths) - print(self.shape) - print(self.elec.axis_lengths) - print(self.elec.shape) + #print(self.elec.blocks_ph) + #print(self.blocks_ph) + #print(self.axis_spaces) + #print(self.axis_lengths) + #print(self.shape) + #print(self.elec.axis_lengths) + #print(self.elec.shape) #print("matvec has to be corrected, since .gs and .gs1 parts are not yet included, also remaining blocks have to included") diff --git a/adcc/ExcitedStates.py b/adcc/ExcitedStates.py index 716876a4..07963d12 100644 --- a/adcc/ExcitedStates.py +++ b/adcc/ExcitedStates.py @@ -369,11 +369,12 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): index_format=index_format) #for qed - for i, vec in enumerate(self.excitation_vector): - self.excitation_vector[i] = vec.elec + #for i, vec in enumerate(self.excitation_vector): + # self.excitation_vector[i] = vec.elec # using this leaves the excitation_vector object like this, + # which however is the standard output for the excitation vectors, so only the .elec vector is accessible this way # Optimise the formatting by pre-inspecting all tensors for tensor in self.excitation_vector: - vector_format.optimise_formatting(tensor) + vector_format.optimise_formatting(tensor.elec) # Determine width of a line lw = 2 + vector_format.linewidth @@ -388,7 +389,7 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): head += f", {eev:13.7} eV" ret += "| " + head + (lw - len(head) - 2) * " " + " |\n" ret += separator - formatted = vector_format.format(vec).replace("\n", " |\n| ") + formatted = vector_format.format(vec.elec).replace("\n", " |\n| ") ret += "| " + formatted + " |\n" if i != len(self.excitation_vector) - 1: ret += "\n" diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index 3cbe4c5a..6c56b386 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -146,8 +146,9 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): # for QED_result we are interested in .elec part only, which grants the electronic transitions. # We also dont need .gs since it should be zero...this has to be checked!!! ampl_elec_norm = amplitude.elec @ amplitude.elec + ampl_phot_norm = amplitude.phot @ amplitude.phot print("care, that .gs is not used for transition_dm, but only .elec (check transition_dm.py)") - print("norm squared of amplitude.elec", ampl_elec_norm) + print("norm squared of amplitude.elec", ampl_elec_norm, " norm squared phot", ampl_phot_norm) print("norm squared of amplitude", amplitude @ amplitude) print("beware, that we normalize .elec, before giving it to the oscillator strength routine") print("amplitude.gs is {}, amplitude.gs1 is {} and amplitude.gs2 is {}".format(amplitude.gs.as_float(), diff --git a/adcc/solver/LanczosIterator.py b/adcc/solver/LanczosIterator.py index b3193381..653a3549 100644 --- a/adcc/solver/LanczosIterator.py +++ b/adcc/solver/LanczosIterator.py @@ -26,7 +26,7 @@ from adcc import evaluate, lincomb from adcc.timings import Timer -from adcc.AmplitudeVector import AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector from .orthogonaliser import GramSchmidtOrthogonaliser @@ -64,7 +64,7 @@ def __init__(self, matrix, guesses, ritz_vectors=None, ritz_values=None, if not isinstance(guesses, list): guesses = [guesses] for guess in guesses: - if not isinstance(guess, AmplitudeVector): + if not isinstance(guess, (AmplitudeVector, QED_AmplitudeVector)): raise TypeError("One of the guesses is not an AmplitudeVector") n_block = len(guesses) # Lanczos block size @@ -109,6 +109,7 @@ def __next__(self): # Initialise Lanczos subspace v = self.ortho.orthogonalise(self.residual) self.lanczos_subspace = v + print(v) r = evaluate(self.matrix @ v) alpha = np.empty((self.n_block, self.n_block)) for p in range(self.n_block): diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 545109ef..9e620cba 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -285,6 +285,7 @@ def form_residual(rval, rvec): # Explicitly symmetrise the new vectors if requested if explicit_symmetrisation: + #print(explicit_symmetrisation) #tmp_list = [] #tmp2 = [] #for vec in preconds: diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index a4c9fb61..7316bd49 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -69,11 +69,11 @@ def symm_subroutine(vec): vec[b] = evaluate(self.symmetrisation_functions[b](vec[b])) return vec - if isinstance(new_vectors, AmplitudeVector): + if isinstance(new_vectors, (AmplitudeVector, QED_AmplitudeVector)): return self.symmetrise([new_vectors])[0] elif isinstance(new_vectors[0], QED_AmplitudeVector): # we dont have to symmetrise the gs blocks...actually only the pphh blocks are symmetrised here - if "pphh" in new_vectors[0].elec.blocks_ph: + if "pphh" in new_vectors[0].elec.blocks_ph: # not sure if this is necessary, since symm should only be requested for doubles after all #print("non explicit symm for qed vec is called") for vec in new_vectors: vec.elec = self.symmetrise([vec.elec])[0] @@ -143,7 +143,7 @@ def __init__(self, matrix, enforce_spin_kind="singlet"): #print("index spin symm class is used") def symmetrise(self, new_vectors): - if isinstance(new_vectors, AmplitudeVector): + if isinstance(new_vectors, (AmplitudeVector, QED_AmplitudeVector)): return self.symmetrise([new_vectors])[0] new_vectors = super().symmetrise(new_vectors) diff --git a/adcc/solver/lanczos.py b/adcc/solver/lanczos.py index 14d2c5ce..94a9aa18 100644 --- a/adcc/solver/lanczos.py +++ b/adcc/solver/lanczos.py @@ -210,11 +210,22 @@ def callback(state, identifier): state.eigenvalues = rvals[epair_mask] state.residual_norms = eigenpair_error[epair_mask] converged = np.all(is_rval_converged[epair_mask]) + #if state.eigenvalues[0] <= 0.5: + # print("for testing purposes we end the iterations here, and print the current states, since one eigenvalue is below 0.5") + # converged = True + # TODO For consistency with the Davidson the residual norms are squared # again to give output in the same order of magnitude. state.residual_norms = state.residual_norms**2 + #tmp_state = amend_true_residuals(state, subspace, rvals, + # rvecs, epair_mask) + + #print("norms of eigenvectors") + #for vec in tmp_state.eigenvectors: + # print(np.sqrt(vec @ vec)) + callback(state, "next_iter") state.timer.restart("iteration") diff --git a/adcc/workflow.py b/adcc/workflow.py index cca325f0..a5328aed 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -488,17 +488,22 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= guesses_elec = obtain_guesses_by_inspection(matrix.elec, n_guesses, kind, n_guesses_doubles) guesses_phot = obtain_guesses_by_inspection(matrix.phot, n_guesses, kind, n_guesses_doubles) guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) + n_guess = len(guesses_elec) + for i in np.arange(n_guess): + guesses_phot[i] *= 0.02 + guesses_phot2[i] *= 0.001 #print("this is from obtain_guess_qed from workflow: guesses_elec[0].pphh", guesses_elec[0].pphh) - if len(guesses_elec) != len(guesses_phot): + if n_guess != len(guesses_phot): raise InputError("amount of guesses for electronic and photonic must be equal, but are" "{} electronic and {} photonic guesses".format(len(guesses_elec), len(guesses_phot))) - guess_gs = np.zeros(len(guesses_elec)) - guess_gs1 = np.zeros(len(guesses_phot)) - guess_gs2 = np.zeros(len(guesses_phot2)) + guess_gs = np.zeros(n_guess) + guess_gs1 = np.zeros(n_guess) + guess_gs2 = np.zeros(n_guess) # these guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a # decoupled state. For gs2 this is a little less important, because the coupling is weaker. - guess_gs1[0] = 2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, - guess_gs2[1] = 5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. + guess_gs[n_guess - 3] = 100 + guess_gs1[n_guess - 2] = 2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, + guess_gs2[n_guess - 1] = 5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. guesses_tmp = [] try: dummy_var = guesses_elec[0].pphh @@ -508,7 +513,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= #guess_gs2 = np.zeros(len(guesses_phot2)) except AttributeError: contains_doubles = False - for guess_index in np.arange(len(guesses_elec)): # build QED_AmplitudeVectors from AmplitudeVector guesses + for guess_index in np.arange(n_guess): # build QED_AmplitudeVectors from AmplitudeVector guesses #if hasattr(guesses_elec[0], "pphh"): if contains_doubles: #print("doubles guesses are set up") From 7fdb1274695dcc99d108004d9def00ebdd42e68d Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 8 Oct 2021 12:46:10 +0200 Subject: [PATCH 10/64] check --- adcc/LazyMp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index a46ed0da..d68c32c7 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -319,6 +319,7 @@ def energy_correction(self, level=2): print("full qed MP2 energy correction (standard hf) " + str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0)) print("qed-mp1 correction, due to standard hf input " + str(qed_mp1_correction)) + print("new qed-mp2 correction compared to qed-hf " + str(qed_mp2_correction_0)) elif level == 2 and is_cvs: terms = [(1.0, hf.oovv, self.t2oo), (2.0, hf.ocvv, self.t2oc), From ad0b144da76edf1fe454e0ec92fc3c099e5e3525 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Wed, 13 Oct 2021 11:57:46 +0200 Subject: [PATCH 11/64] check --- adcc/solver/LanczosIterator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adcc/solver/LanczosIterator.py b/adcc/solver/LanczosIterator.py index 653a3549..8b7fa092 100644 --- a/adcc/solver/LanczosIterator.py +++ b/adcc/solver/LanczosIterator.py @@ -109,7 +109,7 @@ def __next__(self): # Initialise Lanczos subspace v = self.ortho.orthogonalise(self.residual) self.lanczos_subspace = v - print(v) + #print(v) r = evaluate(self.matrix @ v) alpha = np.empty((self.n_block, self.n_block)) for p in range(self.n_block): From 9bdca70b146ef21ccb0606c67a283ecfcbbed202 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 19 Oct 2021 11:14:54 +0200 Subject: [PATCH 12/64] check --- adcc/ElectronicTransition.py | 2 ++ adcc/LazyMp.py | 4 ++++ adcc/workflow.py | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py index b9d5cb13..17438587 100644 --- a/adcc/ElectronicTransition.py +++ b/adcc/ElectronicTransition.py @@ -199,6 +199,8 @@ def transition_magnetic_dipole_moment(self): @mark_excitation_property() def oscillator_strength(self): """List of oscillator strengths of all computed states""" + print("final TDMs", self.transition_dipole_moment) + print("final excitation energies", self.excitation_energy) return 2. / 3. * np.array([ np.linalg.norm(tdm)**2 * np.abs(ev) for tdm, ev in zip(self.transition_dipole_moment, diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index d68c32c7..fb421aab 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -69,6 +69,8 @@ def df(self, space): s1, s2 = split_spaces(space) fC = hf.fock(s1 + s1).diagonal() fv = hf.fock(s2 + s2).diagonal() + #print("occupied orbital energies", fC) + #print("unoccupied orbital energies", fv) return direct_sum("-i+a->ia", fC, fv) @cached_member_function @@ -320,6 +322,8 @@ def energy_correction(self, level=2): + str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0)) print("qed-mp1 correction, due to standard hf input " + str(qed_mp1_correction)) print("new qed-mp2 correction compared to qed-hf " + str(qed_mp2_correction_0)) + #print("transition dipoles * coupling * sqrt(2 * freq)", total_dip.ov) + #print("orbital energy differences", self.df(b.ov)) elif level == 2 and is_cvs: terms = [(1.0, hf.oovv, self.t2oo), (2.0, hf.ocvv, self.t2oc), diff --git a/adcc/workflow.py b/adcc/workflow.py index a5328aed..27c96332 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -501,7 +501,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= guess_gs2 = np.zeros(n_guess) # these guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a # decoupled state. For gs2 this is a little less important, because the coupling is weaker. - guess_gs[n_guess - 3] = 100 + #guess_gs[n_guess - 3] = 100 guess_gs1[n_guess - 2] = 2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, guess_gs2[n_guess - 1] = 5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. guesses_tmp = [] @@ -516,6 +516,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= for guess_index in np.arange(n_guess): # build QED_AmplitudeVectors from AmplitudeVector guesses #if hasattr(guesses_elec[0], "pphh"): if contains_doubles: + guess_gs[n_guess - 3] = 100 #print("doubles guesses are set up") # what if this is not ok without restricting singlets/triplets only, because e.g. phot could be singlet and elec triplet ... doesnt seem to matter #guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, @@ -525,6 +526,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) else: + guess_gs[n_guess - 3] = 0#10**7 #print("singles guesses are set up") guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, None, guess_gs1[guess_index], guesses_phot[guess_index].ph, None, From c2c9a1a92be9509159416b4d07b034c37d13d1bc Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 26 Oct 2021 13:38:20 +0200 Subject: [PATCH 13/64] enabled qed-adc1 from qed-hf reference (for single + double photon dispersion) --- adcc/adc_pp/matrix.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index ffabab98..05cc0a04 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -113,7 +113,7 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): # (original 4 blocks + ph_gs and pphh_gs) per "ADC submatrix" # maybe return float from ph/pphh_gs blocks, instead of QED_AmplitudeVector.gs/.gs1 -# For QED-ADC(2) we then require also the 2 photon block, so we introduce the naming convention: +# For QED-ADC(2) we then require also the double energy photon block, so we introduce the naming convention: # elec phot_couple phot_couple_outer # elec_couple phot phot_couple_inner # elec_couple_edge elec_couple_inner phot2 @@ -743,8 +743,29 @@ def apply(ampl): #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #reintroduced (actually canceled from -E_0 (1) + 2 * omega * ampl.ph2 )) - else: - raise NotImplementedError("and not hasattr(hf, qed_hf)") + elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): + omega = float(ReferenceState.get_qed_omega(hf)) + + # Build two Kronecker deltas + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 + + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 + )) + #np.insert(diagonal, 0, omega) + + def apply(ampl): + return AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 + + 2 * omega * ampl.ph2 + )) return AdcBlock(apply, diagonal) From bf46dde4f457b753a809b676640960a8d924c7f9 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 29 Nov 2021 20:04:07 +0100 Subject: [PATCH 14/64] started correcting for wrong factors (cancellations should be done by now)...probably some factors are still wrong --- adcc/adc_pp/matrix.py | 106 ++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 05cc0a04..8f0c6d74 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -462,14 +462,14 @@ def apply(ampl): def block_ph_gs_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return (-1) * sqrt(omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1) + return (-1) * sqrt(omega / 2) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1) return AdcBlock(apply, 0) def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - def apply(ampl): - return (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) - return AdcBlock(apply, 0) + #def apply(ampl): + # return (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) + return AdcBlock(lambda ampl: 0, 0) @@ -692,9 +692,9 @@ def block_ph_ph_1_couple_inner(hf, mp, intermediates): if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): def apply(ampl): return AmplitudeVector(ph=( - sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) - + (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part + #+ (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part )) return AdcBlock(apply, diagonal) @@ -704,7 +704,7 @@ def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): def apply(ampl): return AmplitudeVector(ph=( - sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) - mp.qed_t1_df(b.ov) * ampl.gs2.as_float()) # gs_ph block )) @@ -913,7 +913,7 @@ def block_ph_pphh_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( - -4 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) + -4 * sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) @@ -921,6 +921,8 @@ def apply(ampl): return AdcBlock(apply, 0) +block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner = block_pphh_ph_0_couple +""" def block_pphh_ph_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): @@ -931,8 +933,8 @@ def apply(ampl): #- einsum("ib,ja,ja->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph)) )) return AdcBlock(apply, 0) - - +""" +""" def block_pphh_ph_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): @@ -944,9 +946,11 @@ def apply(ampl): #- einsum("ib,ja,ja->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph)) )) return AdcBlock(apply, 0) +""" +block_ph_pphh_1_phot_couple = block_ph_pphh_1_phot_couple_inner = block_ph_pphh_0_phot_couple - +""" def block_ph_pphh_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): @@ -957,8 +961,8 @@ def apply(ampl): - einsum("jc,ia,jiac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) )) return AdcBlock(apply, 0) - - +""" +""" def block_ph_pphh_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): @@ -970,7 +974,7 @@ def apply(ampl): + 4 * (1 - sqrt(2)) * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh2) )) return AdcBlock(apply, 0) - +""" def block_pphh_ph_1_phot_couple(hf, mp, intermediates): @@ -989,7 +993,7 @@ def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( - -4 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) + -4 * sqrt(omega / 2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) @@ -1098,7 +1102,7 @@ def block_ph_gs_2_phot(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) # 1. order - diagonal = - omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + omega + diagonal = omega #- omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + omega def apply(ampl): return (einsum("jb,jb->", ( + 0.5 * omega * mp.qed_t0(b.ov) @@ -1129,7 +1133,7 @@ def block_ph_gs_2_phot2(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) # 1. order - diagonal = - omega * (sqrt(3) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + 2 * omega + diagonal = 2 * omega #- omega * (sqrt(3) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + 2 * omega def apply(ampl): return (einsum("jb,jb->", ( + omega * mp.qed_t0(b.ov) @@ -1166,9 +1170,9 @@ def apply(ampl): #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + 0.5 * sqrt(2) * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + sqrt(2) * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), + + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), #* sqrt(2) ampl.ph2) - + (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2)) # 1. order + )#+ (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2)) # 1. order return AdcBlock(apply, diagonal) @@ -1198,10 +1202,10 @@ def apply(ampl): def block_ph_gs_2_phot_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + diagonal = - 0.5 * omega * (1/sqrt(2)) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) def apply(ampl): - return (- 0.5 * omega * sqrt(2) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv), ampl.ph2) + return (- 0.5 * omega * (1/sqrt(2)) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv), ampl.ph2) - einsum("kb,jk,jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo), ampl.ph2))) return AdcBlock(apply, diagonal) @@ -1209,7 +1213,7 @@ def apply(ampl): def block_ph_gs_2_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + diagonal = - 0.5 * omega * (1/sqrt(2)) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) return AdcBlock(lambda ampl: 0, diagonal) @@ -1518,13 +1522,13 @@ def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * (sqrt(2) - 0.5) * ( + + (-omega/2) * (sqrt(2) - 0.5) * ( # correct for factor ??????????????????????????????????????????????? - 2 * direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) - + (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) + #+ (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + einsum("ii,aa->ia", d_oo, d_vv) * omega # 1. order #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) @@ -1540,7 +1544,7 @@ def apply(ampl): - einsum("ij,ja->ia", i2, ampl.ph1) - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 - + (-omega/2) * (sqrt(2) - 0.5) * ( + + (-omega/2) * (sqrt(2) - 0.5) * ( # correct for factor ??????????????????????????????????????????????? - 2 * einsum("ib,ab->ia", ampl.ph1, qed_i1) - 2 * einsum("ij,ja->ia", qed_i2, ampl.ph1) + (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) @@ -1553,7 +1557,7 @@ def apply(ampl): # + einsum("jc,iabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph1)) + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph1) - + (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 + #+ (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #+ omega * einsum("ii,aa->ia", d_oo, d_vv) * ampl.gs1.as_float() #this (and following) is from the gs_ph contribution #- (omega / 2) * (sqrt(2) - 1) * (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) # - einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) @@ -1607,13 +1611,13 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the term + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * (sqrt(3) - 0.5) * ( + + (-omega/2) * (sqrt(3) - 0.5) * ( # correct for factor ??????????????????????????????????????????????? - 2 * direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) - + (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) + #+ (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 # 1. order )) @@ -1624,7 +1628,7 @@ def apply(ampl): - einsum("ij,ja->ia", i2, ampl.ph2) - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 - + (-omega/2) * (sqrt(3) - 0.5) * ( + + (-omega/2) * (sqrt(3) - 0.5) * ( # correct for factor ??????????????????????????????????????????????? - 2 * einsum("ib,ab->ia", ampl.ph2, qed_i1) - 2 * einsum("ij,ja->ia", qed_i2, ampl.ph2) + (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph2) @@ -1633,7 +1637,7 @@ def apply(ampl): + einsum("ij,ja->ia", qed_i2_0, ampl.ph2) + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph2) - + (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 + #+ (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 + gs_part * ampl.gs2.as_float() + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) # 1. order - (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) # 1. order @@ -1673,19 +1677,19 @@ def apply(ampl): + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + gs_part * ampl.gs1.as_float() - + (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) - - (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( - (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph1 - - einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) - - einsum("kb,ka,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) - - einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) - - einsum("jc,ic,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) - + einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) - + einsum("jb,jb->", mp.qed_t0_df(b.ov), ampl.ph1) * mp.qed_t1(b.ov) - ) + #+ (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) + #- (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( + # (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph1 + # - einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) + # - einsum("kb,ka,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) + # - einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) + # - einsum("jc,ic,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) + # + einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) + # + einsum("jb,jb->", mp.qed_t0_df(b.ov), ampl.ph1) * mp.qed_t1(b.ov) + #) + sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) # 1. order - + (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part # 1. order + #+ (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part # 1. order )) return AdcBlock(apply, diagonal) @@ -1737,16 +1741,16 @@ def apply(ampl): # - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) # + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) # * ampl.gs1.as_float() - + (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) * mp.qed_t0(b.ov) - - (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( - (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph2 - - einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - - einsum("ka,kb,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) - - einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - - einsum("ic,jc,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) - + einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) * mp.qed_t0(b.ov) - + einsum("jb,jb->", mp.qed_t1(b.ov), ampl.ph2) * mp.qed_t0_df(b.ov) - ) + #+ (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) * mp.qed_t0(b.ov) + #- (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( + # (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph2 + # - einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + # - einsum("ka,kb,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) + # - einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) + # - einsum("ic,jc,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) + # + einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) * mp.qed_t0(b.ov) + # + einsum("jb,jb->", mp.qed_t1(b.ov), ampl.ph2) * mp.qed_t0_df(b.ov) + #) + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order - mp.qed_t1_df(b.ov) * ampl.gs2.as_float()) # gs_ph block # 1. order From b3580aca9ebbd525ae9ecdad8000814f187e927c Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 30 Nov 2021 17:06:38 +0100 Subject: [PATCH 15/64] some factors are still wrong --- adcc/adc_pp/matrix.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 8f0c6d74..e3056c80 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -462,7 +462,7 @@ def apply(ampl): def block_ph_gs_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return (-1) * sqrt(omega / 2) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1) + return (-1) * sqrt(omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1) return AdcBlock(apply, 0) def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): @@ -692,7 +692,7 @@ def block_ph_ph_1_couple_inner(hf, mp, intermediates): if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): def apply(ampl): return AmplitudeVector(ph=( - sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) #+ (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part )) @@ -704,7 +704,7 @@ def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): def apply(ampl): return AmplitudeVector(ph=( - sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) - mp.qed_t1_df(b.ov) * ampl.gs2.as_float()) # gs_ph block )) @@ -913,7 +913,7 @@ def block_ph_pphh_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( - -4 * sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) + -4 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) @@ -993,7 +993,7 @@ def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( - -4 * sqrt(omega / 2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) + -4 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) @@ -1164,13 +1164,13 @@ def apply(ampl): return ( sqrt(omega / 2) * einsum("jb,jb->", ( - einsum("jckb,kc->jb", hf.ovov, mp.qed_t1(b.ov)) + 2 * omega * mp.qed_t1(b.ov) - - 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) - + 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + - 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) * sqrt(2) + + 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) * sqrt(2) - 0.5 * sqrt(2) * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + 0.5 * sqrt(2) * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), #* sqrt(2) + + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))) * sqrt(2), ampl.ph2) )#+ (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2)) # 1. order return AdcBlock(apply, diagonal) @@ -1202,10 +1202,10 @@ def apply(ampl): def block_ph_gs_2_phot_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = - 0.5 * omega * (1/sqrt(2)) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) def apply(ampl): - return (- 0.5 * omega * (1/sqrt(2)) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv), ampl.ph2) + return (- 0.5 * omega * sqrt(2) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv), ampl.ph2) - einsum("kb,jk,jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo), ampl.ph2))) return AdcBlock(apply, diagonal) @@ -1213,7 +1213,7 @@ def apply(ampl): def block_ph_gs_2_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = - 0.5 * omega * (1/sqrt(2)) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) return AdcBlock(lambda ampl: 0, diagonal) From 380a58218d551fd3274a0dac80190925284ed13a Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 16 Dec 2021 16:02:47 +0100 Subject: [PATCH 16/64] QED-ADC(2) for QED-HF reference should work now --- adcc/adc_pp/matrix.py | 477 +++++++++++++++++++++++------------------- 1 file changed, 265 insertions(+), 212 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index e3056c80..fb8872dc 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -827,6 +827,9 @@ def apply(ampl): # # 1st order coupling # + +# these blocks of equal photonic excitation are only valid for a QED-HF reference !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + def block_ph_pphh_1(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( @@ -1001,7 +1004,7 @@ def apply(ampl): return AdcBlock(apply, 0) - +""" def block_pphh_ph_1_couple_edge(hf, mp, intermediates): #def apply(ampl): # return AmplitudeVector(pphh=mp.t2oo.zeros_like()) @@ -1015,8 +1018,11 @@ def block_ph_pphh_1_couple_edge(hf, mp, intermediates): # return AmplitudeVector(ph=mp.df(b.ov).zeros_like()) #return AdcBlock(apply, 0) return AdcBlock(lambda ampl: 0, 0) +""" + +block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge = block_pphh_ph_0_couple -block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge +block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge = block_ph_pphh_0_phot_couple #block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner = block_pphh_ph_1_phot_couple = block_pphh_ph_1_phot_couple_inner = block_pphh_ph_1_couple_edge @@ -1036,37 +1042,56 @@ def block_ph_gs_2(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - def apply(ampl): - return (einsum("jb,jb->", ( + if hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): + def apply(ampl): + return (einsum("jb,jb->", ( + #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) + - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), + ampl.ph)) + + else: + raise NotImplementedError("There are still some adjustments to be done for QED-ADC(2) from a non-QED-HF reference") + def apply(ampl): + return (einsum("jb,jb->", ( - 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov)) # still some terms are missing !!!!!!!!!!!!! + - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), ampl.ph)) return AdcBlock(apply, 0) def block_ph_gs_2_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) - diagonal = - sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + diagonal = 0#- sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) def apply(ampl): return ( sqrt(omega / 2) * einsum("jb,jb->", ( - einsum("jckb,kc->jb", hf.ovov, mp.qed_t1(b.ov)) + omega * mp.qed_t1(b.ov) - - 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) - + 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) - - 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #- 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) + #+ 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + #- 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #+ 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph1)) return AdcBlock(apply, diagonal) @@ -1076,17 +1101,17 @@ def apply(ampl): def block_ph_gs_2_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) - diagonal = - sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + diagonal = 0#- sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) def apply(ampl): return ( sqrt(omega / 2) * einsum("jb,jb->", ( - - 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #- 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #+ 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph) @@ -1105,16 +1130,18 @@ def block_ph_gs_2_phot(hf, mp, intermediates): diagonal = omega #- omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + omega def apply(ampl): return (einsum("jb,jb->", ( - + 0.5 * omega * mp.qed_t0(b.ov) - - 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - + 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - + sqrt(2) * ( + #+ 0.5 * omega * mp.qed_t0(b.ov) + #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) + + 2 * ( - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) ) - - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), ampl.ph1) + omega * ampl.gs1) # 1. order #(omega * einsum("jb,jb->", einsum("jj,bb->jb", d_oo, d_vv), ampl.ph1) @@ -1137,15 +1164,17 @@ def block_ph_gs_2_phot2(hf, mp, intermediates): def apply(ampl): return (einsum("jb,jb->", ( + omega * mp.qed_t0(b.ov) - - 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - + 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - + sqrt(3) * ( + #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) + + 3 * ( - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) ) - - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), + - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), ampl.ph2) + 2 * omega * ampl.gs2) # 1. order return AdcBlock(apply, diagonal) @@ -1154,23 +1183,24 @@ def apply(ampl): def block_ph_gs_2_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) - diagonal = - sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + diagonal = 0#- sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) def apply(ampl): - return ( sqrt(omega / 2) * einsum("jb,jb->", ( + return ( sqrt(omega) * einsum("jb,jb->", ( - einsum("jckb,kc->jb", hf.ovov, mp.qed_t1(b.ov)) + 2 * omega * mp.qed_t1(b.ov) - - 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) * sqrt(2) - + 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) * sqrt(2) - - 0.5 * sqrt(2) * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #- 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) * sqrt(2) + #+ 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) * sqrt(2) + #- 0.5 * sqrt(2) * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + 0.5 * sqrt(2) * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #+ 0.5 * sqrt(2) * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))) * sqrt(2), + + einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) + + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph2) )#+ (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2)) # 1. order return AdcBlock(apply, diagonal) @@ -1185,12 +1215,12 @@ def block_ph_gs_2_couple_inner(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - diagonal = - sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + diagonal = 0#- sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) def apply(ampl): return ( sqrt(omega) * einsum("jb,jb->", ( - - 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #- 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #+ 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph1) @@ -1246,14 +1276,16 @@ def block_ph_ph_2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) - qed_i1 = intermediates.adc2_qed_ph_ph_2_i1 - qed_i2 = intermediates.adc2_qed_ph_ph_2_i2 + #qed_i1 = intermediates.adc2_qed_ph_ph_2_i1 + #qed_i2 = intermediates.adc2_qed_ph_ph_2_i2 #qed_i1 = intermediates.adc2_qed_i1 #qed_i2 = intermediates.adc2_qed_i2 #qed_i1_0 = intermediates.adc2_qed_i1_0 #qed_i2_0 = intermediates.adc2_qed_i2_0 qed_gs_part = intermediates.adc2_qed_ph_ph_2_gs_part if hasattr(hf, "qed_hf"): + qed_i1 = intermediates.adc2_qed_i1 + qed_i2 = intermediates.adc2_qed_i2 diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) @@ -1280,6 +1312,7 @@ def apply(ampl): + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph))) )) else: + raise NotImplementedError("QED-ADC(2) form non-QED-HF reference is not implemented") diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) @@ -1378,8 +1411,8 @@ def apply(ampl): def block_ph_ph_2_couple(hf, mp, intermediates): #one could cash some of the terms here - if hasattr(hf, "qed_hf"): - raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + #if hasattr(hf, "qed_hf"): + # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) gs_part = intermediates.adc2_qed_ph_ph_2_couple_gs_part qed_i1 = intermediates.adc2_qed_couple_i1 @@ -1392,15 +1425,15 @@ def block_ph_ph_2_couple(hf, mp, intermediates): #one could cash some of the ter diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): - return AmplitudeVector(ph=(0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), - direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph) - - einsum("kb,ka,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph) - einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph) # this is different from mp.diff_df, but why??? - - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) - einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph)) # this is different from mp.diff_df, but why??? + return AmplitudeVector(ph=(#0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), + # direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph) + # - einsum("kb,ka,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph) + # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph) # this is different from mp.diff_df, but why??? + # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) + # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph)) # this is different from mp.diff_df, but why??? + einsum("ib,ab->ia", ampl.ph, qed_i1) + einsum("ij,ja->ia", qed_i2, ampl.ph) - - 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph + #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed + sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed @@ -1433,12 +1466,12 @@ def apply(ampl): def block_ph_ph_2_phot_couple(hf, mp, intermediates): #one could cash some of the terms here - if hasattr(hf, "qed_hf"): - raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + #if hasattr(hf, "qed_hf"): + # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) gs_part = intermediates.adc2_qed_ph_ph_2_phot_couple_gs_part - qed_i1 = intermediates.adc2_qed_phot_couple_i1 - qed_i2 = intermediates.adc2_qed_phot_couple_i2 + qed_i1 = intermediates.adc2_qed_couple_i1 + qed_i2 = intermediates.adc2_qed_couple_i2 #d_oo = zeros_like(hf.foo) #d_vv = zeros_like(hf.fvv) @@ -1448,15 +1481,15 @@ def block_ph_ph_2_phot_couple(hf, mp, intermediates): #one could cash some of th diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): - return AmplitudeVector(ph=(0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), - direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph1) - - einsum("ka,kb,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph1) - einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? - - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) - einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? + return AmplitudeVector(ph=(#0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), + # direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph1) + # - einsum("ka,kb,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph1) + # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? + # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) + # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? + einsum("ib,ab->ia", ampl.ph1, qed_i1) + einsum("ij,ja->ia", qed_i2, ampl.ph1) - - 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 + #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) + sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) @@ -1489,8 +1522,8 @@ def apply(ampl): def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms here - if hasattr(hf, "qed_hf"): - raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + #if hasattr(hf, "qed_hf"): + # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 @@ -1515,21 +1548,21 @@ def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 - qed_i1_0 = intermediates.adc2_qed_i1_0 - qed_i2_0 = intermediates.adc2_qed_i2_0 + #qed_i1_0 = intermediates.adc2_qed_i1_0 + #qed_i2_0 = intermediates.adc2_qed_i2_0 gs_part = intermediates.adc2_qed_ph_ph_2_phot_gs_part diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * (sqrt(2) - 0.5) * ( # correct for factor ??????????????????????????????????????????????? - - 2 * direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) - + 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - + direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + + (-omega/2) * 2 * ( + - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + #+ direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) + #- einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) + #- einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) #+ (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + #+ (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + einsum("ii,aa->ia", d_oo, d_vv) * omega # 1. order #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) @@ -1544,19 +1577,19 @@ def apply(ampl): - einsum("ij,ja->ia", i2, ampl.ph1) - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 - + (-omega/2) * (sqrt(2) - 0.5) * ( # correct for factor ??????????????????????????????????????????????? - - 2 * einsum("ib,ab->ia", ampl.ph1, qed_i1) - - 2 * einsum("ij,ja->ia", qed_i2, ampl.ph1) - + (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) + + (-omega/2) * 2 * ( + - einsum("ib,ab->ia", ampl.ph1, qed_i1) + - einsum("ij,ja->ia", qed_i2, ampl.ph1) + + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph1))) - + einsum("ib,ab->ia", ampl.ph1, qed_i1_0) - + einsum("ij,ja->ia", qed_i2_0, ampl.ph1) + #+ einsum("ib,ab->ia", ampl.ph1, qed_i1_0) + #+ einsum("ij,ja->ia", qed_i2_0, ampl.ph1) #+ (1/2) * (einsum("ka,jkib,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph1) #this can be done by symmetrize a,b # + einsum("kb,jkia,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph1)) #+ (1/2) * (einsum("ic,jabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph1) #this can be done by symmetrize i,j # + einsum("jc,iabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph1)) - + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) - + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph1) + #+ einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) + # + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph1) #+ (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #+ omega * einsum("ii,aa->ia", d_oo, d_vv) * ampl.gs1.as_float() #this (and following) is from the gs_ph contribution #- (omega / 2) * (sqrt(2) - 1) * (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) @@ -1565,8 +1598,8 @@ def apply(ampl): # + einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo))) # * ampl.gs1.as_float() + gs_part * ampl.gs1.as_float() - + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) # 1. order - - (1/2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) # 1. order + #+ (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) # 1. order + #- (1/2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) # 1. order + omega * ampl.ph1 # 1. order #+ (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution # - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) @@ -1586,8 +1619,8 @@ def apply(ampl): def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the terms here - if hasattr(hf, "qed_hf"): - raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + #if hasattr(hf, "qed_hf"): + # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 @@ -1604,21 +1637,21 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the term qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 - qed_i1_0 = intermediates.adc2_qed_i1_0 - qed_i2_0 = intermediates.adc2_qed_i2_0 + #qed_i1_0 = intermediates.adc2_qed_i1_0 + #qed_i2_0 = intermediates.adc2_qed_i2_0 gs_part = intermediates.adc2_qed_ph_ph_2_phot2_gs_part diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * (sqrt(3) - 0.5) * ( # correct for factor ??????????????????????????????????????????????? - - 2 * direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) - + 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - + direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) + + (-omega/2) * 3 * ( + - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + #+ direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) + #- einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) + #- einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) #+ (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + #+ (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 # 1. order )) @@ -1628,27 +1661,27 @@ def apply(ampl): - einsum("ij,ja->ia", i2, ampl.ph2) - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 - + (-omega/2) * (sqrt(3) - 0.5) * ( # correct for factor ??????????????????????????????????????????????? - - 2 * einsum("ib,ab->ia", ampl.ph2, qed_i1) - - 2 * einsum("ij,ja->ia", qed_i2, ampl.ph2) - + (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph2) + + (-omega/2) * 3 * ( + - einsum("ib,ab->ia", ampl.ph2, qed_i1) + - einsum("ij,ja->ia", qed_i2, ampl.ph2) + + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph2) + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph2))) - + einsum("ib,ab->ia", ampl.ph2, qed_i1_0) - + einsum("ij,ja->ia", qed_i2_0, ampl.ph2) - + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) - + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph2) + #+ einsum("ib,ab->ia", ampl.ph2, qed_i1_0) + #+ einsum("ij,ja->ia", qed_i2_0, ampl.ph2) + #+ einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) + # + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph2) #+ (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 + gs_part * ampl.gs2.as_float() - + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) # 1. order - - (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) # 1. order + #+ (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) # 1. order + #- (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) # 1. order + 2 * omega * ampl.ph2 # 1. order )) return AdcBlock(apply, diagonal) def block_ph_ph_2_couple_inner(hf, mp, intermediates): #one could cash some of the terms here - if hasattr(hf, "qed_hf"): - raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + #if hasattr(hf, "qed_hf"): + # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) gs_part = intermediates.adc2_qed_ph_ph_2_couple_inner_gs_part qed_i1 = intermediates.adc2_qed_couple_i1 @@ -1661,15 +1694,15 @@ def block_ph_ph_2_couple_inner(hf, mp, intermediates): #one could cash some of t diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): - return AmplitudeVector(ph=(0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), - direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph1) - - einsum("kb,ka,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph) - einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? - - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) - einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - + sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) - + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) - - 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 + return AmplitudeVector(ph=(#0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), + # direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph1) + # - einsum("kb,ka,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph) + # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? + # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) + # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? + + 2 * einsum("ib,ab->ia", ampl.ph1, qed_i1) + + 2 * einsum("ij,ja->ia", qed_i2, ampl.ph1) + #- 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed + sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed @@ -1702,12 +1735,12 @@ def apply(ampl): def block_ph_ph_2_phot_couple_inner(hf, mp, intermediates): #one could cash some of the terms here - if hasattr(hf, "qed_hf"): - raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") + #if hasattr(hf, "qed_hf"): + # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) gs_part = intermediates.adc2_qed_ph_ph_2_phot_couple_inner_gs_part - qed_i1 = intermediates.adc2_qed_phot_couple_i1 - qed_i2 = intermediates.adc2_qed_phot_couple_i2 + qed_i1 = intermediates.adc2_qed_couple_i1 + qed_i2 = intermediates.adc2_qed_couple_i2 #d_oo = zeros_like(hf.foo) #d_vv = zeros_like(hf.fvv) @@ -1717,18 +1750,18 @@ def block_ph_ph_2_phot_couple_inner(hf, mp, intermediates): #one could cash some diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): - return AmplitudeVector(ph=(0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), - direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph2) - - einsum("ka,kb,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph1) - einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2) # this is different from mp.diff_df, but why??? - - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) - einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2)) # this is different from mp.diff_df, but why??? - + einsum("ib,ab->ia", ampl.ph2, qed_i1) - + einsum("ij,ja->ia", qed_i2, ampl.ph2) - - 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 + return AmplitudeVector(ph=(#0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), + # direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph2) + # - einsum("ka,kb,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph1) + # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2) # this is different from mp.diff_df, but why??? + # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) + # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2)) # this is different from mp.diff_df, but why??? + + 2 * einsum("ib,ab->ia", ampl.ph2, qed_i1) + + 2 * einsum("ij,ja->ia", qed_i2, ampl.ph2) + #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) @@ -1766,7 +1799,7 @@ def apply(ampl): einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph - einsum("ka,kb,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - einsum("ic,jc,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - + einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph) * mp.qed_t1(b.ov) + #+ einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph) * mp.qed_t1(b.ov) + (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo))) * ampl.gs.as_float() # gs-part ) @@ -1783,7 +1816,7 @@ def apply(ampl): - einsum("kb,ka,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - einsum("jc,ic,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) #+ einsum("jb,ia,jb->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - + einsum("jb,jb->", mp.qed_t1(b.ov), ampl.ph2) * mp.qed_t1_df(b.ov) + #+ einsum("jb,jb->", mp.qed_t1(b.ov), ampl.ph2) * mp.qed_t1_df(b.ov) ) return AdcBlock(apply, diagonal) @@ -1913,15 +1946,15 @@ def adc2_qed_i2_0(hf, mp, intermediates): + einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov))) + einsum("kc,kjic->ij", mp.qed_t0(b.ov), hf.ooov)) -@register_as_intermediate -def adc2_qed_ph_ph_2_i1(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - return (omega / 2) * intermediates.adc2_qed_i1.evaluate() + intermediates.adc2_qed_i1_0.evaluate() +#@register_as_intermediate +#def adc2_qed_ph_ph_2_i1(hf, mp, intermediates): +# omega = float(ReferenceState.get_qed_omega(hf)) +# return (omega / 2) * intermediates.adc2_qed_i1.evaluate() + intermediates.adc2_qed_i1_0.evaluate() -@register_as_intermediate -def adc2_qed_ph_ph_2_i2(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - return (omega / 2) * intermediates.adc2_qed_i2.evaluate() + intermediates.adc2_qed_i2_0.evaluate() +#@register_as_intermediate +#def adc2_qed_ph_ph_2_i2(hf, mp, intermediates): +# omega = float(ReferenceState.get_qed_omega(hf)) +# return (omega / 2) * intermediates.adc2_qed_i2.evaluate() + intermediates.adc2_qed_i2_0.evaluate() @register_as_intermediate @@ -1933,56 +1966,60 @@ def adc2_qed_ph_ph_2_gs_part(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - return (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution - - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + return (#0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution + #- 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) + #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) + - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) @register_as_intermediate def adc2_qed_ph_ph_2_couple_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) return sqrt(omega / 2) * ( # gs_ph contribution - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) + omega * mp.qed_t1(b.ov) - - 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) - + 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) - - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #- 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) + #+ 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + #- 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)) + + einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) @register_as_intermediate def adc2_qed_ph_ph_2_couple_inner_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) - return sqrt(omega / 2) * ( # gs_ph contribution + return sqrt(omega) * ( # gs_ph contribution - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) + 2 * omega * mp.qed_t1(b.ov) - - 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) - + 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) - + sqrt(2) * ( - - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #- 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) + #+ 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) + #+ sqrt(2) * ( + #- 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)))) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)) + + einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) @@ -1990,17 +2027,18 @@ def adc2_qed_ph_ph_2_couple_inner_gs_part(hf, mp, intermediates): def adc2_qed_ph_ph_2_phot_couple_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) return sqrt(omega / 2) * ( #gs_ph part - - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) - + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) - - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + #- 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)) + - mp.qed_t1_df(b.ov)) # 1. order @register_as_intermediate @@ -2013,11 +2051,12 @@ def adc2_qed_ph_ph_2_phot_couple_inner_gs_part(hf, mp, intermediates): d_vv.set_mask("aa", 1.0) return sqrt(omega) * ( #gs_ph part - - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) - + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) - - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + #- 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) + #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)) + - mp.qed_t1_df(b.ov)) @register_as_intermediate @@ -2029,16 +2068,23 @@ def adc2_qed_ph_ph_2_phot_gs_part(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - return (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution - - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - + sqrt(2) * ( - - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - + (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) - - (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - ) - - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) - + 0.5 * omega * mp.qed_t0(b.ov)) + return (#0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution + #- 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + #+ sqrt(2) * ( + #- (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + #+ (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) + #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + #) + #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) + #+ 0.5 * omega * mp.qed_t0(b.ov)) + - (omega) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) + - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) @register_as_intermediate @@ -2050,46 +2096,53 @@ def adc2_qed_ph_ph_2_phot2_gs_part(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - return (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution - - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - + sqrt(3) * ( - - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - + (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) - - (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - ) - - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) - + omega * mp.qed_t0(b.ov)) + return (#0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution + #- 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) + #+ sqrt(3) * ( + #- (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + #+ (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) + #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + #) + #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) + #+ omega * mp.qed_t0(b.ov)) + - (omega/2) * 3 * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) + + (omega/2) * 3 * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) + #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) + - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) @register_as_intermediate def adc2_qed_couple_i1(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (0.5 * einsum("ka,kb->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + return ( sqrt(omega / 2) * (#0.5 * einsum("ka,kb->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + einsum("kc,kacb->ab", mp.qed_t1(b.ov), hf.ovvv))) @register_as_intermediate def adc2_qed_couple_i2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (0.5 * einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + return ( sqrt(omega / 2) * (#0.5 * einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + einsum("kc,kjic->ij", mp.qed_t1(b.ov), hf.ooov))) -@register_as_intermediate -def adc2_qed_phot_couple_i1(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (0.5 * einsum("kb,ka->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) - + einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) +#@register_as_intermediate +#def adc2_qed_phot_couple_i1(hf, mp, intermediates): +# omega = float(ReferenceState.get_qed_omega(hf)) +# return ( sqrt(omega / 2) * (#0.5 * einsum("kb,ka->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) +# + einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) -@register_as_intermediate -def adc2_qed_phot_couple_i2(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (0.5 * einsum("jc,ic->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) - + einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) +#@register_as_intermediate +#def adc2_qed_phot_couple_i2(hf, mp, intermediates): +# omega = float(ReferenceState.get_qed_omega(hf)) +# return ( sqrt(omega / 2) * (#0.5 * einsum("jc,ic->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) +# + einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) From 0c5642404fba9496db11100dbbd2d3a09b4ae2d8 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 16 Dec 2021 16:34:48 +0100 Subject: [PATCH 17/64] included check for unrestricted or restricted reference --- adcc/backends/psi4.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index 24237e9f..859c5c1d 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -121,8 +121,20 @@ def get_conv_tol(self): threshold = max(10 * conv_tol, conv_tol_grad) return threshold + #def get_restricted(self): + # return isinstance(self.wfn, (psi4.core.RHF, psi4.core.ROHF)) def get_restricted(self): - return isinstance(self.wfn, (psi4.core.RHF, psi4.core.ROHF)) + if isinstance(self.wfn, (psi4.core.RHF, psi4.core.ROHF)): + return True + elif isinstance(self.wfn, (psi4.core.Wavefunction)): + orben_a = np.asarray(self.wfn.epsilon_a()) + orben_b = np.asarray(self.wfn.epsilon_b()) + if all(orben_a == orben_b): + print("This is a restricted calculation") + return True + else: + print("This is an unrestricted calculation") + return False def get_energy_scf(self): return self.wfn.energy() From 77a96eab174a62deb4933fcddab2eec869be6c5c Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 17 Dec 2021 16:50:49 +0100 Subject: [PATCH 18/64] cashed some stuff in matrix.py --- adcc/adc_pp/matrix.py | 94 ++++++++++++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index fb8872dc..b0a505aa 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -1042,6 +1042,8 @@ def block_ph_gs_2(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) + ph_gs_inter = intermediates.qed_adc2_ph_gs_intermediate + if hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): def apply(ampl): return (einsum("jb,jb->", ( @@ -1052,8 +1054,9 @@ def apply(ampl): + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), + - ph_gs_inter), + #- 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + #- 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), ampl.ph)) else: @@ -1091,7 +1094,7 @@ def apply(ampl): #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) + #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph1)) return AdcBlock(apply, diagonal) @@ -1126,6 +1129,8 @@ def block_ph_gs_2_phot(hf, mp, intermediates): d_vv = zeros_like(hf.fvv) d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) + + ph_gs_inter = intermediates.qed_adc2_ph_gs_intermediate # 1. order diagonal = omega #- omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + omega def apply(ampl): @@ -1140,8 +1145,9 @@ def apply(ampl): #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) ) #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), + - ph_gs_inter), + #- 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + #- 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), ampl.ph1) + omega * ampl.gs1) # 1. order #(omega * einsum("jb,jb->", einsum("jj,bb->jb", d_oo, d_vv), ampl.ph1) @@ -1159,6 +1165,8 @@ def block_ph_gs_2_phot2(hf, mp, intermediates): d_vv = zeros_like(hf.fvv) d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) + + ph_gs_inter = intermediates.qed_adc2_ph_gs_intermediate # 1. order diagonal = 2 * omega #- omega * (sqrt(3) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + 2 * omega def apply(ampl): @@ -1173,8 +1181,9 @@ def apply(ampl): #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) ) #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), + - ph_gs_inter), + #- 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + #- 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), ampl.ph2) + 2 * omega * ampl.gs2) # 1. order return AdcBlock(apply, diagonal) @@ -1199,7 +1208,7 @@ def apply(ampl): #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * sqrt(2) * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) + #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph2) )#+ (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2)) # 1. order @@ -1267,10 +1276,12 @@ def block_ph_ph_2(hf, mp, intermediates): #d_oo.set_mask("ii", 1.0) #d_vv.set_mask("aa", 1.0) - term_t2_eri = ( - + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) - + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) - ).evaluate() + term_t2_eri = intermediates.term_t2_eri + + #term_t2_eri = ( + # + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + # + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) + #).evaluate() if hasattr(hf, "coupling"): omega = float(ReferenceState.get_qed_omega(hf)) @@ -1417,6 +1428,7 @@ def block_ph_ph_2_couple(hf, mp, intermediates): #one could cash some of the ter gs_part = intermediates.adc2_qed_ph_ph_2_couple_gs_part qed_i1 = intermediates.adc2_qed_couple_i1 qed_i2 = intermediates.adc2_qed_couple_i2 + #couple_inter = intermediates.qed_adc2_ph_couple_intermediate #d_oo = zeros_like(hf.foo) #d_vv = zeros_like(hf.fvv) @@ -1440,6 +1452,11 @@ def apply(ampl): #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) + #+ einsum("ijab,jb->ia", + #couple_inter, + #einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov) + #+ einsum("ic,jabc->ijab", mp.qed_t1(b.ov), hf.ovvv), + #ampl.ph)) + gs_part * ampl.gs.as_float() + sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) # 1. order @@ -1496,6 +1513,12 @@ def apply(ampl): #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + #+ einsum("ijab,jb->ia", + #einsum("kb,ikja->ijab", mp.qed_t1(b.ov), hf.ooov) + #+ einsum("jc,ibac->ijab", mp.qed_t1(b.ov), hf.ovvv), + #ampl.ph1)) + #+ einsum("ijab,ia->jb", einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov), ampl.ph1) + #+ einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part + gs_part * ampl.gs1.as_float() + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order @@ -1533,10 +1556,12 @@ def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms #qed_i1 = intermediates.adc2_qed_i1 #qed_i2 = intermediates.adc2_qed_i2 - term_t2_eri = ( - + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) - + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) - ).evaluate() + term_t2_eri = intermediates.term_t2_eri + + #term_t2_eri = ( + # + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + # + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) + #).evaluate() d_oo = zeros_like(hf.foo) d_vv = zeros_like(hf.fvv) @@ -1625,10 +1650,12 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the term i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 - term_t2_eri = ( - + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) - + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) - ).evaluate() + term_t2_eri = intermediates.term_t2_eri + + #term_t2_eri = ( + # + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + # + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) + #).evaluate() d_oo = zeros_like(hf.foo) d_vv = zeros_like(hf.fvv) @@ -1956,6 +1983,25 @@ def adc2_qed_i2_0(hf, mp, intermediates): # omega = float(ReferenceState.get_qed_omega(hf)) # return (omega / 2) * intermediates.adc2_qed_i2.evaluate() + intermediates.adc2_qed_i2_0.evaluate() +@register_as_intermediate +def qed_adc2_ph_gs_intermediate(hf, mp, intermediates): + return (0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + + 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) + +@register_as_intermediate +def term_t2_eri(hf, mp, intermediates): + return (einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo)) + #).evaluate() + +#@register_as_intermediate +#def qed_adc2_ph_couple_intermediate(hf, mp, intermediates): +# return (einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov) +# + einsum("ic,jabc->ijab", mp.qed_t1(b.ov), hf.ovvv)) + +#@register_as_intermediate +#def qed_adc2_ph_phot_couple_intermediate(hf, mp, intermediates): +# return "blub" @register_as_intermediate def adc2_qed_ph_ph_2_gs_part(hf, mp, intermediates): @@ -1995,8 +2041,8 @@ def adc2_qed_ph_ph_2_couple_gs_part(hf, mp, intermediates): #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)) - + einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) @register_as_intermediate @@ -2018,8 +2064,8 @@ def adc2_qed_ph_ph_2_couple_inner_gs_part(hf, mp, intermediates): #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)) - + einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) From 8179a2b62d3a261e0a859119ffe7fea83b44e728 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 17 Jan 2022 16:33:06 +0100 Subject: [PATCH 19/64] corrected QED-ADC(2) from QED-HF reference (should be correct now and ready for testing) --- adcc/adc_pp/matrix.py | 108 +++++++++++++++++++++++------------------- adcc/workflow.py | 6 +-- 2 files changed, 63 insertions(+), 51 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index b0a505aa..dd8a30f4 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -1045,7 +1045,7 @@ def block_ph_gs_2(hf, mp, intermediates): ph_gs_inter = intermediates.qed_adc2_ph_gs_intermediate if hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): - def apply(ampl): + def apply(ampl): # pretty sure this should be zero return (einsum("jb,jb->", ( #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) @@ -1073,7 +1073,7 @@ def apply(ampl): - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), ampl.ph)) - return AdcBlock(apply, 0) + return AdcBlock(lambda ampl: 0, 0) #AdcBlock(apply, 0) def block_ph_gs_2_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -1084,7 +1084,7 @@ def block_ph_gs_2_phot_couple(hf, mp, intermediates): #d_vv.set_mask("aa", 1.0) diagonal = 0#- sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) - def apply(ampl): + def apply(ampl): # pretty sure this should be zero return ( sqrt(omega / 2) * einsum("jb,jb->", ( - einsum("jckb,kc->jb", hf.ovov, mp.qed_t1(b.ov)) + omega * mp.qed_t1(b.ov) @@ -1097,7 +1097,7 @@ def apply(ampl): #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph1)) - return AdcBlock(apply, diagonal) + return AdcBlock(lambda ampl: 0, 0)#AdcBlock(apply, diagonal) @@ -1134,21 +1134,21 @@ def block_ph_gs_2_phot(hf, mp, intermediates): # 1. order diagonal = omega #- omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + omega def apply(ampl): - return (einsum("jb,jb->", ( + return (#(einsum("jb,jb->", ( #+ 0.5 * omega * mp.qed_t0(b.ov) #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - + 2 * ( - - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + # + 2 * ( + # - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + # + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - ) + # ) #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - - ph_gs_inter), + # - ph_gs_inter), #- 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) #- 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), - ampl.ph1) + # ampl.ph1) + omega * ampl.gs1) # 1. order #(omega * einsum("jb,jb->", einsum("jj,bb->jb", d_oo, d_vv), ampl.ph1) #- (omega / 2) * (sqrt(2) - 1) * einsum("jb,jb->", (einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) @@ -1170,21 +1170,21 @@ def block_ph_gs_2_phot2(hf, mp, intermediates): # 1. order diagonal = 2 * omega #- omega * (sqrt(3) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + 2 * omega def apply(ampl): - return (einsum("jb,jb->", ( - + omega * mp.qed_t0(b.ov) + return (#einsum("jb,jb->", ( + # + omega * mp.qed_t0(b.ov) #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - + 3 * ( - - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) + # + 3 * ( + # - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) + # + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - ) + # ) #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - - ph_gs_inter), + # - ph_gs_inter), #- 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) #- 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), - ampl.ph2) + # ampl.ph2) + 2 * omega * ampl.gs2) # 1. order return AdcBlock(apply, diagonal) @@ -1212,7 +1212,7 @@ def apply(ampl): + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph2) )#+ (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2)) # 1. order - return AdcBlock(apply, diagonal) + return AdcBlock(lambda ampl: 0, diagonal) @@ -1247,14 +1247,14 @@ def apply(ampl): return (- 0.5 * omega * sqrt(2) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv), ampl.ph2) - einsum("kb,jk,jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo), ampl.ph2))) - return AdcBlock(apply, diagonal) + return AdcBlock(lambda ampl: 0, 0) def block_ph_gs_2_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) - return AdcBlock(lambda ampl: 0, diagonal) + return AdcBlock(lambda ampl: 0, 0) @@ -1293,7 +1293,7 @@ def block_ph_ph_2(hf, mp, intermediates): #qed_i2 = intermediates.adc2_qed_i2 #qed_i1_0 = intermediates.adc2_qed_i1_0 #qed_i2_0 = intermediates.adc2_qed_i2_0 - qed_gs_part = intermediates.adc2_qed_ph_ph_2_gs_part + #qed_gs_part = intermediates.adc2_qed_ph_ph_2_gs_part if hasattr(hf, "qed_hf"): qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 @@ -1425,7 +1425,7 @@ def block_ph_ph_2_couple(hf, mp, intermediates): #one could cash some of the ter #if hasattr(hf, "qed_hf"): # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) - gs_part = intermediates.adc2_qed_ph_ph_2_couple_gs_part + #gs_part = intermediates.adc2_qed_ph_ph_2_couple_gs_part qed_i1 = intermediates.adc2_qed_couple_i1 qed_i2 = intermediates.adc2_qed_couple_i2 #couple_inter = intermediates.qed_adc2_ph_couple_intermediate @@ -1443,12 +1443,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph) # this is different from mp.diff_df, but why??? # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph)) # this is different from mp.diff_df, but why??? - + einsum("ib,ab->ia", ampl.ph, qed_i1) - + einsum("ij,ja->ia", qed_i2, ampl.ph) + - einsum("ib,ab->ia", ampl.ph, qed_i1) + - einsum("ij,ja->ia", qed_i2, ampl.ph) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - + sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + - sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) @@ -1457,7 +1457,7 @@ def apply(ampl): #einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov) #+ einsum("ic,jabc->ijab", mp.qed_t1(b.ov), hf.ovvv), #ampl.ph)) - + gs_part * ampl.gs.as_float() + #+ gs_part * ampl.gs.as_float() + sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) # 1. order #+ sqrt(omega / 2) * ( # gs_ph contribution @@ -1487,8 +1487,8 @@ def block_ph_ph_2_phot_couple(hf, mp, intermediates): #one could cash some of th # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) gs_part = intermediates.adc2_qed_ph_ph_2_phot_couple_gs_part - qed_i1 = intermediates.adc2_qed_couple_i1 - qed_i2 = intermediates.adc2_qed_couple_i2 + qed_i1 = intermediates.adc2_qed_phot_couple_i1 + qed_i2 = intermediates.adc2_qed_phot_couple_i2 #d_oo = zeros_like(hf.foo) #d_vv = zeros_like(hf.fvv) @@ -1504,12 +1504,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - + einsum("ib,ab->ia", ampl.ph1, qed_i1) - + einsum("ij,ja->ia", qed_i2, ampl.ph1) + - einsum("ib,ab->ia", ampl.ph1, qed_i1) + - einsum("ij,ja->ia", qed_i2, ampl.ph1) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + - sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) @@ -1622,7 +1622,7 @@ def apply(ampl): # - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) # + einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo))) # * ampl.gs1.as_float() - + gs_part * ampl.gs1.as_float() + #+ gs_part * ampl.gs1.as_float() #+ (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) # 1. order #- (1/2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) # 1. order + omega * ampl.ph1 # 1. order @@ -1698,7 +1698,7 @@ def apply(ampl): #+ einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) # + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph2) #+ (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 - + gs_part * ampl.gs2.as_float() + #+ gs_part * ampl.gs2.as_float() #+ (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) # 1. order #- (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) # 1. order + 2 * omega * ampl.ph2 # 1. order @@ -1727,16 +1727,16 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - + 2 * einsum("ib,ab->ia", ampl.ph1, qed_i1) - + 2 * einsum("ij,ja->ia", qed_i2, ampl.ph1) + - sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + - sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) #- 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - + sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + - sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) - + gs_part * ampl.gs1.as_float() + #+ gs_part * ampl.gs1.as_float() #+ (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) #- (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( # (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph1 @@ -1766,8 +1766,8 @@ def block_ph_ph_2_phot_couple_inner(hf, mp, intermediates): #one could cash some # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) gs_part = intermediates.adc2_qed_ph_ph_2_phot_couple_inner_gs_part - qed_i1 = intermediates.adc2_qed_couple_i1 - qed_i2 = intermediates.adc2_qed_couple_i2 + qed_i1 = intermediates.adc2_qed_phot_couple_i1 + qed_i2 = intermediates.adc2_qed_phot_couple_i2 #d_oo = zeros_like(hf.foo) #d_vv = zeros_like(hf.fvv) @@ -1783,12 +1783,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2) # this is different from mp.diff_df, but why??? # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2)) # this is different from mp.diff_df, but why??? - + 2 * einsum("ib,ab->ia", ampl.ph2, qed_i1) - + 2 * einsum("ij,ja->ia", qed_i2, ampl.ph2) + - sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) + - sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + - sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) @@ -1827,8 +1827,8 @@ def apply(ampl): - einsum("ka,kb,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - einsum("ic,jc,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) #+ einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph) * mp.qed_t1(b.ov) - + (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo))) * ampl.gs.as_float() # gs-part + #+ (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) + # - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo))) * ampl.gs.as_float() # gs-part ) return AdcBlock(apply, diagonal) @@ -1945,14 +1945,14 @@ def adc2_i2(hf, mp, intermediates): # qed intermediates for adc2, without the factor of (omega/2), which is added in the actual matrix builder @register_as_intermediate -def adc2_qed_i1(hf, mp, intermediates): +def adc2_qed_i1(hf, mp, intermediates): # maybe do this with symmetrise #return (1/2) * einsum("kb,ka->ab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) return (1/2) * (einsum("kb,ka->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + einsum("ka,kb->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) @register_as_intermediate -def adc2_qed_i2(hf, mp, intermediates): +def adc2_qed_i2(hf, mp, intermediates): # maybe do this with symmetrise #return (1/2) * einsum("jc,ic->ij", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) return (1/2) * (einsum("jc,ic->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + einsum("ic,jc->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) @@ -2175,6 +2175,18 @@ def adc2_qed_couple_i2(hf, mp, intermediates): + einsum("kc,kjic->ij", mp.qed_t1(b.ov), hf.ooov))) +@register_as_intermediate +def adc2_qed_phot_couple_i1(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + return ( sqrt(omega / 2) * (#0.5 * einsum("ka,kb->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + + einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) + + +@register_as_intermediate +def adc2_qed_phot_couple_i2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + return ( sqrt(omega / 2) * (#0.5 * einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + + einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) #@register_as_intermediate diff --git a/adcc/workflow.py b/adcc/workflow.py index 27c96332..d612cea7 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -501,7 +501,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= guess_gs2 = np.zeros(n_guess) # these guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a # decoupled state. For gs2 this is a little less important, because the coupling is weaker. - #guess_gs[n_guess - 3] = 100 + guess_gs[n_guess - 3] = 0 guess_gs1[n_guess - 2] = 2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, guess_gs2[n_guess - 1] = 5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. guesses_tmp = [] @@ -516,7 +516,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= for guess_index in np.arange(n_guess): # build QED_AmplitudeVectors from AmplitudeVector guesses #if hasattr(guesses_elec[0], "pphh"): if contains_doubles: - guess_gs[n_guess - 3] = 100 + #guess_gs[n_guess - 3] = 1e+8 #print("doubles guesses are set up") # what if this is not ok without restricting singlets/triplets only, because e.g. phot could be singlet and elec triplet ... doesnt seem to matter #guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, @@ -526,7 +526,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) else: - guess_gs[n_guess - 3] = 0#10**7 + #guess_gs[n_guess - 3] = 1e+7 #print("singles guesses are set up") guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, None, guess_gs1[guess_index], guesses_phot[guess_index].ph, None, From 2d028d56f6dc8db58092a2b8995f2722dd5fa582 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 3 Feb 2022 11:06:10 +0100 Subject: [PATCH 20/64] removed .gs from the implementation (since it is always zero) and implemented the full_diagonalization option, where a full QED guess, (3 * adc_matrix + 2) guesses, is build. Care that at the moment the H_1 two particle part of the phot/elec ph_ph coupling blocks has been commented out, since this results in the matrix, that can be tested with the dipole property only. --- adcc/AdcMatrix.py | 23 ++++--- adcc/AmplitudeVector.py | 47 ++++++------- adcc/adc_pp/matrix.py | 24 +++---- adcc/adc_pp/transition_dm.py | 3 +- adcc/functions.py | 8 +-- adcc/solver/davidson.py | 53 ++++++++++++++- adcc/workflow.py | 124 ++++++++++++++++++++++++++++++++--- 7 files changed, 220 insertions(+), 62 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 5dffff04..ec539c50 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -279,7 +279,7 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): self.blocks_ph_1_temp[key + "_phot2"] = self.blocks_ph_22[key] self.blocks_ph = {**self.blocks_ph_00, **self.blocks_ph_1_temp} - self.__diagonal_gs = 0 #sum(self.blocks_ph[block].diagonal for block in self.blocks_ph + #self.__diagonal_gs = 0 #sum(self.blocks_ph[block].diagonal for block in self.blocks_ph # if "ph_gs" in block and not block.endswith("phot")) # coupling gs_gs blocks have diagonal = 0 self.__diagonal_gs1 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph if "ph_gs" in block and block.endswith("phot")) @@ -303,12 +303,12 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): #self.__diagonal_gs2 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph # if "ph_gs" in block and block.endswith("phot2")) - self.__diagonal = QED_AmplitudeVector(self.__diagonal_gs, self.elec.diagonal().ph, self.elec.diagonal().pphh, + self.__diagonal = QED_AmplitudeVector(self.elec.diagonal().ph, self.elec.diagonal().pphh, self.__diagonal_gs1, self.phot.diagonal().ph, self.phot.diagonal().pphh, self.__diagonal_gs2, self.phot2.diagonal().ph, self.phot2.diagonal().pphh) else: #print("from adcmatrix init diagonal has no pphh part!!!!!!!!!!!!!!!!!!!") - self.__diagonal = QED_AmplitudeVector(self.__diagonal_gs, self.elec.diagonal().ph, None, + self.__diagonal = QED_AmplitudeVector(self.elec.diagonal().ph, None, self.__diagonal_gs1, self.phot.diagonal().ph, None, self.__diagonal_gs2, self.phot2.diagonal().ph, None) self.__init_space_data_qed(self.elec.diagonal()) # here we need to adapt the attributes, defined via this function @@ -357,7 +357,7 @@ def __init_space_data_qed(self, diagonal0): # self.shape = ((shape0[0]+1) * 3, (shape0[0]+1) * 3) #else: # self.shape = ((shape0[0]+1) * 2, (shape0[0]+1) * 2) - self.shape = ((shape0[0]+1) * 3, (shape0[0]+1) * 3) + self.shape = ((shape0[0]+1) * 3 - 1, (shape0[0]+1) * 3 - 1) self.axis_spaces["gs"] = ["o0", "v0"] self.axis_lengths["gs"] = 1 # axis_spaces and axis_lengths both are dicts, referring to "ph", "pphh" as a key, so either make extra blocks here, or probably better @@ -510,7 +510,7 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) phot2_part = self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) - gs_part = 0 + #gs_part = 0 gs1_part = 0 gs2_part = 0 @@ -540,7 +540,8 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude gs2_part += self.blocks_ph[block].apply(v) elif block.endswith("phot_couple_edge"): #print("phot_couple_edge") - gs_part += self.blocks_ph[block].apply(v) + #gs_part += self.blocks_ph[block].apply(v) + pass elif block.endswith("phot_couple_inner"): #print("phot_couple_inner") gs1_part += self.blocks_ph[block].apply(v) @@ -552,7 +553,8 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude gs2_part += self.blocks_ph[block].apply(v) elif block.endswith("phot_couple"): #print("phot_couple") - gs_part += self.blocks_ph[block].apply(v) + #gs_part += self.blocks_ph[block].apply(v) + pass elif block.endswith("phot"): #print("phot") gs1_part += self.blocks_ph[block].apply(v) @@ -561,7 +563,8 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude gs1_part += self.blocks_ph[block].apply(v) else: # elec #print("elec") - gs_part += self.blocks_ph[block].apply(v) + #gs_part += self.blocks_ph[block].apply(v) + pass """ elif "gs" in block and block.startswith("gs"): if "phot_couple" in block: @@ -671,10 +674,10 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude """ #print(res.gs, res.elec, res.gs1, res.phot) if "pphh" in elec_part.blocks_ph: - return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, + return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) + return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index 5d1b834a..233e5af3 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -230,10 +230,10 @@ def __radd__(self, other): class QED_AmplitudeVector: # it seems all operations, without further specification of pphh part, are unused and can be omitted #def __init__(self, gs=None, elec=None, gs1=None, phot=None): - def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, gs2=None, ph2=None, pphh2=None): # also write this class with *args, **kwargs + def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, gs2=None, ph2=None, pphh2=None): # also write this class with *args, **kwargs # maybe do this via a list e.g. ph=[ph, ph1, ph2] - self.gs = gs_vec(gs) + #self.gs = gs_vec(0)#gs) self.gs1 = gs_vec(gs1) self.gs2 = gs_vec(gs2) @@ -298,11 +298,11 @@ def __init__(self, gs=None, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, def dot(self, invec): def dot_(self, invec): if "pphh" in self.elec.blocks_ph: - return (self.gs * invec.gs + self.elec.ph.dot(invec.elec.ph) + self.elec.pphh.dot(invec.elec.pphh) + return (self.elec.ph.dot(invec.elec.ph) + self.elec.pphh.dot(invec.elec.pphh) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + self.phot.pphh.dot(invec.phot.pphh) + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) + self.phot2.pphh.dot(invec.phot2.pphh)) else: - return (self.gs * invec.gs + self.elec.ph.dot(invec.elec.ph) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + return (self.elec.ph.dot(invec.elec.ph) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) ) if isinstance(invec, list): list_temp = [] # to return a np.array with different dimensions (gs,ph,pphh), we have to fill them into a list and then convert the list ??? @@ -342,22 +342,23 @@ def __matmul__(self, other): def __sub__(self, invec): if isinstance(invec, QED_AmplitudeVector): - return QED_AmplitudeVector(self.gs - invec.gs, self.elec.__sub__(invec.elec), self.gs1 - invec.gs1, self.phot.__sub__(invec.phot)) + return QED_AmplitudeVector(self.elec.__sub__(invec.elec), self.gs1 - invec.gs1, self.phot.__sub__(invec.phot), + self.gs2 - invec.gs2, self.phot2.__sub__(invec.phot2)) elif isinstance(invec, (float, int)): # for diagonal - shift in preconditioner.py # this results in a scalar in block pphh, if pphh is originally None if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(gs=self.gs - invec, ph=self.elec.ph.__sub__(invec), pphh=self.elec.pphh.__sub__(invec), + return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), pphh=self.elec.pphh.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), pphh1=self.phot.pphh.__sub__(invec), gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec), pphh2=self.phot2.pphh.__sub__(invec)) else: - return QED_AmplitudeVector(gs=self.gs - invec, ph=self.elec.ph.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), + return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec)) def __mul__(self, scalar): - return QED_AmplitudeVector(scalar * self.gs, self.elec.__mul__(scalar), scalar * self.gs1, self.phot.__mul__(scalar)) + return QED_AmplitudeVector(self.elec.__mul__(scalar), scalar * self.gs1, self.phot.__mul__(scalar)) def __rmul__(self, scalar): - return QED_AmplitudeVector(scalar * self.gs, self.elec.__rmul__(scalar), scalar * self.gs1, self.phot.__rmul__(scalar)) + return QED_AmplitudeVector(self.elec.__rmul__(scalar), scalar * self.gs1, self.phot.__rmul__(scalar)) def __truediv__(self, other): #print(type(self.elec), self.elec.ph) @@ -368,42 +369,42 @@ def __truediv__(self, other): #else: if isinstance(other, QED_AmplitudeVector): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(gs=self.gs / other.gs, ph=self.elec.ph.__truediv__(other.elec.ph), pphh=self.elec.pphh.__truediv__(other.elec.pphh), + return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other.elec.ph), pphh=self.elec.pphh.__truediv__(other.elec.pphh), gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), pphh1=self.phot.pphh.__truediv__(other.phot.pphh), gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph), pphh2=self.phot2.pphh.__truediv__(other.phot2.pphh)) else: - return QED_AmplitudeVector(gs=self.gs / other.gs, ph=self.elec.ph.__truediv__(other.elec.ph), + return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other.elec.ph), gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph)) elif isinstance(other, (float, int)): #return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), # gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other)) if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(gs=self.gs / other, ph=self.elec.ph.__truediv__(other), pphh=self.elec.pphh.__truediv__(other), + return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other), pphh=self.elec.pphh.__truediv__(other), gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), pphh1=self.phot.pphh.__truediv__(other), gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other), pphh2=self.phot2.pphh.__truediv__(other)) else: - return QED_AmplitudeVector(gs=self.gs / other, ph=self.elec.ph.__truediv__(other), + return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other), gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) def zeros_like(self): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(0, self.elec.zeros_like(), 0, self.phot.zeros_like(), 0, self.phot2.zeros_like()) + return QED_AmplitudeVector(self.elec.zeros_like(), gs_vec(0), self.phot.zeros_like(), gs_vec(0), self.phot2.zeros_like()) else: - return QED_AmplitudeVector(0, self.elec.zeros_like(), 0, self.phot.zeros_like()) + return QED_AmplitudeVector(self.elec.zeros_like(), 0, self.phot.zeros_like()) def empty_like(self): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector([], self.elec.empty_like(), [], self.phot.empty_like(), [], self.phot2.empty_like()) + return QED_AmplitudeVector(self.elec.empty_like(), [], self.phot.empty_like(), [], self.phot2.empty_like()) else: - return QED_AmplitudeVector([], self.elec.empty_like(), [], self.phot.empty_like()) + return QED_AmplitudeVector(self.elec.empty_like(), [], self.phot.empty_like()) def copy(self): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(self.gs, self.elec.copy(), self.gs1, self.phot.copy(), self.gs2, self.phot2.copy()) - else: - return QED_AmplitudeVector(self.gs, self.elec.copy(), self.gs1, self.phot.copy()) + #if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(self.elec.copy(), gs_vec(self.gs1.as_float()), self.phot.copy(), gs_vec(self.gs2.as_float()), self.phot2.copy()) + #else: + # return QED_AmplitudeVector(self.elec.copy(), self.gs1, self.phot.copy(), self.gs2, self.phot2.copy()) def evaluate(self): #print(self.elec0, self.elec, self.phot0, self.phot) @@ -411,7 +412,7 @@ def evaluate(self): self.elec.evaluate() self.phot.evaluate() self.phot2.evaluate() - self.gs.as_float() + #self.gs.as_float() self.gs1.as_float() self.gs2.as_float() #try: @@ -466,7 +467,7 @@ def __add__(self, scalar): else: #print("uses add gs_vec", self.val, scalar) #print(type(self.val)) - return self.val + scalar + return gs_vec(self.val + scalar) def __radd__(self, scalar): return self.__add__(scalar) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index dd8a30f4..f5331eb0 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -1448,10 +1448,10 @@ def apply(ampl): #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - - sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + #- sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed - + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) - + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) + #+ einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # electric H_1 term + #+ einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) # electric H_1 term #+ einsum("ijab,jb->ia", #couple_inter, #einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov) @@ -1509,10 +1509,10 @@ def apply(ampl): #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - - sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + #- sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) - + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) - + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + #+ einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + #+ einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term #+ einsum("ijab,jb->ia", #einsum("kb,ikja->ijab", mp.qed_t1(b.ov), hf.ooov) #+ einsum("jc,ibac->ijab", mp.qed_t1(b.ov), hf.ovvv), @@ -1732,10 +1732,10 @@ def apply(ampl): #- 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - - sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + #- sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed - + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) - + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + #+ einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + #+ einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term #+ gs_part * ampl.gs1.as_float() #+ (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) #- (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( @@ -1788,10 +1788,10 @@ def apply(ampl): #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - - sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + #- sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) - + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) - + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) + #+ einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term + #+ einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part + gs_part * ampl.gs2.as_float() #+ sqrt(omega / 2) * ( #gs_ph part diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index 6c56b386..52c6cb2b 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -151,8 +151,7 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): print("norm squared of amplitude.elec", ampl_elec_norm, " norm squared phot", ampl_phot_norm) print("norm squared of amplitude", amplitude @ amplitude) print("beware, that we normalize .elec, before giving it to the oscillator strength routine") - print("amplitude.gs is {}, amplitude.gs1 is {} and amplitude.gs2 is {}".format(amplitude.gs.as_float(), - amplitude.gs1.as_float(), amplitude.gs2.as_float())) + print("amplitude.gs1 is {} and amplitude.gs2 is {}".format(amplitude.gs1.as_float(), amplitude.gs2.as_float())) normalized_ampl = amplitude.elec / sqrt(ampl_elec_norm) ret = DISPATCH[method.name](ground_state, normalized_ampl, intermediates) else: diff --git a/adcc/functions.py b/adcc/functions.py index a7b9fde0..d6c474b4 100644 --- a/adcc/functions.py +++ b/adcc/functions.py @@ -128,14 +128,14 @@ def lincomb(coefficients, tensors, evaluate=False): # give .elec and .phot to lin_comb_strict via AmplitudeVector instance # then coeff[0] * .gs[from first tensor/vector] + coeff[1] * .gs[from second tensor/vector] + ... # same for .gs1 - gs_part = 0 + #gs_part = 0 gs1_part = 0 gs2_part = 0 phot2_list = [] elec_list = [] phot_list = [] for coeff_ind, ten in enumerate(tensors): - gs_part += coefficients[coeff_ind] * ten.gs + #gs_part += coefficients[coeff_ind] * ten.gs gs1_part += coefficients[coeff_ind] * ten.gs1 gs2_part += coefficients[coeff_ind] * ten.gs2 elec_list.append(ten.elec) @@ -151,10 +151,10 @@ def lincomb(coefficients, tensors, evaluate=False): # gs2_part += coefficients[coeff_ind] * ten.gs2 # phot2_list.append(ten.phot2) # phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) - return QED_AmplitudeVector(gs_part, elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, + return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(gs_part, elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) + return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) #print(qed_vec.ph) #print(coefficients) #print(tensors[0][block]) diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 9e620cba..563c28eb 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -22,6 +22,7 @@ ## --------------------------------------------------------------------- import sys import warnings +from attr import has import numpy as np from numpy.lib.function_base import blackman import scipy.linalg as la @@ -167,6 +168,46 @@ def callback(state, identifier): assert len(SS) >= n_block assert len(SS) <= max_subspace + """ + if len(SS) == max_subspace: + print("subspace reached full matrix dimension -> starting to diagonalize full matrix from already obtained SS") + # just copy paste of some of the davidson functions + converged = False + while not converged: + Ass = Ass_cont[:n_ss_vec, :n_ss_vec] # Increase the work view size + for i in range(n_block): + #print(type(Ax[-n_block + i]), Ax[-n_block + i]) + #print(type(SS), SS) + Ass[:, -n_block + i] = Ax[-n_block + i] @ SS + Ass[-n_block:, :] = np.transpose(Ass[:, -n_block:]) + + rvals, rvecs = la.eigh(Ass) + + def form_residual(rval, rvec): + coefficients = np.hstack((rvec, -rval * rvec)) + return lincomb(coefficients, Ax + SS, evaluate=True) + + residuals = [form_residual(rvals[i], v) + for i, v in enumerate(np.transpose(rvecs))] + residual_norms = np.array([r @ r for r in residuals]) + + residual_converged = residual_norms < 1e-9 # same as for davidson + converged = np.all(residual_converged) + + if converged: + print("converged eigenvalues = ", rvals) + break + else: + print("intermediate eigenvalues = ", rvals) + #for i in np.arange(max_subspace): + #print("rvec ", rvecs[i]) + #print("residual", residuals[i]) + new_guesses_tmp = [rvecs[i] - residuals[i] for i in np.arange(max_subspace)] + new_guesses = [vec / np.sqrt(vec @ vec) for vec in new_guesses_tmp] + Ax = evaluate(matrix @ new_guesses) + """ + + # Project A onto the subspace, keeping in mind # that the values Ass[:-n_block, :-n_block] are already valid, @@ -183,11 +224,18 @@ def callback(state, identifier): # and the associated ritz vector as well as residual with state.timer.record("rayleigh_ritz"): if Ass.shape == (n_block, n_block): + #print("davidson eigh", la.eigh(Ass)[0]) rvals, rvecs = la.eigh(Ass) # Do a full diagonalisation + print("davidson eigh", rvals) + eigenvecs = [lincomb(v, SS, evaluate=True) + for v in np.transpose(rvecs)] + for eigv in eigenvecs: + print("norm of eigenvector", np.sqrt(eigv @ eigv)) else: # TODO Maybe play with precision a little here # TODO Maybe use previous vectors somehow v0 = None + print("davidson eigh", la.eigh(Ass)[0]) rvals, rvecs = sla.eigsh(Ass, k=n_block, which=which, v0=v0) with state.timer.record("residuals"): @@ -447,7 +495,10 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, # max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) # original print("for qed-adc we use double the standard max_subspace, due to convergence problems," "if the doubly excited photonic space contributes to the states") - max_subspace = max(12 * n_ep, 20, 10 * len(guesses)) + if hasattr(matrix.reference_state, "full_diagonalization"): + max_subspace = len(guesses) + else: + max_subspace = max(12 * n_ep, 20, 10 * len(guesses)) def convergence_test(state): state.residuals_converged = state.residual_norms < conv_tol diff --git a/adcc/workflow.py b/adcc/workflow.py index d612cea7..dff5a29b 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -20,6 +20,7 @@ ## along with adcc. If not, see . ## ## --------------------------------------------------------------------- +import enum import sys import warnings import numpy as np @@ -39,7 +40,7 @@ from .solver.davidson import jacobi_davidson from .solver.explicit_symmetrisation import (IndexSpinSymmetrisation, IndexSymmetrisation) -from .AmplitudeVector import QED_AmplitudeVector +from .AmplitudeVector import QED_AmplitudeVector, gs_vec __all__ = ["run_adc"] @@ -490,20 +491,22 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) n_guess = len(guesses_elec) for i in np.arange(n_guess): - guesses_phot[i] *= 0.02 - guesses_phot2[i] *= 0.001 + guesses_phot[i] *= 1#0.02 + guesses_phot2[i] *= 1#0.001 #print("this is from obtain_guess_qed from workflow: guesses_elec[0].pphh", guesses_elec[0].pphh) if n_guess != len(guesses_phot): raise InputError("amount of guesses for electronic and photonic must be equal, but are" "{} electronic and {} photonic guesses".format(len(guesses_elec), len(guesses_phot))) - guess_gs = np.zeros(n_guess) + #guess_gs = np.zeros(n_guess) guess_gs1 = np.zeros(n_guess) guess_gs2 = np.zeros(n_guess) # these guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a # decoupled state. For gs2 this is a little less important, because the coupling is weaker. - guess_gs[n_guess - 3] = 0 + #guess_gs[n_guess - 3] = 0 + """ guess_gs1[n_guess - 2] = 2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, guess_gs2[n_guess - 1] = 5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. + """ guesses_tmp = [] try: dummy_var = guesses_elec[0].pphh @@ -516,22 +519,123 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= for guess_index in np.arange(n_guess): # build QED_AmplitudeVectors from AmplitudeVector guesses #if hasattr(guesses_elec[0], "pphh"): if contains_doubles: - #guess_gs[n_guess - 3] = 1e+8 + #guess_gs[n_guess - 3] = 1e+12 #8 #print("doubles guesses are set up") # what if this is not ok without restricting singlets/triplets only, because e.g. phot could be singlet and elec triplet ... doesnt seem to matter #guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, # guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, # guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) - guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, + guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) else: #guess_gs[n_guess - 3] = 1e+7 #print("singles guesses are set up") - guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, None, + guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, None, guess_gs1[guess_index], guesses_phot[guess_index].ph, None, guess_gs2[guess_index], guesses_phot2[guess_index].ph, None)) + + if hasattr(matrix.reference_state, "full_diagonalization"): + full_guess = [] + for qed_vec in guesses_tmp: + tmp_vec = qed_vec.copy() + tmp_elec_vec = qed_vec.elec.copy() + tmp_phot_vec = qed_vec.phot.zeros_like() + tmp_phot2_vec = qed_vec.phot2.zeros_like() + #print("elec part", type(qed_vec.elec.ph)) + #print("phot part", type(qed_vec.phot.ph)) + #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) + #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) + #tmp_elec_vec *= 1 + #tmp_elec_vec.ph *= 5 + #tmp_elec_vec.pphh *= 5 + vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, + 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + full_guess.append(vec) + for qed_vec in guesses_tmp: + tmp_elec_vec = qed_vec.elec.zeros_like() + tmp_phot_vec = qed_vec.phot.copy() + tmp_phot2_vec = qed_vec.phot2.zeros_like() + #print("elec part", type(qed_vec.elec.ph)) + #print("phot part", type(qed_vec.phot.ph)) + #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) + #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) + tmp_phot_vec *= 1e+10 + #tmp_elec_vec.ph *= 5 + #tmp_elec_vec.pphh *= 5 + vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + full_guess.append(vec) + #tmp_vec = qed_vec.copy() + #print(type(tmp_vec.phot.ph)) + #tmp_vec.phot.ph *= 5 + #tmp_vec.phot.pphh *= 5 + #full_guess.append(tmp_vec) + for qed_vec in guesses_tmp: + tmp_elec_vec = qed_vec.elec.zeros_like() + tmp_phot_vec = qed_vec.phot.zeros_like() + tmp_phot2_vec = qed_vec.phot2.copy() + #print("elec part", type(qed_vec.elec.ph)) + #print("phot part", type(qed_vec.phot.ph)) + #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) + #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) + tmp_phot2_vec *= 1e+10 + #tmp_elec_vec.ph *= 5 + #tmp_elec_vec.pphh *= 5 + vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + full_guess.append(vec) + #tmp_vec = qed_vec.copy() + #print(type(tmp_vec.phot2.ph)) + #tmp_vec.phot2.ph *= 5 + #tmp_vec.phot2.pphh *= 5 + #full_guess.append(tmp_vec) + + tmp_elec_vec = guesses_tmp[0].elec.zeros_like() + tmp_phot_vec = guesses_tmp[0].phot.zeros_like() + tmp_phot2_vec = guesses_tmp[0].phot2.zeros_like() + + tmp_elec_vec2 = guesses_tmp[0].elec.zeros_like() + tmp_phot_vec2 = guesses_tmp[0].phot.zeros_like() + tmp_phot2_vec2 = guesses_tmp[0].phot2.zeros_like() + + vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + vec2 = QED_AmplitudeVector(tmp_elec_vec2.ph, tmp_elec_vec2.pphh, 0, tmp_phot_vec2.ph, tmp_phot_vec2.pphh, 0, tmp_phot2_vec2.ph, tmp_phot2_vec2.pphh) + + #full_guess.append(guesses_tmp[0].copy()) + #full_guess.append(guesses_tmp[1].copy()) + + full_guess.append(vec) + full_guess.append(vec2) + + final_guesses = full_guess + else: + final_guesses = guesses_tmp + #guesses_tmp = full_guess + + """ + guesses_tmp = np.concatenate((guesses_tmp, guesses_tmp, guesses_tmp, guesses_tmp[:2])) + for i, guess in enumerate(guesses_tmp): + if i <= 19: + guess.elec.ph = guess.elec.ph * 5 + guess.elec.pphh = guess.elec.pphh * 5 + elif i >= 20 and i <= 39: + guess.phot.ph = guess.phot.ph * 5 + guess.phot.pphh = guess.phot.pphh * 5 + elif i >= 40 and i <= 59: + guess.phot2.ph = guess.phot2.ph * 5 + guess.phot2.pphh = guess.phot2.pphh * 5 + else: + pass + """ + #try: + final_guesses[len(final_guesses) - 2].gs1 += 2 #gs_vec(100) + final_guesses[len(final_guesses) - 1].gs2 += 5 #gs_vec(100) + #guesses_tmp = full_guess + #except: + # guesses_tmp[len(guesses_tmp) - 2].gs1 += 100 #gs_vec(100) + # guesses_tmp[len(guesses_tmp) - 1].gs2 += 100 #gs_vec(100) + +# # guesses_tmp needs to be normalized then #for vec in guesses_tmp: # print(type(vec.gs), vec.gs) @@ -542,9 +646,9 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= #normalized_guesses = [vec / np.sqrt(vec @ vec) for vec in guesses_tmp] #print("after normalization guesses[0].pphh is ", normalized_guesses[0].pphh) - for guess in guesses_tmp: + for guess in final_guesses: print(guess.gs1.as_float(), guess.gs2.as_float()) - return [vec / np.sqrt(vec @ vec) for vec in guesses_tmp] + return [vec / np.sqrt(vec @ vec) for vec in final_guesses] #for vec in guesses_tmp: # print("norm of guess vector = ", np.sqrt(vec @ vec)) #return guesses_tmp From 001e2d3779e1e9f444949760ec174d35442717d0 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 15 Feb 2022 13:48:34 +0100 Subject: [PATCH 21/64] added the possibility to only take first order photonic coupling into account, while electronic terms (including vacuum coupling to the quantized field) are taken into account through second order. --- adcc/AdcMatrix.py | 24 +++-- adcc/adc_pp/matrix.py | 210 ++++++++++++++++++++++++++++-------------- 2 files changed, 159 insertions(+), 75 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index ec539c50..46073c41 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -46,9 +46,9 @@ class AdcMatrix(AdcMatrixlike): default_block_orders = { # ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), #"adc0": dict(gs_gs=0, gs_ph=0, ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc0": dict(ph_gs=0, ph_ph=0, pphh_gs=None, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc1": dict(ph_gs=1, ph_ph=1, pphh_gs=None, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc2": dict(ph_gs=2, ph_ph=2, pphh_gs=1 , ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 + "adc0": dict(ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc1": dict(ph_gs=1, ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc2": dict(ph_gs=2, ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 "adc2x": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=1), # noqa: E501 "adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501 } @@ -91,6 +91,11 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None): if method.base_method.name == "adc2x" or method.base_method.name == "adc3": NotImplementedError("Neither adc2x nor adc3 are implemented for QED-ADC") + if method.base_method.name == "adc2" and hasattr(self.reference_state, "first_order_coupling"): + # this way we only need to include the separate case in the ph_ph=1 blocks, + # and the 4 non-zero coupling blocks + self.default_block_orders["adc2"] = dict(ph_gs=1, ph_ph=1, ph_pphh=1, pphh_ph=1, pphh_pphh=0) + self.intermediates = intermediates if self.intermediates is None: @@ -494,16 +499,19 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude #print("shape of phot couple inner", self.phot_couple_inner.matvec(v), type(self.phot_couple_inner.matvec(v))) - + phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) - if "pphh" in phot_part.blocks_ph: + if "pphh" in phot_part.blocks_ph and not hasattr(self.reference_state, "first_order_coupling"): phot_couple_edge_with_doubles = AmplitudeVector(ph=self.phot_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) elec_couple_edge_with_doubles = AmplitudeVector(ph=self.elec_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + phot_couple_edge_with_doubles #self.phot_couple_edge.matvec(v) #phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) #phot2_part = self.elec_couple_edge.matvec(v) + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) phot2_part = elec_couple_edge_with_doubles + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + elif "pphh" in phot_part.blocks_ph and hasattr(self.reference_state, "first_order_coupling"): + elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + phot2_part = self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) else: #phot_couple_edge_with_singles = AmplitudeVector(ph=v.ph.zeros_like()) #elec_couple_edge_with_singles = AmplitudeVector(ph=v.ph.zeros_like()) @@ -544,10 +552,12 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude pass elif block.endswith("phot_couple_inner"): #print("phot_couple_inner") - gs1_part += self.blocks_ph[block].apply(v) + #gs1_part += self.blocks_ph[block].apply(v) + pass elif block.endswith("couple_edge"): #print("couple_edge") - gs2_part += self.blocks_ph[block].apply(v) + #gs2_part += self.blocks_ph[block].apply(v) + pass elif block.endswith("couple_inner"): #print("couple_inner") gs2_part += self.blocks_ph[block].apply(v) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index f5331eb0..ebfa0f80 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -22,6 +22,7 @@ ## --------------------------------------------------------------------- from math import sqrt from collections import namedtuple +from attr import has import numpy as np from numpy.lib.function_base import blackman @@ -513,7 +514,7 @@ def apply(ampl): def block_ph_ph_1(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - omega = float(ReferenceState.get_qed_omega(hf)) # only for test purposes + #omega = float(ReferenceState.get_qed_omega(hf)) # only for test purposes if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): #diag_qed_term = einsum("klkl->", hf.oooo) diagonal = AmplitudeVector(ph=( @@ -538,26 +539,44 @@ def apply(ampl): #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #reintroduced (actually canceled from -E_0 (1) )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): - omega = float(ReferenceState.get_qed_omega(hf)) # omega is only here for a test...actually omega does not appear in this block - diagonal = AmplitudeVector(ph=( #change to QED_AmplitudeVector - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - - einsum("IaIa->Ia", CvCv) # order 1 - )) - #print(diagonal.set_random()) - #np.insert(diagonal, 0, 0) - #diagonal = np.zeros(np.add(diagonal_.shape, np.array([0]))) - #diagonal[1:,1:] = diagonal_ + #omega = float(ReferenceState.get_qed_omega(hf)) # omega is only here for a test...actually omega does not appear in this block + if hasattr(hf, "first_order_coupling"): + i1 = intermediates.adc2_i1 + i2 = intermediates.adc2_i2 + term_t2_eri = intermediates.term_t2_eri + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + )) - def apply(ampl): - mvprod = AmplitudeVector(ph=( #change to QED_AmplitudeVector # PT order - + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 - - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 - - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph, i1) + - einsum("ij,ja->ia", i2, ampl.ph) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 + )) + else: + diagonal = AmplitudeVector(ph=( #change to QED_AmplitudeVector + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 )) - #np.insert(mvprod, 0, 0) - #mvprod = np.zeros(np.add(mv_prod_.shape, np.array([0]))) - #mvprod[1:,1:] = mvprod_ - return mvprod + #print(diagonal.set_random()) + #np.insert(diagonal, 0, 0) + #diagonal = np.zeros(np.add(diagonal_.shape, np.array([0]))) + #diagonal[1:,1:] = diagonal_ + + def apply(ampl): + return AmplitudeVector(ph=( #change to QED_AmplitudeVector # PT order + + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 + )) + #np.insert(mvprod, 0, 0) + #mvprod = np.zeros(np.add(mv_prod_.shape, np.array([0]))) + #mvprod[1:,1:] = mvprod_ + #return mvprod else: diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 @@ -611,29 +630,47 @@ def apply(ampl): )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): omega = float(ReferenceState.get_qed_omega(hf)) - # Build two Kronecker deltas d_oo = zeros_like(hf.foo) d_vv = zeros_like(hf.fvv) d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - - einsum("IaIa->Ia", CvCv) # order 1 - + einsum("ii,aa->ia", d_oo, d_vv) * omega - )) - #np.insert(diagonal, 0, omega) + if hasattr(hf, "first_order_coupling"): + i1 = intermediates.adc2_i1 + i2 = intermediates.adc2_i2 + term_t2_eri = intermediates.term_t2_eri + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + + einsum("ii,aa->ia", d_oo, d_vv) * omega + )) - def apply(ampl): - mvprod = AmplitudeVector(ph=( # PT order - + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 - - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 - - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 - + omega * ampl.ph1 + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph1, i1) + - einsum("ij,ja->ia", i2, ampl.ph1) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 + + omega * ampl.ph1 + )) + else: + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 + + einsum("ii,aa->ia", d_oo, d_vv) * omega )) - #np.insert(mvprod, 0, omega) - return mvprod + #np.insert(diagonal, 0, omega) + + def apply(ampl): + return AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 + + omega * ampl.ph1 + )) + #np.insert(mvprod, 0, omega) else: raise NotImplementedError("and not hasattr(hf, qed_hf)") return AdcBlock(apply, diagonal) @@ -752,20 +789,40 @@ def apply(ampl): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - - einsum("IaIa->Ia", CvCv) # order 1 - + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 - )) - #np.insert(diagonal, 0, omega) + if hasattr(hf, "first_order_coupling"): + i1 = intermediates.adc2_i1 + i2 = intermediates.adc2_i2 + term_t2_eri = intermediates.term_t2_eri + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 + )) - def apply(ampl): - return AmplitudeVector(ph=( # PT order - + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 - - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 - - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 - + 2 * omega * ampl.ph2 + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph2, i1) + - einsum("ij,ja->ia", i2, ampl.ph2) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 + + 2 * omega * ampl.ph2 + )) + else: + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 + - einsum("IaIa->Ia", CvCv) # order 1 + + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 )) + #np.insert(diagonal, 0, omega) + + def apply(ampl): + return AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 + + 2 * omega * ampl.ph2 + )) return AdcBlock(apply, diagonal) @@ -904,24 +961,32 @@ def block_ph_pphh_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( + # usually this would be factor 4, but additional factor 2, due to normalization of pphh vector -4 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) )) - return AdcBlock(apply, 0) + if hasattr(hf, "first_order_coupling"): + return AdcBlock(lambda ampl: 0, 0) + else: + return AdcBlock(apply, 0) def block_ph_pphh_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( + # usually this would be factor 4, but additional factor 2, due to normalization of pphh vector -4 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) )) - return AdcBlock(apply, 0) + if hasattr(hf, "first_order_coupling"): + return AdcBlock(lambda ampl: 0, 0) + else: + return AdcBlock(apply, 0) block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner = block_pphh_ph_0_couple @@ -984,24 +1049,32 @@ def block_pphh_ph_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( + # usually this would be factor 4, but additional factor 1/2, due to normalization of pphh vector -4 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) )) - return AdcBlock(apply, 0) + if hasattr(hf, "first_order_coupling"): + return AdcBlock(lambda ampl: 0, 0) + else: + return AdcBlock(apply, 0) def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( + # usually this would be factor 4, but additional factor 1/2, due to normalization of pphh vector -4 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) )) - return AdcBlock(apply, 0) + if hasattr(hf, "first_order_coupling"): + return AdcBlock(lambda ampl: 0, 0) + else: + return AdcBlock(apply, 0) """ @@ -1575,7 +1648,7 @@ def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms qed_i2 = intermediates.adc2_qed_i2 #qed_i1_0 = intermediates.adc2_qed_i1_0 #qed_i2_0 = intermediates.adc2_qed_i2_0 - gs_part = intermediates.adc2_qed_ph_ph_2_phot_gs_part + #gs_part = intermediates.adc2_qed_ph_ph_2_phot_gs_part diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) @@ -1666,7 +1739,7 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the term qed_i2 = intermediates.adc2_qed_i2 #qed_i1_0 = intermediates.adc2_qed_i1_0 #qed_i2_0 = intermediates.adc2_qed_i2_0 - gs_part = intermediates.adc2_qed_ph_ph_2_phot2_gs_part + #gs_part = intermediates.adc2_qed_ph_ph_2_phot2_gs_part diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) @@ -1710,7 +1783,7 @@ def block_ph_ph_2_couple_inner(hf, mp, intermediates): #one could cash some of t #if hasattr(hf, "qed_hf"): # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") omega = float(ReferenceState.get_qed_omega(hf)) - gs_part = intermediates.adc2_qed_ph_ph_2_couple_inner_gs_part + #gs_part = intermediates.adc2_qed_ph_ph_2_couple_inner_gs_part qed_i1 = intermediates.adc2_qed_couple_i1 qed_i2 = intermediates.adc2_qed_couple_i2 @@ -1958,6 +2031,7 @@ def adc2_qed_i2(hf, mp, intermediates): # maybe do this with symmetrise + einsum("ic,jc->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) #qed intermediates for adc2, for non-qed-hf input +""" @register_as_intermediate def adc2_qed_i1_0(hf, mp, intermediates): #return (1/2) * einsum("kb,ka->ab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) @@ -1972,7 +2046,7 @@ def adc2_qed_i2_0(hf, mp, intermediates): return ((1/8) * (einsum("jc,ic->ij", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) + einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov))) + einsum("kc,kjic->ij", mp.qed_t0(b.ov), hf.ooov)) - +""" #@register_as_intermediate #def adc2_qed_ph_ph_2_i1(hf, mp, intermediates): # omega = float(ReferenceState.get_qed_omega(hf)) @@ -2002,7 +2076,7 @@ def term_t2_eri(hf, mp, intermediates): #@register_as_intermediate #def qed_adc2_ph_phot_couple_intermediate(hf, mp, intermediates): # return "blub" - +""" @register_as_intermediate def adc2_qed_ph_ph_2_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -2021,8 +2095,8 @@ def adc2_qed_ph_ph_2_gs_part(hf, mp, intermediates): #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) - - +""" +""" @register_as_intermediate def adc2_qed_ph_ph_2_couple_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -2043,8 +2117,8 @@ def adc2_qed_ph_ph_2_couple_gs_part(hf, mp, intermediates): #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) - - +""" +""" @register_as_intermediate def adc2_qed_ph_ph_2_couple_inner_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -2066,7 +2140,7 @@ def adc2_qed_ph_ph_2_couple_inner_gs_part(hf, mp, intermediates): #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) - +""" @register_as_intermediate @@ -2083,8 +2157,8 @@ def adc2_qed_ph_ph_2_phot_couple_gs_part(hf, mp, intermediates): #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)) - - mp.qed_t1_df(b.ov)) # 1. order + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + #- mp.qed_t1_df(b.ov)) # 1. order @register_as_intermediate @@ -2101,10 +2175,10 @@ def adc2_qed_ph_ph_2_phot_couple_inner_gs_part(hf, mp, intermediates): #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov)) - - mp.qed_t1_df(b.ov)) - + + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + #- mp.qed_t1_df(b.ov)) +""" @register_as_intermediate def adc2_qed_ph_ph_2_phot_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -2131,8 +2205,8 @@ def adc2_qed_ph_ph_2_phot_gs_part(hf, mp, intermediates): #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) - - +""" +""" @register_as_intermediate def adc2_qed_ph_ph_2_phot2_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -2159,7 +2233,7 @@ def adc2_qed_ph_ph_2_phot2_gs_part(hf, mp, intermediates): #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) - +""" @register_as_intermediate def adc2_qed_couple_i1(hf, mp, intermediates): From 7b8ac29a1ec83af9eb574a0ccc61c267785112f6 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 18 Feb 2022 16:51:33 +0100 Subject: [PATCH 22/64] first_order_coupling option now also treats additive part to ERIs in first order for qed_adc2. --- adcc/ReferenceState.py | 2 +- adcc/adc_pp/matrix.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index f56fd0de..e75145e8 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -228,7 +228,7 @@ def qed_D_object(self, block): return ds[block] def eri(self, block): - if hasattr(self, "coupling"): + if hasattr(self, "coupling") and not hasattr(self, "first_order_coupling") : from . import block as b from .functions import einsum ds_init = OneParticleOperator(self.mospaces, is_symmetric=True) #Since there is no TwoParticleOperator we do this diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index ebfa0f80..48420177 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -546,7 +546,7 @@ def apply(ampl): term_t2_eri = intermediates.term_t2_eri diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) + - einsum("IaIa->Ia", hf.ovov + hf.qed_D_object(b.ovov)) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) )) @@ -554,7 +554,7 @@ def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, i1) - einsum("ij,ja->ia", i2, ampl.ph) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 + - einsum("jaib,jb->ia", hf.ovov + hf.qed_D_object(b.ovov), ampl.ph) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 )) else: @@ -642,7 +642,7 @@ def apply(ampl): term_t2_eri = intermediates.term_t2_eri diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) + - einsum("IaIa->Ia", hf.ovov + hf.qed_D_object(b.ovov)) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + einsum("ii,aa->ia", d_oo, d_vv) * omega )) @@ -651,7 +651,7 @@ def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph1, i1) - einsum("ij,ja->ia", i2, ampl.ph1) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 + - einsum("jaib,jb->ia", hf.ovov + hf.qed_D_object(b.ovov), ampl.ph1) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 + omega * ampl.ph1 )) @@ -790,12 +790,13 @@ def apply(ampl): d_vv.set_mask("aa", 1.0) if hasattr(hf, "first_order_coupling"): + print("correct block chosen") i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 term_t2_eri = intermediates.term_t2_eri diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) + - einsum("IaIa->Ia", hf.ovov + hf.qed_D_object(b.ovov)) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 )) @@ -804,7 +805,7 @@ def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph2, i1) - einsum("ij,ja->ia", i2, ampl.ph2) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 + - einsum("jaib,jb->ia", hf.ovov + hf.qed_D_object(b.ovov), ampl.ph2) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 + 2 * omega * ampl.ph2 )) From 1866ce1ae175dc123c87ef0962782722564eeed2 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 18 Feb 2022 16:53:42 +0100 Subject: [PATCH 23/64] first_order_coupling option now also treats additive part to ERIs in first order for qed_adc2. --- adcc/adc_pp/matrix.py | 1 - 1 file changed, 1 deletion(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 48420177..eb3797fd 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -790,7 +790,6 @@ def apply(ampl): d_vv.set_mask("aa", 1.0) if hasattr(hf, "first_order_coupling"): - print("correct block chosen") i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 term_t2_eri = intermediates.term_t2_eri From 479ab9809b939202fb534e589b2e108896f86dd0 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 21 Feb 2022 11:25:00 +0100 Subject: [PATCH 24/64] uncommented missing H_1 terms in photonic coupling blocks --- adcc/adc_pp/matrix.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index eb3797fd..d2d894bb 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -1521,10 +1521,10 @@ def apply(ampl): #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - #- sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + - sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed - #+ einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # electric H_1 term - #+ einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) # electric H_1 term + + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # electric H_1 term + + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) # electric H_1 term #+ einsum("ijab,jb->ia", #couple_inter, #einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov) @@ -1582,10 +1582,10 @@ def apply(ampl): #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - #- sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + - sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) - #+ einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term - #+ einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term + + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term #+ einsum("ijab,jb->ia", #einsum("kb,ikja->ijab", mp.qed_t1(b.ov), hf.ooov) #+ einsum("jc,ibac->ijab", mp.qed_t1(b.ov), hf.ovvv), @@ -1805,10 +1805,10 @@ def apply(ampl): #- 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - #- sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + - sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed - #+ einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term - #+ einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term + + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term #+ gs_part * ampl.gs1.as_float() #+ (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) #- (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( @@ -1861,10 +1861,10 @@ def apply(ampl): #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - #- sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + - sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) - #+ einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term - #+ einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term + + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term + + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part + gs_part * ampl.gs2.as_float() #+ sqrt(omega / 2) * ( #gs_ph part From f1c920392ddcef1e5ac6c97ccb122de87d5b3deb Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 25 Feb 2022 15:25:35 +0100 Subject: [PATCH 25/64] corrected mistake within the qed pphh_ph (and vice versa) blocks --- adcc/adc_pp/matrix.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index d2d894bb..24f49338 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -962,7 +962,7 @@ def block_ph_pphh_1_couple(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( # usually this would be factor 4, but additional factor 2, due to normalization of pphh vector - -4 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) + -2 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) @@ -978,7 +978,7 @@ def block_ph_pphh_1_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( # usually this would be factor 4, but additional factor 2, due to normalization of pphh vector - -4 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) + -2 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) @@ -1050,7 +1050,7 @@ def block_pphh_ph_1_phot_couple(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pphh=( # usually this would be factor 4, but additional factor 1/2, due to normalization of pphh vector - -4 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) + -2 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) @@ -1066,7 +1066,7 @@ def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pphh=( # usually this would be factor 4, but additional factor 1/2, due to normalization of pphh vector - -4 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) + -2 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) From ad3f8c71889655b618df8f2cc0d0ac93dd8e42bc Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Wed, 2 Mar 2022 14:14:36 +0100 Subject: [PATCH 26/64] corrected sign error in ph_pphh photonic coupling blocks --- adcc/ReferenceState.py | 2 +- adcc/adc_pp/matrix.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index e75145e8..d1cb3d6d 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -228,7 +228,7 @@ def qed_D_object(self, block): return ds[block] def eri(self, block): - if hasattr(self, "coupling") and not hasattr(self, "first_order_coupling") : + if hasattr(self, "coupling"):# and not hasattr(self, "first_order_coupling") : from . import block as b from .functions import einsum ds_init = OneParticleOperator(self.mospaces, is_symmetric=True) #Since there is no TwoParticleOperator we do this diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 24f49338..5a89d615 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -546,7 +546,7 @@ def apply(ampl): term_t2_eri = intermediates.term_t2_eri diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov + hf.qed_D_object(b.ovov)) + - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) )) @@ -554,7 +554,7 @@ def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, i1) - einsum("ij,ja->ia", i2, ampl.ph) - - einsum("jaib,jb->ia", hf.ovov + hf.qed_D_object(b.ovov), ampl.ph) # 1 + - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 )) else: @@ -642,7 +642,7 @@ def apply(ampl): term_t2_eri = intermediates.term_t2_eri diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov + hf.qed_D_object(b.ovov)) + - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + einsum("ii,aa->ia", d_oo, d_vv) * omega )) @@ -651,7 +651,7 @@ def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph1, i1) - einsum("ij,ja->ia", i2, ampl.ph1) - - einsum("jaib,jb->ia", hf.ovov + hf.qed_D_object(b.ovov), ampl.ph1) # 1 + - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 + omega * ampl.ph1 )) @@ -795,7 +795,7 @@ def apply(ampl): term_t2_eri = intermediates.term_t2_eri diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov + hf.qed_D_object(b.ovov)) + - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 )) @@ -804,7 +804,7 @@ def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph2, i1) - einsum("ij,ja->ia", i2, ampl.ph2) - - einsum("jaib,jb->ia", hf.ovov + hf.qed_D_object(b.ovov), ampl.ph2) # 1 + - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 + 2 * omega * ampl.ph2 )) @@ -962,7 +962,7 @@ def block_ph_pphh_1_couple(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( # usually this would be factor 4, but additional factor 2, due to normalization of pphh vector - -2 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) + 2 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) @@ -978,7 +978,7 @@ def block_ph_pphh_1_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( # usually this would be factor 4, but additional factor 2, due to normalization of pphh vector - -2 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) + 2 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) @@ -1050,7 +1050,7 @@ def block_pphh_ph_1_phot_couple(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pphh=( # usually this would be factor 4, but additional factor 1/2, due to normalization of pphh vector - -2 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) + 2 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) @@ -1066,7 +1066,7 @@ def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pphh=( # usually this would be factor 4, but additional factor 1/2, due to normalization of pphh vector - -2 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) + 2 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) From c60a4b095850e1d1e53d50e432cda220ed9303f0 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 3 Mar 2022 10:37:32 +0100 Subject: [PATCH 27/64] full_diagonalize option included for qed-adc(1) --- adcc/AmplitudeVector.py | 16 ++++++++-------- adcc/workflow.py | 35 ++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index 233e5af3..ba05565a 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -389,16 +389,16 @@ def __truediv__(self, other): gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) def zeros_like(self): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(self.elec.zeros_like(), gs_vec(0), self.phot.zeros_like(), gs_vec(0), self.phot2.zeros_like()) - else: - return QED_AmplitudeVector(self.elec.zeros_like(), 0, self.phot.zeros_like()) + #if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(self.elec.zeros_like(), gs_vec(0), self.phot.zeros_like(), gs_vec(0), self.phot2.zeros_like()) + #else: + # return QED_AmplitudeVector(self.elec.zeros_like(), gs_vec(0), self.phot.zeros_like()) def empty_like(self): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(self.elec.empty_like(), [], self.phot.empty_like(), [], self.phot2.empty_like()) - else: - return QED_AmplitudeVector(self.elec.empty_like(), [], self.phot.empty_like()) + #if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(self.elec.empty_like(), [], self.phot.empty_like(), [], self.phot2.empty_like()) + #else: + # return QED_AmplitudeVector(self.elec.empty_like(), [], self.phot.empty_like()) def copy(self): #if "pphh" in self.elec.blocks_ph: diff --git a/adcc/workflow.py b/adcc/workflow.py index dff5a29b..57945471 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -490,9 +490,10 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= guesses_phot = obtain_guesses_by_inspection(matrix.phot, n_guesses, kind, n_guesses_doubles) guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) n_guess = len(guesses_elec) - for i in np.arange(n_guess): - guesses_phot[i] *= 1#0.02 - guesses_phot2[i] *= 1#0.001 + if not hasattr(matrix.reference_state, "full_diagonalization"): + for i in np.arange(n_guess): + guesses_phot[i] *= 0.02 + guesses_phot2[i] *= 0.001 #print("this is from obtain_guess_qed from workflow: guesses_elec[0].pphh", guesses_elec[0].pphh) if n_guess != len(guesses_phot): raise InputError("amount of guesses for electronic and photonic must be equal, but are" @@ -550,8 +551,11 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= #tmp_elec_vec *= 1 #tmp_elec_vec.ph *= 5 #tmp_elec_vec.pphh *= 5 - vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, + if contains_doubles: + vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + else: + vec = QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_phot2_vec.ph, None) full_guess.append(vec) for qed_vec in guesses_tmp: tmp_elec_vec = qed_vec.elec.zeros_like() @@ -564,7 +568,11 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= tmp_phot_vec *= 1e+10 #tmp_elec_vec.ph *= 5 #tmp_elec_vec.pphh *= 5 - vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + if contains_doubles: + vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, + 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + else: + vec = QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_phot2_vec.ph, None) full_guess.append(vec) #tmp_vec = qed_vec.copy() #print(type(tmp_vec.phot.ph)) @@ -582,7 +590,11 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= tmp_phot2_vec *= 1e+10 #tmp_elec_vec.ph *= 5 #tmp_elec_vec.pphh *= 5 - vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + if contains_doubles: + vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, + 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + else: + vec = QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_phot2_vec.ph, None) full_guess.append(vec) #tmp_vec = qed_vec.copy() #print(type(tmp_vec.phot2.ph)) @@ -598,9 +610,14 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= tmp_phot_vec2 = guesses_tmp[0].phot.zeros_like() tmp_phot2_vec2 = guesses_tmp[0].phot2.zeros_like() - vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) - vec2 = QED_AmplitudeVector(tmp_elec_vec2.ph, tmp_elec_vec2.pphh, 0, tmp_phot_vec2.ph, tmp_phot_vec2.pphh, 0, tmp_phot2_vec2.ph, tmp_phot2_vec2.pphh) - + if contains_doubles: + vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, + 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + vec2 = QED_AmplitudeVector(tmp_elec_vec2.ph, tmp_elec_vec2.pphh, 0, tmp_phot_vec2.ph, tmp_phot_vec2.pphh, + 0, tmp_phot2_vec2.ph, tmp_phot2_vec2.pphh) + else: + vec = QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_phot2_vec.ph, None) + vec2 = QED_AmplitudeVector(tmp_elec_vec2.ph, None, 0, tmp_phot_vec2.ph, None, 0, tmp_phot2_vec2.ph, None) #full_guess.append(guesses_tmp[0].copy()) #full_guess.append(guesses_tmp[1].copy()) From 09401966fba398e73f5164609b2e221b6ebf987e Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 8 Mar 2022 14:12:01 +0100 Subject: [PATCH 28/64] added omega with corresponding factor to diagonal doubles space --- adcc/AdcMatrix.py | 6 +++--- adcc/ReferenceState.py | 2 +- adcc/adc_pp/matrix.py | 26 ++++++++++++++++++++++++-- adcc/workflow.py | 6 +++--- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 46073c41..9495568a 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -509,9 +509,9 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude #phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) #phot2_part = self.elec_couple_edge.matvec(v) + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) phot2_part = elec_couple_edge_with_doubles + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) - elif "pphh" in phot_part.blocks_ph and hasattr(self.reference_state, "first_order_coupling"): - elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) - phot2_part = self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) + #elif "pphh" in phot_part.blocks_ph and hasattr(self.reference_state, "first_order_coupling"): + # elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + # phot2_part = self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) else: #phot_couple_edge_with_singles = AmplitudeVector(ph=v.ph.zeros_like()) #elec_couple_edge_with_singles = AmplitudeVector(ph=v.ph.zeros_like()) diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index d1cb3d6d..7b220bc9 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -210,7 +210,7 @@ def qed_D_object(self, block): from . import block as b from .functions import einsum total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) - omega = ReferenceState.get_qed_omega(self) + #omega = ReferenceState.get_qed_omega(self) total_dip.oo = ReferenceState.get_qed_total_dip(self, b.oo) total_dip.ov = ReferenceState.get_qed_total_dip(self, b.ov) total_dip.vv = ReferenceState.get_qed_total_dip(self, b.vv) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 5a89d615..dd8cb931 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -359,6 +359,23 @@ def diagonal_pphh_pphh_0(hf): return AmplitudeVector(pphh=res.symmetrise(2, 3)) +def diagonal_pphh_pphh_0_qed(hf, n_omega): + # Note: adcman similarly does not symmetrise the occupied indices + # (for both CVS and general ADC) + omega = float(ReferenceState.get_qed_omega(hf)) + #d_oo = zeros_like(hf.foo) + #d_vv = zeros_like(hf.fvv) + #d_oo.set_mask("ii", 1.0) + #d_vv.set_mask("aa", 1.0) + qed = n_omega * omega + + fCC = hf.fcc if hf.has_core_occupied_space else hf.foo + res = direct_sum("-i-J+a+b->iJab", + hf.foo.diagonal() + qed, fCC.diagonal() + qed, + hf.fvv.diagonal() + qed, hf.fvv.diagonal() + qed) + return AmplitudeVector(pphh=res.symmetrise(2, 3)) + + def block_pphh_pphh_0(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pphh=( @@ -378,12 +395,14 @@ def block_pphh_pphh_0_couple(hf, mp, intermediates): block_pphh_pphh_0_couple_edge = block_pphh_pphh_0_couple_inner = block_pphh_pphh_0_couple def block_pphh_pphh_0_phot(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( + 2 * einsum("ijac,bc->ijab", ampl.pphh1, hf.fvv).antisymmetrise(2, 3) - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh1).antisymmetrise(0, 1) + + 2 * omega * ampl.pphh1 )) - return AdcBlock(apply, diagonal_pphh_pphh_0(hf)) + return AdcBlock(apply, diagonal_pphh_pphh_0_qed(hf, 1)) def block_cvs_pphh_pphh_0(hf, mp, intermediates): @@ -397,12 +416,14 @@ def apply(ampl): def block_pphh_pphh_0_phot2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( + 2 * einsum("ijac,bc->ijab", ampl.pphh2, hf.fvv).antisymmetrise(2, 3) - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh2).antisymmetrise(0, 1) + + 4 * omega * ampl.pphh2 )) - return AdcBlock(apply, diagonal_pphh_pphh_0(hf)) + return AdcBlock(apply, diagonal_pphh_pphh_0_qed(hf, 2)) # @@ -734,6 +755,7 @@ def apply(ampl): #+ (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part )) return AdcBlock(apply, diagonal) + def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) diff --git a/adcc/workflow.py b/adcc/workflow.py index 57945471..91603e63 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -548,7 +548,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= #print("phot part", type(qed_vec.phot.ph)) #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) - #tmp_elec_vec *= 1 + tmp_elec_vec *= 10 #tmp_elec_vec.ph *= 5 #tmp_elec_vec.pphh *= 5 if contains_doubles: @@ -565,7 +565,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= #print("phot part", type(qed_vec.phot.ph)) #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) - tmp_phot_vec *= 1e+10 + tmp_phot_vec *= 10 #tmp_elec_vec.ph *= 5 #tmp_elec_vec.pphh *= 5 if contains_doubles: @@ -587,7 +587,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= #print("phot part", type(qed_vec.phot.ph)) #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) - tmp_phot2_vec *= 1e+10 + tmp_phot2_vec *= 10 #tmp_elec_vec.ph *= 5 #tmp_elec_vec.pphh *= 5 if contains_doubles: From b2096156e85668fa961e45a87445de6416acf732 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Wed, 9 Mar 2022 09:37:17 +0100 Subject: [PATCH 29/64] added 1. order coupling blocks to first order coupling --- adcc/adc_pp/matrix.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index dd8cb931..44fe9001 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -989,10 +989,10 @@ def apply(ampl): #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) )) - if hasattr(hf, "first_order_coupling"): - return AdcBlock(lambda ampl: 0, 0) - else: - return AdcBlock(apply, 0) + #if hasattr(hf, "first_order_coupling"): + # return AdcBlock(lambda ampl: 0, 0) + #else: + return AdcBlock(apply, 0) def block_ph_pphh_1_couple_inner(hf, mp, intermediates): @@ -1005,10 +1005,10 @@ def apply(ampl): #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) )) - if hasattr(hf, "first_order_coupling"): - return AdcBlock(lambda ampl: 0, 0) - else: - return AdcBlock(apply, 0) + #if hasattr(hf, "first_order_coupling"): + # return AdcBlock(lambda ampl: 0, 0) + #else: + return AdcBlock(apply, 0) block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner = block_pphh_ph_0_couple @@ -1077,10 +1077,10 @@ def apply(ampl): #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) )) - if hasattr(hf, "first_order_coupling"): - return AdcBlock(lambda ampl: 0, 0) - else: - return AdcBlock(apply, 0) + #if hasattr(hf, "first_order_coupling"): + # return AdcBlock(lambda ampl: 0, 0) + #else: + return AdcBlock(apply, 0) def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): @@ -1093,10 +1093,10 @@ def apply(ampl): #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) )) - if hasattr(hf, "first_order_coupling"): - return AdcBlock(lambda ampl: 0, 0) - else: - return AdcBlock(apply, 0) + #if hasattr(hf, "first_order_coupling"): + # return AdcBlock(lambda ampl: 0, 0) + #else: + return AdcBlock(apply, 0) """ From 06c18fe57d362a3bf0caf07506a98b06f11bea34 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 11 Mar 2022 14:01:07 +0100 Subject: [PATCH 30/64] corrected the scaling factor for omega in the doubles block --- adcc/adc_pp/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 44fe9001..9789cb93 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -400,7 +400,7 @@ def apply(ampl): return AmplitudeVector(pphh=( + 2 * einsum("ijac,bc->ijab", ampl.pphh1, hf.fvv).antisymmetrise(2, 3) - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh1).antisymmetrise(0, 1) - + 2 * omega * ampl.pphh1 + + omega * ampl.pphh1 )) return AdcBlock(apply, diagonal_pphh_pphh_0_qed(hf, 1)) @@ -421,7 +421,7 @@ def apply(ampl): return AmplitudeVector(pphh=( + 2 * einsum("ijac,bc->ijab", ampl.pphh2, hf.fvv).antisymmetrise(2, 3) - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh2).antisymmetrise(0, 1) - + 4 * omega * ampl.pphh2 + + 2 * omega * ampl.pphh2 )) return AdcBlock(apply, diagonal_pphh_pphh_0_qed(hf, 2)) From 3a1d8b484796f2b8b83c02d18ebdf2dbe9fc2d96 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Wed, 16 Mar 2022 16:34:01 +0100 Subject: [PATCH 31/64] documentary stuff --- adcc/adc_pp/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 9789cb93..66b184d8 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -983,7 +983,7 @@ def block_ph_pphh_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( - # usually this would be factor 4, but additional factor 2, due to normalization of pphh vector + # usually this would be factor 1, but additional factor 2, due to normalization of pphh vector 2 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) @@ -999,7 +999,7 @@ def block_ph_pphh_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( - # usually this would be factor 4, but additional factor 2, due to normalization of pphh vector + # usually this would be factor 1, but additional factor 2, due to normalization of pphh vector 2 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) From d5e8867477b17c842992c5caaa16bb30bfcefd64 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 18 Mar 2022 13:19:32 +0100 Subject: [PATCH 32/64] changed factor and sign of H_1 term of photonic coupling blocks --- adcc/adc_pp/matrix.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 66b184d8..1b50b320 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -1538,12 +1538,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph) # this is different from mp.diff_df, but why??? # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph)) # this is different from mp.diff_df, but why??? - - einsum("ib,ab->ia", ampl.ph, qed_i1) - - einsum("ij,ja->ia", qed_i2, ampl.ph) + + 0.25 * einsum("ib,ab->ia", ampl.ph, qed_i1) + + 0.25 * einsum("ij,ja->ia", qed_i2, ampl.ph) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - - sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + + 0.25 * sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # electric H_1 term + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) # electric H_1 term @@ -1599,12 +1599,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - - einsum("ib,ab->ia", ampl.ph1, qed_i1) - - einsum("ij,ja->ia", qed_i2, ampl.ph1) + + 0.25 * einsum("ib,ab->ia", ampl.ph1, qed_i1) + + 0.25 * einsum("ij,ja->ia", qed_i2, ampl.ph1) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - - sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + 0.25 * sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term @@ -1822,12 +1822,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - - sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) - - sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) + + 0.25 * sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + + 0.25 * sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) #- 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - - sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + + 0.25 * sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term @@ -1878,12 +1878,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2) # this is different from mp.diff_df, but why??? # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2)) # this is different from mp.diff_df, but why??? - - sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) - - sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) + + 0.25 * sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) + + 0.25 * sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - - sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + 0.25 * sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term From 7c2fe874065fdadd633254b9bb95cafe3de39c4a Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 18 Mar 2022 14:05:38 +0100 Subject: [PATCH 33/64] again factor changes in H_1 part of photonic coupling blocks --- adcc/adc_pp/matrix.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 1b50b320..c04fe2cd 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -1538,12 +1538,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph) # this is different from mp.diff_df, but why??? # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph)) # this is different from mp.diff_df, but why??? - + 0.25 * einsum("ib,ab->ia", ampl.ph, qed_i1) - + 0.25 * einsum("ij,ja->ia", qed_i2, ampl.ph) + + (1/4) * einsum("ib,ab->ia", ampl.ph, qed_i1) + + (1/4) * einsum("ij,ja->ia", qed_i2, ampl.ph) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - + 0.25 * sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + + (1/4) * sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # electric H_1 term + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) # electric H_1 term @@ -1599,12 +1599,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - + 0.25 * einsum("ib,ab->ia", ampl.ph1, qed_i1) - + 0.25 * einsum("ij,ja->ia", qed_i2, ampl.ph1) + + (1/4) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + + (1/4) * einsum("ij,ja->ia", qed_i2, ampl.ph1) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + 0.25 * sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + (1/4) * sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term @@ -1822,12 +1822,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - + 0.25 * sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) - + 0.25 * sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) + + (1/4) * sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + + (1/4) * sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) #- 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - + 0.25 * sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + + (1/4) * sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term @@ -1878,12 +1878,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2) # this is different from mp.diff_df, but why??? # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2)) # this is different from mp.diff_df, but why??? - + 0.25 * sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) - + 0.25 * sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) + + (1/4) * sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) + + (1/4) * sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + 0.25 * sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + (1/4) * sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term From 72a4fa8adddc3c21a6d04f58af02c63f15672389 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 18 Mar 2022 14:32:23 +0100 Subject: [PATCH 34/64] see above --- adcc/adc_pp/matrix.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index c04fe2cd..c3de427d 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -1538,12 +1538,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph) # this is different from mp.diff_df, but why??? # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph)) # this is different from mp.diff_df, but why??? - + (1/4) * einsum("ib,ab->ia", ampl.ph, qed_i1) - + (1/4) * einsum("ij,ja->ia", qed_i2, ampl.ph) + + einsum("ib,ab->ia", ampl.ph, qed_i1) + + einsum("ij,ja->ia", qed_i2, ampl.ph) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - + (1/4) * sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + + sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # electric H_1 term + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) # electric H_1 term @@ -1599,12 +1599,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - + (1/4) * einsum("ib,ab->ia", ampl.ph1, qed_i1) - + (1/4) * einsum("ij,ja->ia", qed_i2, ampl.ph1) + + einsum("ib,ab->ia", ampl.ph1, qed_i1) + + einsum("ij,ja->ia", qed_i2, ampl.ph1) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + (1/4) * sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term @@ -1822,12 +1822,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? - + (1/4) * sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) - + (1/4) * sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) + + sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) #- 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - + (1/4) * sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed + + sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term @@ -1878,12 +1878,12 @@ def apply(ampl): # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2) # this is different from mp.diff_df, but why??? # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2)) # this is different from mp.diff_df, but why??? - + (1/4) * sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) - + (1/4) * sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) + + sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) + + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + (1/4) * sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term From 8fc907fa0bc8e63a6cb786498fa1e79c76f9a7ac Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 21 Mar 2022 16:49:30 +0100 Subject: [PATCH 35/64] QED-ADC(2) test works now --- adcc/adc_pp/matrix.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index c3de427d..7cfc6e6c 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -119,7 +119,11 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): # elec_couple phot phot_couple_inner # elec_couple_edge elec_couple_inner phot2 - +# The implementations for QED-ADC(1) and QED-ADC(2) are tested by constructing the original non-QED-ADC matrix +# of the corresponding order with the transformed ERIs, and using those eigenvectors to construct the remaining +# contributions as properties. Those are mostly dipole operator terms, but one term also provides the two electron +# term from the original H_1 perturbation from MP. With those properties the same matrix can be build in the basis +# of the eigenvectors of the non-QED-ADC matrix, yielding the same eigenvalues upon diagonalization. # From 911fb88778e95cdbc9080d97ecb63fb26e141bd8 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 6 May 2022 13:40:14 +0200 Subject: [PATCH 36/64] less printout in davidson --- adcc/solver/davidson.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 563c28eb..0705e99d 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -226,16 +226,16 @@ def form_residual(rval, rvec): if Ass.shape == (n_block, n_block): #print("davidson eigh", la.eigh(Ass)[0]) rvals, rvecs = la.eigh(Ass) # Do a full diagonalisation - print("davidson eigh", rvals) - eigenvecs = [lincomb(v, SS, evaluate=True) - for v in np.transpose(rvecs)] - for eigv in eigenvecs: - print("norm of eigenvector", np.sqrt(eigv @ eigv)) + #print("davidson eigh", rvals) + #eigenvecs = [lincomb(v, SS, evaluate=True) + # for v in np.transpose(rvecs)] + #for eigv in eigenvecs: + #print("norm of eigenvector", np.sqrt(eigv @ eigv)) else: # TODO Maybe play with precision a little here # TODO Maybe use previous vectors somehow v0 = None - print("davidson eigh", la.eigh(Ass)[0]) + #print("davidson eigh", la.eigh(Ass)[0]) rvals, rvecs = sla.eigsh(Ass, k=n_block, which=which, v0=v0) with state.timer.record("residuals"): From 6167f340a22caaade341e81e888dff62cb250e82 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 16 May 2022 16:50:54 +0200 Subject: [PATCH 37/64] import for orbital energies and restricted checks in psi4 now works via Fa and Fb element (instead of epsilon_a and epsilon_b), which enables an easy transformation into an other basis, as required for the QED-CIS test --- adcc/backends/psi4.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index 859c5c1d..89bdee4f 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -127,8 +127,11 @@ def get_restricted(self): if isinstance(self.wfn, (psi4.core.RHF, psi4.core.ROHF)): return True elif isinstance(self.wfn, (psi4.core.Wavefunction)): - orben_a = np.asarray(self.wfn.epsilon_a()) - orben_b = np.asarray(self.wfn.epsilon_b()) + #orben_a = np.asarray(self.wfn.epsilon_a()) + #orben_b = np.asarray(self.wfn.epsilon_b()) + orben_a = np.asarray(self.wfn.Fa()).diagonal() + orben_b = np.asarray(self.wfn.Fb()).diagonal() + print(orben_a) if all(orben_a == orben_b): print("This is a restricted calculation") return True @@ -169,8 +172,10 @@ def fill_orbcoeff_fb(self, out): ) def fill_orben_f(self, out): - orben_a = np.asarray(self.wfn.epsilon_a()) - orben_b = np.asarray(self.wfn.epsilon_b()) + #orben_a = np.asarray(self.wfn.epsilon_a()) + #orben_b = np.asarray(self.wfn.epsilon_b()) + orben_a = np.asarray(self.wfn.Fa()).diagonal() + orben_b = np.asarray(self.wfn.Fb()).diagonal() out[:] = np.hstack((orben_a, orben_b)) def fill_occupation_f(self, out): From 696269bca5935e9d29cc2216bd1b9cbe2608aa3c Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 17 May 2022 13:37:10 +0200 Subject: [PATCH 38/64] orbital energies in psi4 backend need to be adjusted, depending on input. Usually epsilon is sufficient, but for psi4numpy qed_rhf implementation Fa.diagonal is required! --- adcc/backends/psi4.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index 89bdee4f..45211406 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -172,10 +172,11 @@ def fill_orbcoeff_fb(self, out): ) def fill_orben_f(self, out): - #orben_a = np.asarray(self.wfn.epsilon_a()) - #orben_b = np.asarray(self.wfn.epsilon_b()) - orben_a = np.asarray(self.wfn.Fa()).diagonal() - orben_b = np.asarray(self.wfn.Fb()).diagonal() + print("orbital energies are currently taken from epsilon (not from F.diagonal") + orben_a = np.asarray(self.wfn.epsilon_a()) + orben_b = np.asarray(self.wfn.epsilon_b()) + #orben_a = np.asarray(self.wfn.Fa()).diagonal() + #orben_b = np.asarray(self.wfn.Fb()).diagonal() out[:] = np.hstack((orben_a, orben_b)) def fill_occupation_f(self, out): From 41c8bab325ebd7bfc3890222240a5625a18f85e5 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 20 May 2022 14:25:04 +0200 Subject: [PATCH 39/64] adjusted psi4 backend for hilbert qed-(u/r)hf and psi4numpy cqed-rhf import --- adcc/backends/psi4.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index 45211406..934190f7 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -121,17 +121,12 @@ def get_conv_tol(self): threshold = max(10 * conv_tol, conv_tol_grad) return threshold - #def get_restricted(self): - # return isinstance(self.wfn, (psi4.core.RHF, psi4.core.ROHF)) def get_restricted(self): if isinstance(self.wfn, (psi4.core.RHF, psi4.core.ROHF)): return True elif isinstance(self.wfn, (psi4.core.Wavefunction)): - #orben_a = np.asarray(self.wfn.epsilon_a()) - #orben_b = np.asarray(self.wfn.epsilon_b()) - orben_a = np.asarray(self.wfn.Fa()).diagonal() - orben_b = np.asarray(self.wfn.Fb()).diagonal() - print(orben_a) + orben_a = np.asarray(self.wfn.epsilon_a()) + orben_b = np.asarray(self.wfn.epsilon_b()) if all(orben_a == orben_b): print("This is a restricted calculation") return True @@ -172,11 +167,8 @@ def fill_orbcoeff_fb(self, out): ) def fill_orben_f(self, out): - print("orbital energies are currently taken from epsilon (not from F.diagonal") orben_a = np.asarray(self.wfn.epsilon_a()) orben_b = np.asarray(self.wfn.epsilon_b()) - #orben_a = np.asarray(self.wfn.Fa()).diagonal() - #orben_b = np.asarray(self.wfn.Fb()).diagonal() out[:] = np.hstack((orben_a, orben_b)) def fill_occupation_f(self, out): From 40e752be959f1d4355c5b3dc646ac2c7ea4fd49d Mon Sep 17 00:00:00 2001 From: Marco Bauer Date: Tue, 24 May 2022 11:11:10 +0200 Subject: [PATCH 40/64] merged changes from master into submatrix class --- adcc/AdcMatrix.py | 162 ++++++++++++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 70 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 94992a1d..57ef8091 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -179,7 +179,6 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, variant=variant) for block, order in self.block_orders.items() if order is not None } -<<<<<<< HEAD # TODO Rename to self.block in 0.16.0 self.blocks_ph = {bl: blocks[bl].apply for bl in blocks} if diagonal_precomputed: @@ -188,14 +187,6 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, self.__diagonal = sum(bl.diagonal for bl in blocks.values() if bl.diagonal) self.__diagonal.evaluate() -======= - #for block, order in block_orders.items(): - # if order is not None: - # print(block, order) - self.__diagonal = sum(bl.diagonal for bl in self.blocks_ph.values() - if bl.diagonal) - self.__diagonal.evaluate() ->>>>>>> qed_adc self.__init_space_data(self.__diagonal) #print("following is self.__init_space_data(self.__diagonal)") #print(self.__init_space_data(self.__diagonal)) @@ -606,8 +597,6 @@ def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_Amplitude # print("god damn") #print("shape of phot couple edge", self.phot_couple_edge, type(self.phot_couple_edge)) #print("shape of phot couple inner", self.phot_couple_inner.matvec(v), type(self.phot_couple_inner.matvec(v))) - - phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) @@ -1065,35 +1054,43 @@ class AdcMatrix_submatrix(AdcMatrixlike): "adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501 } - def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates=None): + def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates=None, + diagonal_precomputed=None): """ Initialise an ADC matrix. - Parameters ---------- method : str or AdcMethod Method to use. hf_or_mp : adcc.ReferenceState or adcc.LazyMp HF reference or MP ground state - elec_or_phot: string, either "elec" or "phot" - electronic or photonic part of qed-matrix, without groundstate block_orders : optional The order of perturbation theory to employ for each matrix block. If not set, defaults according to the selected ADC method are chosen. intermediates : adcc.Intermediates or NoneType Allows to pass intermediates to re-use to this class. + diagonal_precomputed: adcc.AmplitudeVector + Allows to pass a pre-computed diagonal, for internal use only. """ if isinstance(hf_or_mp, (libadcc.ReferenceState, libadcc.HartreeFockSolution_i)): hf_or_mp = LazyMp(hf_or_mp) if not isinstance(hf_or_mp, LazyMp): - raise TypeError("mp_results is not a valid object. It needs to be " + raise TypeError("hf_or_mp is not a valid object. It needs to be " "either a LazyMp, a ReferenceState or a " "HartreeFockSolution_i.") if not isinstance(method, AdcMethod): method = AdcMethod(method) + if diagonal_precomputed: + if not isinstance(diagonal_precomputed, AmplitudeVector): + raise TypeError("diagonal_precomputed needs to be" + " an AmplitudeVector.") + if diagonal_precomputed.needs_evaluation: + raise ValueError("diagonal_precomputed must already" + " be evaluated.") + self.timer = Timer() self.method = method self.ground_state = hf_or_mp @@ -1101,6 +1098,7 @@ def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates= self.mospaces = hf_or_mp.reference_state.mospaces self.is_core_valence_separated = method.is_core_valence_separated self.ndim = 2 + self.extra_terms = [] self.intermediates = intermediates if self.intermediates is None: @@ -1116,7 +1114,7 @@ def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates= # Sanity checks on block_orders for block in block_orders.keys(): - if block not in ("gs_gs", "gs_ph", "ph_gs" ,"ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"): + if block not in ("ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"): raise ValueError(f"Invalid block order key: {block}") if block_orders["ph_pphh"] != block_orders["pphh_ph"]: raise ValueError("ph_pphh and pphh_ph should always have " @@ -1132,65 +1130,65 @@ def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates= variant = None if method.is_core_valence_separated: variant = "cvs" - self.blocks_ph = {} + #self.blocks_ph = {} if subblock == "elec": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=order, intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } elif subblock == "elec_couple": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=str(order) + "_couple", intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } elif subblock == "phot_couple": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=str(order) + "_phot_couple", intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } elif subblock == "phot": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=str(order) + "_phot", intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } elif subblock == "phot2": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=str(order) + "_phot2", intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } elif subblock == "phot_couple_inner": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=str(order) + "_phot_couple_inner", intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } elif subblock == "phot_couple_edge": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=str(order) + "_phot_couple_edge", intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } elif subblock == "elec_couple_inner": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=str(order) + "_couple_inner", intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } elif subblock == "elec_couple_edge": - self.blocks_ph = { # TODO Rename to self.block in 0.16.0 + blocks = { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), order=str(order) + "_couple_edge", intermediates=self.intermediates, variant=variant) @@ -1208,9 +1206,17 @@ def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates= # print(bl.diagonal) #for bl in self.blocks_ph: # print(bl) - self.__diagonal = sum(bl.diagonal for bl in self.blocks_ph.values() - if bl.diagonal) - self.__diagonal.evaluate() + #self.__diagonal = sum(bl.diagonal for bl in self.blocks_ph.values() + # if bl.diagonal) + #self.__diagonal.evaluate() + # TODO Rename to self.block in 0.16.0 + self.blocks_ph = {bl: blocks[bl].apply for bl in blocks} + if diagonal_precomputed: + self.__diagonal = diagonal_precomputed + else: + self.__diagonal = sum(bl.diagonal for bl in blocks.values() + if bl.diagonal) + self.__diagonal.evaluate() self.__init_space_data(self.__diagonal) #print("following is self.__init_space_data(self.__diagonal)") #print(self.__init_space_data(self.__diagonal)) @@ -1286,6 +1292,56 @@ def __init_space_data_qed(self, diagonal0): # also it seems, that self.axis_lengths is not further used, but shape[0] is rather used for that, so not necessary to build self.axis_lengths correctly """ + def __iadd__(self, other): + """In-place addition of an :py:class:`AdcExtraTerm` + Parameters + ---------- + other : AdcExtraTerm + the extra term to be added + """ + if not isinstance(other, AdcExtraTerm): + return NotImplemented + if not all(k in self.blocks_ph for k in other.blocks): + raise ValueError("Can only add to blocks of" + " AdcMatrix that already exist.") + for sp in other.blocks: + orig_app = self.blocks_ph[sp] + other_app = other.blocks[sp].apply + + def patched_apply(ampl, original=orig_app, other=other_app): + return sum(app(ampl) for app in (original, other)) + self.blocks_ph[sp] = patched_apply + other_diagonal = sum(bl.diagonal for bl in other.blocks.values() + if bl.diagonal) + self.__diagonal = self.__diagonal + other_diagonal + self.__diagonal.evaluate() + self.extra_terms.append(other) + return self + + def __add__(self, other): + """Addition of an :py:class:`AdcExtraTerm`, creating + a copy of self and adding the term to the new matrix + Parameters + ---------- + other : AdcExtraTerm + the extra term to be added + Returns + ------- + AdcMatrix + a copy of the AdcMatrix with the extra term added + """ + if not isinstance(other, AdcExtraTerm): + return NotImplemented + ret = AdcMatrix(self.method, self.ground_state, + block_orders=self.block_orders, + intermediates=self.intermediates, + diagonal_precomputed=self.diagonal()) + ret += other + return ret + + def __radd__(self, other): + return self.__add__(other) + def __init_space_data(self, diagonal): """Update the cached data regarding the spaces of the ADC matrix""" self.axis_spaces = {} @@ -1295,15 +1351,8 @@ def __init_space_data(self, diagonal): self.axis_lengths[block] = np.prod([ self.mospaces.n_orbs(sp) for sp in self.axis_spaces[block] ]) - #self.axis_spaces["gs"] = ["o0", "v0"] - #self.axis_lengths["gs"] = 1 self.shape = (sum(self.axis_lengths.values()), sum(self.axis_lengths.values())) - - #print(self.axis_spaces) - #print(self.axis_lengths) - #print(self.shape) - def __repr__(self): ret = f"AdcMatrix({self.method.name}, " @@ -1330,13 +1379,12 @@ def block_spaces(self, block): "will be removed in 0.16.0. " "Use `matrix.axis_spaces[block]` in the future.") return { - #"g": self.axis_spaces.get("gs", None), "s": self.axis_spaces.get("ph", None), "d": self.axis_spaces.get("pphh", None), "t": self.axis_spaces.get("ppphhh", None), }[block] - @property #non-qed + @property def axis_blocks(self): """ Return the blocks used along one of the axes of the ADC matrix @@ -1344,18 +1392,11 @@ def axis_blocks(self): """ return list(self.axis_spaces.keys()) - #@property #qed - #def axis_blocks(self): - #print(list(self.axis_spaces.keys()) + list(self.axis_spaces.keys())) - # return list(self.axis_spaces.keys()) + list(self.axis_spaces.keys()) - def diagonal(self, block=None): """Return the diagonal of the ADC matrix""" if block is not None: warnings.warn("Support for the block argument will be dropped " "in 0.16.0.") - #if block == "g": - # return self.__diagonal.gs #this is just a float, but it could be e.g. omega, for which I dont know if required data is given in this function if block == "s": return self.__diagonal.ph if block == "d": @@ -1365,11 +1406,10 @@ def diagonal(self, block=None): def compute_apply(self, block, tensor): warnings.warn("The compute_apply function is deprecated and " "will be removed in 0.16.0.") - if block in ("gg", "gs", "sg" ,"ss", "sd", "ds", "dd"): + if block in ("ss", "sd", "ds", "dd"): warnings.warn("The singles-doubles interface is deprecated and " "will be removed in 0.16.0.") - block = {"gg": "gs_gs", "gs": "gs_ph", "sg": "ph_gs", - "ss": "ph_ph", "sd": "ph_pphh", + block = {"ss": "ph_ph", "sd": "ph_pphh", "ds": "pphh_ph", "dd": "pphh_pphh"}[block] return self.block_apply(block, tensor) @@ -1444,15 +1484,12 @@ def construct_symmetrisation_for_blocks(self): applied to relevant blocks of an AmplitudeVector in order to symmetrise it to the right symmetry in order to be used with the various matrix-vector-products of this function. - Most importantly the returned functions antisymmetrise the occupied and virtual parts of the doubles parts if this is sensible for the method behind this adcmatrix. - Returns a dictionary block identifier -> function """ ret = {} - print("antisymm function is used in Amplitudevector part of AdcMatrix function") if self.is_core_valence_separated: # CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a) ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)]) @@ -1469,7 +1506,6 @@ def dense_basis(self, axis_blocks=None, ordering="adcc"): """ Return the list of indices and their values of the dense basis representation - ordering: adcc, spin, spatial """ ret = [] @@ -1497,11 +1533,6 @@ def reduce_index(n_orbsa, idx): # Sort first by spatial, then by spin return (spatial, is_beta) - if "gs" in axis_blocks: - ret_g = [] - ret_g.append([(0, 0), 1]) - ret.extend(ret_g) - if "ph" in axis_blocks: ret_s = [] sp_s = self.axis_spaces["ph"] @@ -1554,29 +1585,25 @@ def sortfctn(x): ret_d.sort(key=sortfctn) ret.extend(ret_d) - if any(b not in ("gs" ,"ph", "pphh") for b in self.axis_blocks): - raise NotImplementedError("Blocks other than gs, ph and pphh " + if any(b not in ("ph", "pphh") for b in self.axis_blocks): + raise NotImplementedError("Blocks other than ph and pphh " "not implemented") return ret - def to_ndarray(self, out=None): # this is not adapted to gs + def to_ndarray(self, out=None): """ Return the ADC matrix object as a dense numpy array. Converts the sparse internal representation of the ADC matrix to a dense matrix and return as a numpy array. - Notes ----- - This method is only intended to be used for debugging and visualisation purposes as it involves computing a large amount of matrix-vector products and the returned array consumes a considerable amount of memory. - The resulting matrix has no spin symmetry imposed, which means that its eigenspectrum may contain non-physical excitations (e.g. with linear combinations of α->β and α->α components in the excitation vector). - This function has not been sufficiently tested to be considered stable. """ # TODO Update to ph / pphh @@ -1645,7 +1672,6 @@ def to_ndarray(self, out=None): # this is not adapted to gs return out - class AdcBlockView(AdcMatrix): def __init__(self, fullmatrix, block): warnings.warn("The AdcBlockView class got deprecated and will be " @@ -1670,7 +1696,6 @@ def __init__(self, matrix, shift=0.0): """ Initialise a shifted ADC matrix. Applying this class to a vector ``v`` represents an efficient version of ``matrix @ v + shift * v``. - Parameters ---------- matrix : AdcMatrix @@ -1719,18 +1744,15 @@ def __init__(self, matrix, excitation_blocks, core_orbitals=None, Initialise a projected ADC matrix, i.e. represents the expression ``P @ M @ P`` where ``P`` is a projector onto a subset of ``excitation_blocks``. - The ``excitation_blocks`` are defined by partitioning the ``o1`` occupied and ``v1`` virtual space of the ``matrix.mospaces`` into a core-occupied ``c``, valence-occupied ``o``, inner-virtual ``v`` and outer-virtual ``w``. This matrix will only keep selected blocks in the amplitudes non-zero, which are selected in the ``excitation_blocks`` list (e.g. ``["cv", "ccvv", "ocvv"]``). - For details on the option how to select the spaces, see the documentation in :py:`adcc.ReferenceState.__init__` (``outer_virtuals`` follows the same rules as ``frozen_virtuals``). - Parameters ---------- matrix : AdcMatrix From 99c78a966ebe0f645d11f9a7717f1276c8b7a07f Mon Sep 17 00:00:00 2001 From: Marco Bauer Date: Thu, 2 Jun 2022 14:42:46 +0200 Subject: [PATCH 41/64] AdcMatrix.py now has only one matrix class, which is capable of handling the QED and standard method. Otherwise some cleanup has been done. --- adcc/AdcMatrix.py | 1254 +++--------------------- adcc/AmplitudeVector.py | 452 +-------- adcc/ExcitedStates.py | 11 +- adcc/adc_pp/matrix.py | 12 +- adcc/adc_pp/transition_dm.py | 2 +- adcc/functions.py | 19 +- adcc/guess/guesses_from_diagonal.py | 39 +- adcc/solver/davidson.py | 131 +-- adcc/solver/explicit_symmetrisation.py | 65 +- adcc/solver/lanczos.py | 14 +- adcc/workflow.py | 229 ++--- 11 files changed, 305 insertions(+), 1923 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 57ef8091..e7c7eeab 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -20,8 +20,6 @@ ## along with adcc. If not, see . ## ## --------------------------------------------------------------------- -from os import name -from numpy.lib.function_base import blackman import libadcc import warnings import numpy as np @@ -32,7 +30,7 @@ from .AdcMethod import AdcMethod from .functions import ones_like from .Intermediates import Intermediates -from .AmplitudeVector import QED_AmplitudeVector, AmplitudeVector, gs_vec +from .AmplitudeVector import QED_AmplitudeVector, AmplitudeVector class AdcExtraTerm: @@ -79,18 +77,23 @@ class AdcMatrix(AdcMatrixlike): default_block_orders = { # ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), #"adc0": dict(gs_gs=0, gs_ph=0, ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc0": dict(ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc1": dict(ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 + "adc2": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 + "adc2x": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=1), # noqa: E501 + "adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501 + } + + qed_default_block_orders = { "adc0": dict(ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 "adc1": dict(ph_gs=1, ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 "adc2": dict(ph_gs=2, ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 - "adc2x": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=1), # noqa: E501 - "adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501 } def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, diagonal_precomputed=None): """ Initialise an ADC matrix. - Parameters ---------- method : str or AdcMethod @@ -133,23 +136,25 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, self.ndim = 2 self.extra_terms = [] - if method.base_method.name == "adc2x" or method.base_method.name == "adc3": - NotImplementedError("Neither adc2x nor adc3 are implemented for QED-ADC") + if hasattr(self.reference_state, "coupling"): + if method.base_method.name == "adc2x" or method.base_method.name == "adc3": + NotImplementedError("Neither adc2x nor adc3 are implemented for QED-ADC") - if method.base_method.name == "adc2" and hasattr(self.reference_state, "first_order_coupling"): + if hasattr(self.reference_state, "first_order_coupling") and method.base_method.name == "adc2": # this way we only need to include the separate case in the ph_ph=1 blocks, # and the 4 non-zero coupling blocks - self.default_block_orders["adc2"] = dict(ph_gs=1, ph_ph=1, ph_pphh=1, pphh_ph=1, pphh_pphh=0) - + self.qed_default_block_orders["adc2"] = dict(ph_gs=1, ph_ph=1, ph_pphh=1, pphh_ph=1, pphh_pphh=0) self.intermediates = intermediates if self.intermediates is None: self.intermediates = Intermediates(self.ground_state) - # Determine orders of PT in the blocks if block_orders is None: - block_orders = self.default_block_orders[method.base_method.name] + if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): + block_orders = self.qed_default_block_orders[method.base_method.name] + else: + block_orders = self.default_block_orders[method.base_method.name] else: tmp_orders = self.default_block_orders[method.base_method.name].copy() tmp_orders.update(block_orders) @@ -157,7 +162,7 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, # Sanity checks on block_orders for block in block_orders.keys(): - if block not in ("ph_gs", "gs_ph", "pphh_gs", "ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"): + if block not in ("ph_gs", "ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"): raise ValueError(f"Invalid block order key: {block}") if block_orders["ph_pphh"] != block_orders["pphh_ph"]: raise ValueError("ph_pphh and pphh_ph should always have " @@ -167,259 +172,92 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, raise ValueError("pphh_pphh cannot be None if ph_pphh isn't.") self.block_orders = block_orders - # Build the blocks and diagonals - """#non-qed - with self.timer.record("build"): - variant = None - if self.is_core_valence_separated: - variant = "cvs" - blocks = { + self.qed_dispatch_dict = { + "elec": "", + "elec_couple": "_couple", "phot_couple": "_phot_couple", + "phot": "_phot", "phot2": "_phot2", "elec_couple_inner": "_couple_inner", + "elec_couple_edge": "_couple_edge", "phot_couple_inner": "_phot_couple_inner", + "phot_couple_edge": "_phot_couple_edge" + } + + def get_pp_blocks(disp_str): + return { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), - order=order, intermediates=self.intermediates, + order=str(order) + self.qed_dispatch_dict[disp_str], intermediates=self.intermediates, variant=variant) - for block, order in self.block_orders.items() if order is not None + for block, order in block_orders.items() if order is not None } - # TODO Rename to self.block in 0.16.0 - self.blocks_ph = {bl: blocks[bl].apply for bl in blocks} - if diagonal_precomputed: - self.__diagonal = diagonal_precomputed - else: - self.__diagonal = sum(bl.diagonal for bl in blocks.values() - if bl.diagonal) - self.__diagonal.evaluate() - self.__init_space_data(self.__diagonal) - #print("following is self.__init_space_data(self.__diagonal)") - #print(self.__init_space_data(self.__diagonal)) - """ - # here we try to use the whole functionality given for the non-qed case (with the AmplitudeVector class) - # and then use these to build the QED_AmplitudeVector class. Then we do something like - # self.blocks_ph = QED_AmplitudeVector(val0, self.blocks_ph0, val1, self.blocks_ph1) and - # self.diagonal = QED_AmplitudeVector(val0, self.__diagonal0, val1, self.__diagonal1) - + # Build the blocks and diagonals + with self.timer.record("build"): variant = None if method.is_core_valence_separated: variant = "cvs" - # first electronic part - - self.elec = AdcMatrix_submatrix(method, hf_or_mp, "elec") - - self.blocks_ph_00 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=order, intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - """ - for block, order in block_orders.items(): - if order is not None: - print(block, order) - self.__diagonal_0 = sum(bl.diagonal for bl in self.blocks_ph_0.values() - if bl.diagonal) - self.__diagonal_0.evaluate() - #self.__init_space_data_0(self.__diagonal_0) - """ - - # now for coupling parts - - self.elec_couple = AdcMatrix_submatrix(method, hf_or_mp, "elec_couple") - - self.blocks_ph_10 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_couple", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - - self.phot_couple = AdcMatrix_submatrix(method, hf_or_mp, "phot_couple") - - self.blocks_ph_01 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot_couple", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - - # now for photonic part - - self.phot = AdcMatrix_submatrix(method, hf_or_mp, "phot") - - self.blocks_ph_11 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - """ - #for block, order in block_orders.items(): - # if order is not None: - # print(block, order) - self.__diagonal_1 = sum(bl.diagonal for bl in self.blocks_ph_1.values() - if bl.diagonal) - self.__diagonal_1.evaluate() - #self.__init_space_data_0(self.__diagonal_1) - """ - - if method.base_method.name == "adc2" or method.base_method.name == "adc1": - self.phot2 = AdcMatrix_submatrix(method, hf_or_mp, "phot2") - - self.blocks_ph_22 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot2", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - - self.elec_couple_inner = AdcMatrix_submatrix(method, hf_or_mp, "elec_couple_inner") - - self.blocks_ph_21 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_couple_inner", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - - self.elec_couple_edge = AdcMatrix_submatrix(method, hf_or_mp, "elec_couple_edge") - - self.blocks_ph_20 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_couple_edge", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - - self.phot_couple_inner = AdcMatrix_submatrix(method, hf_or_mp, "phot_couple_inner") - - self.blocks_ph_12 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot_couple_inner", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - - self.phot_couple_edge = AdcMatrix_submatrix(method, hf_or_mp, "phot_couple_edge") - - self.blocks_ph_02 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot_couple_edge", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - - # build QED_AmplitudeVector - - self.blocks_ph_1_temp = {} - for key in self.blocks_ph_10: - self.blocks_ph_1_temp[key + "_couple"] = self.blocks_ph_10[key] - for key in self.blocks_ph_01: - self.blocks_ph_1_temp[key + "_phot_couple"] = self.blocks_ph_01[key] - for key in self.blocks_ph_11: - self.blocks_ph_1_temp[key + "_phot"] = self.blocks_ph_11[key] - - #if hasattr(self.elec.diagonal(), "pphh"): - for key in self.blocks_ph_20: - self.blocks_ph_1_temp[key + "_couple_edge"] = self.blocks_ph_20[key] - for key in self.blocks_ph_21: - self.blocks_ph_1_temp[key + "_couple_inner"] = self.blocks_ph_21[key] - for key in self.blocks_ph_02: - self.blocks_ph_1_temp[key + "_phot_couple_edge"] = self.blocks_ph_02[key] - for key in self.blocks_ph_12: - self.blocks_ph_1_temp[key + "_phot_couple_inner"] = self.blocks_ph_12[key] - for key in self.blocks_ph_22: - self.blocks_ph_1_temp[key + "_phot2"] = self.blocks_ph_22[key] - self.blocks_ph = {**self.blocks_ph_00, **self.blocks_ph_1_temp} - - #self.__diagonal_gs = 0 #sum(self.blocks_ph[block].diagonal for block in self.blocks_ph - # if "ph_gs" in block and not block.endswith("phot")) # coupling gs_gs blocks have diagonal = 0 - self.__diagonal_gs1 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph - if "ph_gs" in block and block.endswith("phot")) - self.__diagonal_gs2 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph - if "ph_gs" in block and block.endswith("phot2")) - - - #print("following should be the blocks, that make up the diagonal for gs_gs") - #for block in self.blocks_ph: - # if "ph_gs" in block and block.endswith("phot"): - # print("this is the block ", block) - #print(self.__diagonal_gs) - #print(self.__diagonal_gs1) - #self.blocks_ph = {**self.elec.blocks_ph, **self.blocks_ph_1_temp} - #self.blocks_ph = QED_AmplitudeVector(0, self.elec.blocks_ph, 0, self.phot.blocks_ph) - #self.__diagonal = QED_AmplitudeVector(0, self.elec.diagonal, 0, self.phot.diagonal) - #self.__init_space_data_qed(self.elec.diagonal) # here we need to adapt the attributes, defined via this function - if hasattr(self.elec.diagonal(), "pphh"): # this needs to be adapted, so that the diagonals for gs and gs1 are actually those from matrix.py, - # but if this is only for the guesses, this should only be a perfomance issue and not relevant for the precision - #print("from adcmatrix init diagonal has pphh part") - #self.__diagonal_gs2 = sum(self.blocks_ph[block].diagonal for block in self.blocks_ph - # if "ph_gs" in block and block.endswith("phot2")) - - self.__diagonal = QED_AmplitudeVector(self.elec.diagonal().ph, self.elec.diagonal().pphh, - self.__diagonal_gs1, self.phot.diagonal().ph, self.phot.diagonal().pphh, - self.__diagonal_gs2, self.phot2.diagonal().ph, self.phot2.diagonal().pphh) + if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): + blocks = {} + + if hasattr(hf_or_mp.reference_state, "coupling"): + for key in self.qed_dispatch_dict: + for bl, order in block_orders.items(): + if order is not None: + blocks[bl + "_" + key] = get_pp_blocks(key)[bl] + + + self.__diagonal_gs1 = sum(blocks[block].diagonal for block in blocks + if "ph_gs" in block and block.endswith("phot")) + self.__diagonal_gs2 = sum(blocks[block].diagonal for block in blocks + if "ph_gs" in block and block.endswith("phot2")) + + if "pphh_pphh_elec" in blocks: + self.__diagonal = QED_AmplitudeVector(blocks["ph_ph_elec"].diagonal.evaluate().ph, blocks["pphh_pphh_elec"].diagonal.evaluate().pphh, + self.__diagonal_gs1, blocks["ph_ph_phot"].diagonal.evaluate().ph, blocks["pphh_pphh_phot"].diagonal.evaluate().pphh, + self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, blocks["pphh_pphh_phot2"].diagonal.evaluate().pphh) + else: + self.__diagonal = QED_AmplitudeVector(blocks["ph_ph_elec"].diagonal.evaluate().ph, None, + self.__diagonal_gs1, blocks["ph_ph_phot"].diagonal.evaluate().ph, None, + self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, None) + self.__init_space_data_qed(self.__diagonal.elec) + #self.__diagonal.evaluate() else: - #print("from adcmatrix init diagonal has no pphh part!!!!!!!!!!!!!!!!!!!") - self.__diagonal = QED_AmplitudeVector(self.elec.diagonal().ph, None, - self.__diagonal_gs1, self.phot.diagonal().ph, None, - self.__diagonal_gs2, self.phot2.diagonal().ph, None) - self.__init_space_data_qed(self.elec.diagonal()) # here we need to adapt the attributes, defined via this function - # namely self.axis_spaces, self.axis_lengths, self.shape - self.__diagonal.evaluate() - #print(self.elec.blocks_ph) - #print(self.blocks_ph) - #print(self.axis_spaces) - #print(self.axis_lengths) - #print(self.shape) - #print(self.elec.axis_lengths) - #print(self.elec.shape) - #print("matvec has to be corrected, since .gs and .gs1 parts are not yet included, also remaining blocks have to included") - - + blocks = { + bl: get_pp_blocks("elec")[bl] for bl, order in block_orders.items() if order is not None + } - """ - #self.blocks_ph = QED_AmplitudeVector(0, self.blocks_ph_0, 0, self.blocks_ph_1) - self.blocks_ph_1_temp = {} - for key in self.blocks_ph_1: - self.blocks_ph_1_temp[key + "_phot"] = self.blocks_ph_1[key] - self.blocks_ph = {**self.blocks_ph_0, **self.blocks_ph_1_temp} - self.__diagonal = QED_AmplitudeVector(0, self.__diagonal_0, 0, self.__diagonal_1) - self.__init_space_data_qed(self.__diagonal_0) # here we need to adapt the attributes, defined via this function - # namely self.axis_spaces, self.axis_lengths, self.shape - print(self.blocks_ph_0) - print(self.blocks_ph_1) - print(self.blocks_ph) - print(self.axis_spaces) - print(self.axis_lengths) - print(self.shape) - """ + if diagonal_precomputed: + self.__diagonal = diagonal_precomputed + else: + self.__diagonal = sum(bl.diagonal for bl in blocks.values() + if bl.diagonal) + self.__diagonal.evaluate() + self.__init_space_data(self.__diagonal) + # TODO Rename to self.block in 0.16.0 + self.blocks_ph = {bl: blocks[bl].apply for bl in blocks} - # since the guesses need the whole matrix object as input, istead of just the diagonals, it is useful to define - # self.elec and self.phot as the the corresponding original matrices - # this is done in one class, since we also require the full matrix for the solver - - def __init_space_data_qed(self, diagonal0): - self.__init_space_data(diagonal0) - axis_spaces0 = self.axis_spaces # this (and the following) will be assigned in the submatrix class as well - axis_lengths0 = self.axis_lengths + def __init_space_data_qed(self, diagonal): + self.__init_space_data(diagonal) shape0 = self.shape - #if hasattr(diagonal0, "pphh"): - # self.shape = ((shape0[0]+1) * 3, (shape0[0]+1) * 3) - #else: - # self.shape = ((shape0[0]+1) * 2, (shape0[0]+1) * 2) self.shape = ((shape0[0]+1) * 3 - 1, (shape0[0]+1) * 3 - 1) - self.axis_spaces["gs"] = ["o0", "v0"] - self.axis_lengths["gs"] = 1 - # axis_spaces and axis_lengths both are dicts, referring to "ph", "pphh" as a key, so either make extra blocks here, or probably better - # make a longer list in funciton 'axis_blocks', so that later everything for "ph" and "pphh" is just used twice - # also it seems, that self.axis_lengths is not further used, but shape[0] is rather used for that, so not necessary to build self.axis_lengths correctly + #self.axis_spaces["gs"] = ["o0", "v0"] + #self.axis_lengths["gs"] = 1 + + def qed_subblock(self, qed_disp_key): + # These subblocks are "standard ADC matrices", so we can use the implemented functions. + # This is useful e.g. in the matvec function + """ + Extraction of subblocks from the full QED-ADC Matrix, + without the ph_gs blocks + """ + return [bl for key, bl in self.blocks_ph.items() + if not key.startswith("ph_gs") and key.endswith(qed_disp_key)] + def __iadd__(self, other): """In-place addition of an :py:class:`AdcExtraTerm` - Parameters ---------- other : AdcExtraTerm @@ -447,12 +285,10 @@ def patched_apply(ampl, original=orig_app, other=other_app): def __add__(self, other): """Addition of an :py:class:`AdcExtraTerm`, creating a copy of self and adding the term to the new matrix - Parameters ---------- other : AdcExtraTerm the extra term to be added - Returns ------- AdcMatrix @@ -481,10 +317,6 @@ def __init_space_data(self, diagonal): ]) self.shape = (sum(self.axis_lengths.values()), sum(self.axis_lengths.values())) - #print(self.axis_spaces) - #print(self.axis_lengths) - #print(self.shape) - def __repr__(self): ret = f"AdcMatrix({self.method.name}, " @@ -517,18 +349,19 @@ def block_spaces(self, block): "t": self.axis_spaces.get("ppphhh", None), }[block] - #@property #non-qed - #def axis_blocks(self): + @property + def axis_blocks(self): """ Return the blocks used along one of the axes of the ADC matrix (e.g. ['ph', 'pphh']). """ - # return list(self.axis_spaces.keys()) - + return list(self.axis_spaces.keys()) + """ @property def axis_blocks(self): # this is only for debugging and has not been adapted yet #print(list(self.axis_spaces.keys()) + list(self.axis_spaces.keys())) return list(self.axis_spaces.keys()) + list(self.axis_spaces.keys()) + """ def diagonal(self, block=None): """Return the diagonal of the ADC matrix""" @@ -546,10 +379,10 @@ def diagonal(self, block=None): def compute_apply(self, block, tensor): warnings.warn("The compute_apply function is deprecated and " "will be removed in 0.16.0.") - if block in ("gg", "gs", "sg" ,"ss", "sd", "ds", "dd"): + if block in ("sg" ,"ss", "sd", "ds", "dd"): warnings.warn("The singles-doubles interface is deprecated and " "will be removed in 0.16.0.") - block = {"gg": "gs_gs", "gs": "gs_ph", "sg": "ph_gs", + block = {"sg": "ph_gs", "ss": "ph_ph", "sd": "ph_pphh", "ds": "pphh_ph", "dd": "pphh_pphh"}[block] return self.block_apply(block, tensor) @@ -566,875 +399,64 @@ def block_apply(self, block, tensor): with self.timer.record(f"apply/{block}"): outblock, inblock = block.split("_") ampl = QED_AmplitudeVector(**{inblock: tensor}) - ret = self.blocks_ph[block].apply(ampl) + ret = self.blocks_ph[block](ampl) return getattr(ret, outblock) @timed_member_call() - def matvec(self, v): # for this sum the __add__ and/or __radd__ in QED_AmplitudeVector needs to be adjusted - # maybe also this function has to be adapted - # lets try differentiating via _phot or not in self.blocks_ph and then do the summation over the blocks, - # which can then be forwarded to AmplitudeVector for ph and pphh and gs_vec for gs + def matvec(self, v): """ Compute the matrix-vector product of the ADC matrix with an excitation amplitude and return the result. """ - #print("printing matvec stuff from AdcMatrix") - - #res = v.zeros_like() - - #elec1 = self.elec.matvec(v) - #elec2 = self.phot_couple.matvec(v) - #print(type(elec1.ph), elec1.ph) - #print(type(elec2.ph), elec2.ph) - - # maybe also gs_ph and gs_pphh blocks can be included here, by adding them to the ph_ph and pphh_pphh blocks, respectively. - # this should be possible, since we give v as ampl in matrix.py, so we can select e.g. ampl.gs1 in the ph_ph block. - #print("this is v.pphh from matvec ", v.pphh) - #try: - # self.phot_couple.matvec(v).ph + self.phot_couple_edge.matvec(v) - # print("works yeaaaaaaaaaaaaah") - #except: - # print("god damn") - #print("shape of phot couple edge", self.phot_couple_edge, type(self.phot_couple_edge)) - #print("shape of phot couple inner", self.phot_couple_inner.matvec(v), type(self.phot_couple_inner.matvec(v))) - - phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) - - if "pphh" in phot_part.blocks_ph and not hasattr(self.reference_state, "first_order_coupling"): - phot_couple_edge_with_doubles = AmplitudeVector(ph=self.phot_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) - elec_couple_edge_with_doubles = AmplitudeVector(ph=self.elec_couple_edge.matvec(v), pphh=v.pphh.zeros_like()) - elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) + phot_couple_edge_with_doubles #self.phot_couple_edge.matvec(v) - #phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) + self.phot_couple_inner.matvec(v) - #phot2_part = self.elec_couple_edge.matvec(v) + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) - phot2_part = elec_couple_edge_with_doubles + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) - #elif "pphh" in phot_part.blocks_ph and hasattr(self.reference_state, "first_order_coupling"): - # elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) - # phot2_part = self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) - else: - #phot_couple_edge_with_singles = AmplitudeVector(ph=v.ph.zeros_like()) - #elec_couple_edge_with_singles = AmplitudeVector(ph=v.ph.zeros_like()) - elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) - phot2_part = self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) - - #gs_part = 0 - gs1_part = 0 - gs2_part = 0 - - """ - elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) - phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) - gs_part = 0 - gs1_part = 0 - - if "pphh" in elec_part.blocks_ph: - gs2_part = 0 - elec_part = elec_part + self.phot_couple_edge.matvec(v) - phot_part = phot_part + self.phot_couple_inner.matvec(v) - phot2_part = self.elec_couple_edge.matvec(v) + self.elec_couple_inner.matvec(v) + self.phot2.matvec(v) - """ - #else: - # elec_part = self.elec.matvec(v) + self.phot_couple.matvec(v) - # phot_part = self.elec_couple.matvec(v) + self.phot.matvec(v) - - for block in self.blocks_ph: - if "gs" in block and not block.startswith("gs"): - #print(block) - #print(block, type(self.blocks_ph[block].apply(v)), self.blocks_ph[block].apply(v)) - #if "pphh" in elec_part.blocks_ph: - if block.endswith("phot2"): - #print("phot2") - gs2_part += self.blocks_ph[block].apply(v) - elif block.endswith("phot_couple_edge"): - #print("phot_couple_edge") - #gs_part += self.blocks_ph[block].apply(v) - pass - elif block.endswith("phot_couple_inner"): - #print("phot_couple_inner") - #gs1_part += self.blocks_ph[block].apply(v) - pass - elif block.endswith("couple_edge"): - #print("couple_edge") - #gs2_part += self.blocks_ph[block].apply(v) - pass - elif block.endswith("couple_inner"): - #print("couple_inner") - gs2_part += self.blocks_ph[block].apply(v) - elif block.endswith("phot_couple"): - #print("phot_couple") - #gs_part += self.blocks_ph[block].apply(v) - pass - elif block.endswith("phot"): - #print("phot") - gs1_part += self.blocks_ph[block].apply(v) - elif block.endswith("couple"): - #print("couple") - gs1_part += self.blocks_ph[block].apply(v) - else: # elec - #print("elec") - #gs_part += self.blocks_ph[block].apply(v) - pass - """ - elif "gs" in block and block.startswith("gs"): - if "phot_couple" in block: - elec_part += self.blocks_ph[block].apply(v) - elif "phot" in block: - phot_part += self.blocks_ph[block].apply(v) - elif "couple" in block: - phot_part += self.blocks_ph[block].apply(v) - else: # elec - elec_part += self.blocks_ph[block].apply(v) - """ - - # try .gs and .gs1 parts with floats first, and then give them to the QED_AmplitudeVector in the end - # if that doesnt work, use QED_AmplitudeVectors from the beginning, which then has to be implemented in matrix.py as well + if isinstance(v, AmplitudeVector): + return sum(block(v) for block in self.blocks_ph.values()) + elif isinstance(v, QED_AmplitudeVector): - """ - for block in self.blocks_ph: - if "gs" not in block: - print(block) - if block.endswith("_phot_couple"): - res.elec = sum(res.elec,self.blocks_ph[block].apply(v)) - elif block.endswith("_couple"): - res.phot = sum(res.phot,self.blocks_ph[block].apply(v)) - elif block.endswith("_phot"): - res.phot = sum(res.phot,self.blocks_ph[block].apply(v)) - elif block in self.blocks_ph_00: - print(type(res.elec), type(self.blocks_ph[block].apply(v))) - res.elec = sum(res.elec,self.blocks_ph[block].apply(v)) - else: - print("block {} has not been taken into account in matvec".format(block)) - """ + def mv(qed_disp_key): + return sum(block(v) for block in self.qed_subblock(qed_disp_key)) + phot_part = mv("elec_couple") + mv("phot") + mv("phot_couple_inner") - """ - self.blocks_ph_elec = {} - self.blocks_ph_phot = {} - self.blocks_ph_elec0 = {} # this is only necessary, if gs block part in ph/pphh space are in explicit blocks, and not e.g. in ph_ph block - self.blocks_ph_phot0 = {} # this is only necessary, if gs block part in ph/pphh space are in explicit blocks, and not e.g. in ph_ph_phot block - for block in self.blocks_ph: - if block.endswith("_phot"): - if not block.startswith("block_gs") and "gs" in block: # find all gs space blocks (with ampl.gs/ampl.gs1) - if self.blocks_ph[block].gs == None: - self.blocks_ph_phot0[block] = self.blocks_ph[block].gs1 - else: - self.blocks_ph_phot0[block] = self.blocks_ph[block].gs - else: - if self.blocks_ph[block].elec == None: - self.blocks_ph_phot[block] = self.blocks_ph[block].phot - else: - self.blocks_ph_phot[block] = self.blocks_ph[block].elec - #print(block, self.blocks_ph[block]) + if "pphh_pphh_elec" in self.blocks_ph.keys() and not hasattr(self.reference_state, "first_order_coupling"): + phot_couple_edge_with_doubles = AmplitudeVector(ph=mv("phot_couple_edge"), pphh=v.pphh.zeros_like()) + elec_couple_edge_with_doubles = AmplitudeVector(ph=mv("elec_couple_edge"), pphh=v.pphh.zeros_like()) + elec_part = mv("elec") + mv("phot_couple") + phot_couple_edge_with_doubles + phot2_part = elec_couple_edge_with_doubles + mv("elec_couple_inner") + mv("phot2") else: - if not block.startswith("block_gs") and "gs" in block: # find all gs1 space blocks (with ampl.gs/ampl.gs1) - if self.blocks_ph[block].gs == None: - self.blocks_ph_elec0[block] = self.blocks_ph[block].gs1 - else: - self.blocks_ph_elec0[block] = self.blocks_ph[block].gs - else: - if self.blocks_ph[block].elec == None: - self.blocks_ph_elec[block] = self.blocks_ph[block].phot - else: - self.blocks_ph_elec[block] = self.blocks_ph[block].elec - #print(block, self.blocks_ph[block]) - - res = v.zeros_like() - #print("res", res.gs, res.elec, res.gs1, res.phot) - - res.gs = sum(block.apply(v) for block in self.blocks_ph_elec.values())# if hasattr(block.apply(v), "gs")) # __add__ from gs_vec - res.elec = sum(block.apply(v) for block in self.blocks_ph_elec.values())# if not hasattr(block.apply(v), "gs")) # __add__ from AmplitudeVector - res.gs1 = sum(block.apply(v) for block in self.blocks_ph_phot.values())# if hasattr(block.apply(v), "gs")) # __add__ from gs_vec - res.phot = sum(block.apply(v) for block in self.blocks_ph_phot.values())# if not hasattr(block.apply(v), "gs")) # __add__ from AmplitudeVector - """ - - #for block in self.blocks_ph_elec.values(): - # print(type(block.apply(v))) - # print(block.apply(v)) - #for block in self.blocks_ph: - # print(block) - """ - for block in self.blocks_ph_elec.values(): - #print(type(block.apply(v))) - if hasattr(block.apply(v), "gs"): # for gs QED_AmplitudeVector, while for ph and pphh AmplitudeVector - #print(block) - if block.apply(v).gs != None: - #print(res.gs, block.apply(v).gs) - res.gs += block.apply(v).gs - elif block.apply(v).gs1 != None: - res.gs += block.apply(v).gs1 - #else: - # if block.apply(v) != None: - # res.elec = sum(res.elec, block.apply(v)) - - for block in self.blocks_ph_phot.values(): - if hasattr(block.apply(v), "gs"): # for gs QED_AmplitudeVector, while for ph and pphh AmplitudeVector - #print(block) - if block.apply(v).gs != None: - #print("printing line 405 now") - #print(res.gs1, block.apply(v).gs) - res.gs1 += block.apply(v).gs - elif block.apply(v).gs1 != None: - res.gs1 += block.apply(v).gs1 - #else: - # if block.apply(v) != None: - # res.phot += block.apply(v) - - #return sum(block.apply(v) for block in self.blocks_ph.values()) - """ - #print(res.gs, res.elec, res.gs1, res.phot) - if "pphh" in elec_part.blocks_ph: - return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, - gs2_part, phot2_part.ph, phot2_part.pphh) - else: - return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) - - - - - def rmatvec(self, v): - # ADC matrix is symmetric - return self.matvec(v) - - def compute_matvec(self, ampl): - """ - Compute the matrix-vector product of the ADC matrix - with an excitation amplitude and return the result. - """ - warnings.warn("The compute_matvec function is deprecated and " - "will be removed in 0.16.0.") - return self.matvec(ampl) + elec_part = mv("elec") + mv("phot_couple") + phot2_part = mv("elec_couple_inner") + mv("phot2") - def __matmul__(self, other): - if isinstance(other, QED_AmplitudeVector): - return self.matvec(other) - if isinstance(other, list): - if all(isinstance(elem, QED_AmplitudeVector) for elem in other): - return [self.matvec(ov) for ov in other] - return NotImplemented - - def block_view(self, block): - """ - Return a view into the AdcMatrix that represents a single - block of the matrix. Currently only diagonal blocks are supported. - """ - b1, b2 = block.split("_") - if b1 != b2: - raise NotImplementedError("Off-diagonal block views not yet " - "implemented.") - # TODO For off-diagonal blocks we probably need a different - # data structure as the AdcMatrix class as these block - # are inherently different than an AdcMatrix (no Hermiticity - # for example) and basically they only need to support some - # form of matrix-vector product and some stastics like - # spaces and sizes etc. - block_orders = {bl: None for bl in self.block_orders.keys()} - block_orders[block] = self.block_orders[block] - return AdcMatrix(self.method, self.ground_state, - block_orders=block_orders, - intermediates=self.intermediates) - - def construct_symmetrisation_for_blocks(self): - """ - Construct the symmetrisation functions, which need to be - applied to relevant blocks of an AmplitudeVector in order - to symmetrise it to the right symmetry in order to be used - with the various matrix-vector-products of this function. - - Most importantly the returned functions antisymmetrise - the occupied and virtual parts of the doubles parts - if this is sensible for the method behind this adcmatrix. - - Returns a dictionary block identifier -> function - """ - ret = {} - print("antisymm function is used in QED_Amplitudevector part of AdcMatrix function") - if self.is_core_valence_separated: - # CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a) - ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)]) - else: - def symmetrise_generic_adc_doubles(invec): - # doubles part is antisymmetric wrt. (i,j,a,b) <-> (i,j,b,a) - scratch = invec.antisymmetrise([(2, 3)]) - #print(type(invec), invec) - # doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a) - return scratch.symmetrise([(0, 1), (2, 3)]) - ret["pphh"] = symmetrise_generic_adc_doubles - #print(ret) - return ret - - def dense_basis(self, axis_blocks=None, ordering="adcc"): - """ - Return the list of indices and their values - of the dense basis representation - - ordering: adcc, spin, spatial - """ - ret = [] - if axis_blocks is None: - axis_blocks = self.axis_blocks - if not isinstance(axis_blocks, list): - axis_blocks = [axis_blocks] - - # Define function to impose the order in the basis - if ordering == "adcc": - def reduce_index(n_orbsa, idx): - return idx, idx - elif ordering == "spin": - def reduce_index(n_orbsa, idx): - is_beta = [idx[i] >= n_orbsa[i] for i in range(len(idx))] - spatial = [idx[i] - n_orbsa[i] if is_beta[i] else idx[i] - for i in range(len(idx))] - # Sort first by spin, then by spatial - return (is_beta, spatial) - elif ordering == "spatial": - def reduce_index(n_orbsa, idx): - is_beta = [idx[i] >= n_orbsa[i] for i in range(len(idx))] - spatial = [idx[i] - n_orbsa[i] if is_beta[i] else idx[i] - for i in range(len(idx))] - # Sort first by spatial, then by spin - return (spatial, is_beta) - - if "gs" in axis_blocks: - ret_g = [] - ret_g.append([(0, 0), 1]) - ret.extend(ret_g) - - if "ph" in axis_blocks: - ret_s = [] - sp_s = self.axis_spaces["ph"] - n_orbs_s = [self.mospaces.n_orbs(sp) for sp in sp_s] - n_orbsa_s = [self.mospaces.n_orbs_alpha(sp) for sp in sp_s] - for i in range(n_orbs_s[0]): - for a in range(n_orbs_s[1]): - ret_s.append([((i, a), 1)]) - - def sortfctn(x): - return min(reduce_index(n_orbsa_s, idx) for idx, factor in x) - ret_s.sort(key=sortfctn) - ret_s.sort(key=sortfctn) - ret.extend(ret_s) - - if "pphh" in axis_blocks: - ret_d = [] - sp_d = self.axis_spaces["pphh"] - n_orbsa_d = [self.mospaces.n_orbs_alpha(sp) for sp in sp_d] + gs1_part = 0 + gs2_part = 0 - if sp_d[0] == sp_d[1] and sp_d[2] == sp_d[3]: - nso = self.mospaces.n_orbs(sp_d[0]) - nsv = self.mospaces.n_orbs(sp_d[2]) - ret_d.extend([[((i, j, a, b), +1 / 2), - ((j, i, a, b), -1 / 2), - ((i, j, b, a), -1 / 2), - ((j, i, b, a), +1 / 2)] - for i in range(nso) for j in range(i) - for a in range(nsv) for b in range(a)]) - elif sp_d[2] == sp_d[3]: - nso = self.mospaces.n_orbs(sp_d[0]) - nsc = self.mospaces.n_orbs(sp_d[1]) - nsv = self.mospaces.n_orbs(sp_d[2]) - ret_d.extend([[((i, j, a, b), +1 / np.sqrt(2)), - ((i, j, b, a), -1 / np.sqrt(2))] - for i in range(nso) for j in range(nsc) - for a in range(nsv) for b in range(a)]) + for block in self.blocks_ph: + if "gs" in block and not block.startswith("gs"): + if block.endswith("phot2"): + gs2_part += self.blocks_ph[block](v) + elif block.endswith("phot_couple_edge"): + continue + elif block.endswith("phot_couple_inner"): + continue + elif block.endswith("couple_edge"): + continue + elif block.endswith("couple_inner"): + gs2_part += self.blocks_ph[block](v) + elif block.endswith("phot_couple"): + continue + elif block.endswith("phot"): + gs1_part += self.blocks_ph[block](v) + elif block.endswith("couple"): + gs1_part += self.blocks_ph[block](v) + else: # elec + continue + + if "pphh_pphh_elec" in self.blocks_ph.keys(): + return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, + gs2_part, phot2_part.ph, phot2_part.pphh) else: - nso = self.mospaces.n_orbs(sp_d[0]) - nsc = self.mospaces.n_orbs(sp_d[1]) - nsv = self.mospaces.n_orbs(sp_d[2]) - nsw = self.mospaces.n_orbs(sp_d[3]) - ret_d.append([((i, j, b, a), 1) - for i in range(nso) for j in range(nsc) - for a in range(nsv) for b in range(nsw)]) - - def sortfctn(x): - return min(reduce_index(n_orbsa_d, idx) for idx, factor in x) - ret_d.sort(key=sortfctn) - ret_d.sort(key=sortfctn) - ret.extend(ret_d) - - if any(b not in ("gs" ,"ph", "pphh") for b in self.axis_blocks): - raise NotImplementedError("Blocks other than gs, ph and pphh " - "not implemented") - return ret - - def to_ndarray(self, out=None): # this is not adapted to gs - """ - Return the ADC matrix object as a dense numpy array. Converts the sparse - internal representation of the ADC matrix to a dense matrix and return - as a numpy array. - - Notes - ----- - - This method is only intended to be used for debugging and - visualisation purposes as it involves computing a large amount of - matrix-vector products and the returned array consumes a considerable - amount of memory. - - The resulting matrix has no spin symmetry imposed, which means that - its eigenspectrum may contain non-physical excitations (e.g. with linear - combinations of α->β and α->α components in the excitation vector). - - This function has not been sufficiently tested to be considered stable. - """ - # TODO Update to ph / pphh - # TODO Still uses deprecated functions - import tqdm - - from adcc import guess_zero - - # Get zero amplitude of the appropriate symmetry - # (TODO: Only true for C1, where there is only a single irrep) - ampl_zero = guess_zero(self) - assert self.mospaces.point_group == "C1" - - # Build the shape of the returned array - # Since the basis of the doubles block is not the unit vectors - # this *not* equal to the shape of the AdcMatrix object - basis = {b: self.dense_basis(b) for b in self.axis_blocks} - mat_len = sum(len(basis[b]) for b in basis) - - if out is None: - out = np.zeros((mat_len, mat_len)) - else: - if out.shape != (mat_len, mat_len): - raise ValueError("Output array has shape ({0:}, {1:}), but " - "shape ({2:}, {2:}) is required." - "".format(*out.shape, mat_len)) - out[:] = 0 # Zero all data in out. - - # Check for the cases actually implemented - if any(b not in ("ph", "pphh") for b in self.axis_blocks): - raise NotImplementedError("Blocks other than ph and pphh " - "not implemented") - if "ph" not in self.axis_blocks: - raise NotImplementedError("Block 'ph' needs to be present") - - # Extract singles-singles block (contiguous) - assert "ph" in self.axis_blocks - n_orbs_ph = [self.mospaces.n_orbs(sp) for sp in self.axis_spaces["ph"]] - n_ph = np.prod(n_orbs_ph) - assert len(basis["ph"]) == n_ph - view_ss = out[:n_ph, :n_ph].reshape(*n_orbs_ph, *n_orbs_ph) - for i in range(n_orbs_ph[0]): - for a in range(n_orbs_ph[1]): - ampl = ampl_zero.copy() - ampl.ph[i, a] = 1 - view_ss[:, :, i, a] = (self @ ampl).ph.to_ndarray() - - # Extract singles-doubles and doubles-doubles block - if "pphh" in self.axis_blocks: - assert self.axis_blocks == ["ph", "pphh"] - view_sd = out[:n_ph, n_ph:].reshape(*n_orbs_ph, len(basis["pphh"])) - view_dd = out[n_ph:, n_ph:] - for j, bas1 in tqdm.tqdm(enumerate(basis["pphh"]), - total=len(basis["pphh"])): - ampl = ampl_zero.copy() - for idx, val in bas1: - ampl.pphh[idx] = val - ret_ampl = self @ ampl - view_sd[:, :, j] = ret_ampl.ph.to_ndarray() - - for i, bas2 in enumerate(basis["pphh"]): - view_dd[i, j] = sum(val * ret_ampl.pphh[idx] - for idx, val in bas2) - - out[n_ph:, :n_ph] = np.transpose(out[:n_ph, n_ph:]) - return out - - - -class AdcMatrix_submatrix(AdcMatrixlike): - # Default perturbation-theory orders for the matrix blocks (== standard ADC-PP). - default_block_orders = { - # ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), - #"adc0": dict(gs_gs=0, gs_ph=0, ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc0": dict(ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc1": dict(ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc2": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 - "adc2x": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=1), # noqa: E501 - "adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501 - } - - def __init__(self, method, hf_or_mp, subblock, block_orders=None, intermediates=None, - diagonal_precomputed=None): - """ - Initialise an ADC matrix. - Parameters - ---------- - method : str or AdcMethod - Method to use. - hf_or_mp : adcc.ReferenceState or adcc.LazyMp - HF reference or MP ground state - block_orders : optional - The order of perturbation theory to employ for each matrix block. - If not set, defaults according to the selected ADC method are chosen. - intermediates : adcc.Intermediates or NoneType - Allows to pass intermediates to re-use to this class. - diagonal_precomputed: adcc.AmplitudeVector - Allows to pass a pre-computed diagonal, for internal use only. - """ - if isinstance(hf_or_mp, (libadcc.ReferenceState, - libadcc.HartreeFockSolution_i)): - hf_or_mp = LazyMp(hf_or_mp) - if not isinstance(hf_or_mp, LazyMp): - raise TypeError("hf_or_mp is not a valid object. It needs to be " - "either a LazyMp, a ReferenceState or a " - "HartreeFockSolution_i.") - - if not isinstance(method, AdcMethod): - method = AdcMethod(method) - - if diagonal_precomputed: - if not isinstance(diagonal_precomputed, AmplitudeVector): - raise TypeError("diagonal_precomputed needs to be" - " an AmplitudeVector.") - if diagonal_precomputed.needs_evaluation: - raise ValueError("diagonal_precomputed must already" - " be evaluated.") - - self.timer = Timer() - self.method = method - self.ground_state = hf_or_mp - self.reference_state = hf_or_mp.reference_state - self.mospaces = hf_or_mp.reference_state.mospaces - self.is_core_valence_separated = method.is_core_valence_separated - self.ndim = 2 - self.extra_terms = [] - - self.intermediates = intermediates - if self.intermediates is None: - self.intermediates = Intermediates(self.ground_state) - - # Determine orders of PT in the blocks - if block_orders is None: - block_orders = self.default_block_orders[method.base_method.name] + return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) else: - tmp_orders = self.default_block_orders[method.base_method.name].copy() - tmp_orders.update(block_orders) - block_orders = tmp_orders - - # Sanity checks on block_orders - for block in block_orders.keys(): - if block not in ("ph_ph", "ph_pphh", "pphh_ph", "pphh_pphh"): - raise ValueError(f"Invalid block order key: {block}") - if block_orders["ph_pphh"] != block_orders["pphh_ph"]: - raise ValueError("ph_pphh and pphh_ph should always have " - "the same order") - if block_orders["ph_pphh"] is not None \ - and block_orders["pphh_pphh"] is None: - raise ValueError("pphh_pphh cannot be None if ph_pphh isn't.") - self.block_orders = block_orders - - # Build the blocks and diagonals - - with self.timer.record("build"): - variant = None - if method.is_core_valence_separated: - variant = "cvs" - #self.blocks_ph = {} - if subblock == "elec": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=order, intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - elif subblock == "elec_couple": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_couple", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - elif subblock == "phot_couple": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot_couple", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - elif subblock == "phot": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - elif subblock == "phot2": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot2", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - elif subblock == "phot_couple_inner": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot_couple_inner", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - elif subblock == "phot_couple_edge": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot_couple_edge", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - elif subblock == "elec_couple_inner": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_couple_inner", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - elif subblock == "elec_couple_edge": - blocks = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_couple_edge", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - else: - ValueError("Using this class you have to give the parameter elec_or_phot") - - - - #for block, order in block_orders.items(): - # if order is not None: - # print(block, order) - #for bl in self.blocks_ph.values(): - # print(bl.diagonal) - #for bl in self.blocks_ph: - # print(bl) - #self.__diagonal = sum(bl.diagonal for bl in self.blocks_ph.values() - # if bl.diagonal) - #self.__diagonal.evaluate() - # TODO Rename to self.block in 0.16.0 - self.blocks_ph = {bl: blocks[bl].apply for bl in blocks} - if diagonal_precomputed: - self.__diagonal = diagonal_precomputed - else: - self.__diagonal = sum(bl.diagonal for bl in blocks.values() - if bl.diagonal) - self.__diagonal.evaluate() - self.__init_space_data(self.__diagonal) - #print("following is self.__init_space_data(self.__diagonal)") - #print(self.__init_space_data(self.__diagonal)) - - """ - # here we try to use the whole functionality given for the non-qed case (with the AmplitudeVector class) - # and then use these to build the QED_AmplitudeVector class. Then we do something like - # self.blocks_ph = QED_AmplitudeVector(val0, self.blocks_ph0, val1, self.blocks_ph1) and - # self.diagonal = QED_AmplitudeVector(val0, self.__diagonal0, val1, self.__diagonal1) - - with self.timer.record("build"): - variant = None - if method.is_core_valence_separated: - variant = "cvs" - - # first electronic part - - self.blocks_ph_0 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=order, intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - for block, order in block_orders.items(): - if order is not None: - print(block, order) - self.__diagonal_0 = sum(bl.diagonal for bl in self.blocks_ph_0.values() - if bl.diagonal) - self.__diagonal_0.evaluate() - #self.__init_space_data_0(self.__diagonal_0) - - # now for photonic part - - self.blocks_ph_1 = { # TODO Rename to self.block in 0.16.0 - block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + "_phot", intermediates=self.intermediates, - variant=variant) - for block, order in block_orders.items() if order is not None - } - #for block, order in block_orders.items(): - # if order is not None: - # print(block, order) - self.__diagonal_1 = sum(bl.diagonal for bl in self.blocks_ph_1.values() - if bl.diagonal) - self.__diagonal_1.evaluate() - #self.__init_space_data_0(self.__diagonal_1) - - # build QED_AmplitudeVector - - self.blocks_ph = QED_AmplitudeVector(0, self.blocks_ph_0, 0, self.blocks_ph_1) - self.__diagonal = QED_AmplitudeVector(0, self.__diagonal_0, 0, self.__diagonal_1) - self.__init_space_data_qed(self.__diagonal_0) # here we need to adapt the attributes, defined via this function - # namely self.axis_spaces, self.axis_lengths, self.shape - print(self.axis_spaces) - print(self.axis_lengths) - print(self.shape) - - # since the guesses need the whole matrix object as input, istead of just the diagonals, it is useful to define - # self.elec and self.phot as the the corresponding original matrices - # this is done in one class, since we also require the full matrix for the solver - """ - """ - def __init_space_data_qed(self, diagonal0): - self.__init_space_data(diagonal0) - axis_spaces0 = self.axis_spaces - axis_lengths0 = self.axis_lengths - shape0 = self.shape - self.shape = (shape0[0] * 2 + 1, shape0[0] * 2 + 1) - self.axis_spaces["gs"] = ["o0", "v0"] - self.axis_lengths["gs"] = 1 - # axis_spaces and axis_lengths both are dicts, referring to "ph", "pphh" as a key, so either make extra blocks here, or probably better - # make a longer list in funciton 'axis_blocks', so that later everything for "ph" and "pphh" is just used twice - # also it seems, that self.axis_lengths is not further used, but shape[0] is rather used for that, so not necessary to build self.axis_lengths correctly - """ - - def __iadd__(self, other): - """In-place addition of an :py:class:`AdcExtraTerm` - Parameters - ---------- - other : AdcExtraTerm - the extra term to be added - """ - if not isinstance(other, AdcExtraTerm): - return NotImplemented - if not all(k in self.blocks_ph for k in other.blocks): - raise ValueError("Can only add to blocks of" - " AdcMatrix that already exist.") - for sp in other.blocks: - orig_app = self.blocks_ph[sp] - other_app = other.blocks[sp].apply - - def patched_apply(ampl, original=orig_app, other=other_app): - return sum(app(ampl) for app in (original, other)) - self.blocks_ph[sp] = patched_apply - other_diagonal = sum(bl.diagonal for bl in other.blocks.values() - if bl.diagonal) - self.__diagonal = self.__diagonal + other_diagonal - self.__diagonal.evaluate() - self.extra_terms.append(other) - return self - - def __add__(self, other): - """Addition of an :py:class:`AdcExtraTerm`, creating - a copy of self and adding the term to the new matrix - Parameters - ---------- - other : AdcExtraTerm - the extra term to be added - Returns - ------- - AdcMatrix - a copy of the AdcMatrix with the extra term added - """ - if not isinstance(other, AdcExtraTerm): - return NotImplemented - ret = AdcMatrix(self.method, self.ground_state, - block_orders=self.block_orders, - intermediates=self.intermediates, - diagonal_precomputed=self.diagonal()) - ret += other - return ret - - def __radd__(self, other): - return self.__add__(other) - - def __init_space_data(self, diagonal): - """Update the cached data regarding the spaces of the ADC matrix""" - self.axis_spaces = {} - self.axis_lengths = {} - for block in diagonal.blocks_ph: - self.axis_spaces[block] = getattr(diagonal, block).subspaces - self.axis_lengths[block] = np.prod([ - self.mospaces.n_orbs(sp) for sp in self.axis_spaces[block] - ]) - self.shape = (sum(self.axis_lengths.values()), - sum(self.axis_lengths.values())) - - def __repr__(self): - ret = f"AdcMatrix({self.method.name}, " - for b, o in self.block_orders.items(): - ret += f"{b}={o}, " - return ret + ")" - - def __len__(self): - return self.shape[0] - - @property - def blocks(self): - # TODO Remove in 0.16.0 - return self.__diagonal.blocks - - def has_block(self, block): - warnings.warn("The has_block function is deprecated and " - "will be removed in 0.16.0. " - "Use `in matrix.axis_blocks` in the future.") - return self.block_spaces(block) is not None - - def block_spaces(self, block): - warnings.warn("The block_spaces function is deprecated and " - "will be removed in 0.16.0. " - "Use `matrix.axis_spaces[block]` in the future.") - return { - "s": self.axis_spaces.get("ph", None), - "d": self.axis_spaces.get("pphh", None), - "t": self.axis_spaces.get("ppphhh", None), - }[block] - - @property - def axis_blocks(self): - """ - Return the blocks used along one of the axes of the ADC matrix - (e.g. ['ph', 'pphh']). - """ - return list(self.axis_spaces.keys()) - - def diagonal(self, block=None): - """Return the diagonal of the ADC matrix""" - if block is not None: - warnings.warn("Support for the block argument will be dropped " - "in 0.16.0.") - if block == "s": - return self.__diagonal.ph - if block == "d": - return self.__diagonal.pphh - return self.__diagonal - - def compute_apply(self, block, tensor): - warnings.warn("The compute_apply function is deprecated and " - "will be removed in 0.16.0.") - if block in ("ss", "sd", "ds", "dd"): - warnings.warn("The singles-doubles interface is deprecated and " - "will be removed in 0.16.0.") - block = {"ss": "ph_ph", "sd": "ph_pphh", - "ds": "pphh_ph", "dd": "pphh_pphh"}[block] - return self.block_apply(block, tensor) - - def block_apply(self, block, tensor): - """ - Compute the application of a block of the ADC matrix - with another AmplitudeVector or Tensor. Non-matching blocks - in the AmplitudeVector will be ignored. - """ - if not isinstance(tensor, libadcc.Tensor): - raise TypeError("tensor should be an adcc.Tensor") - - with self.timer.record(f"apply/{block}"): - outblock, inblock = block.split("_") - ampl = AmplitudeVector(**{inblock: tensor}) - ret = self.blocks_ph[block](ampl) - return getattr(ret, outblock) - - @timed_member_call() - def matvec(self, v): - """ - Compute the matrix-vector product of the ADC matrix - with an excitation amplitude and return the result. - """ - return sum(block(v) for block in self.blocks_ph.values()) + TypeError("matvec needs to be invoked with AmplitudeVector or QED_AmplitudeVector") def rmatvec(self, v): # ADC matrix is symmetric @@ -1450,10 +472,10 @@ def compute_matvec(self, ampl): return self.matvec(ampl) def __matmul__(self, other): - if isinstance(other, AmplitudeVector): + if isinstance(other, (AmplitudeVector, QED_AmplitudeVector)): return self.matvec(other) if isinstance(other, list): - if all(isinstance(elem, AmplitudeVector) for elem in other): + if all(isinstance(elem, (AmplitudeVector, QED_AmplitudeVector)) for elem in other): return [self.matvec(ov) for ov in other] return NotImplemented @@ -1818,4 +840,4 @@ def block_view(self, block): raise NotImplementedError("Block-view not yet implemented for " "projected ADC matrices.") # TODO The way to implement this is to ask the inner matrix to - # a block_view and then wrap that in an AdcMatrixProjected. + # a block_view and then wrap that in an AdcMatrixProjected. \ No newline at end of file diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index c2397f78..8aca8d32 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -22,7 +22,6 @@ ## --------------------------------------------------------------------- import warnings -from numpy.lib.function_base import blackman import numpy as np @@ -162,8 +161,6 @@ def __matmul__(self, other): def __forward_to_blocks(self, fname, other): if isinstance(other, AmplitudeVector): if sorted(other.blocks_ph) != sorted(self.blocks_ph): - #print(self.blocks_ph, other.blocks_ph) - #print(other.pphh) raise ValueError("Blocks of both AmplitudeVector objects " f"need to agree to perform {fname}") ret = {k: getattr(tensor, fname)(other[k]) @@ -209,16 +206,10 @@ def __repr__(self): # with missing blocks def __add__(self, other): if isinstance(other, AmplitudeVector): - #print("__add__") allblocks = sorted(set(self.blocks_ph).union(other.blocks_ph)) - #print(allblocks) - #for k in allblocks: - #print(self.get(k, 0), other.get(k, 0)) ret = {k: self.get(k, 0) + other.get(k, 0) for k in allblocks} - #print(ret.items()) ret = {k: v for k, v in ret.items() if v != 0} else: - #print("__add__ else") ret = {k: tensor + other for k, tensor in self.items()} return AmplitudeVector(**ret) @@ -226,21 +217,21 @@ def __radd__(self, other): if isinstance(other, AmplitudeVector): return other.__add__(self) else: - #print("__radd__ else") ret = {k: other + tensor for k, tensor in self.items()} return AmplitudeVector(**ret) -class QED_AmplitudeVector: # it seems all operations, without further specification of pphh part, are unused and can be omitted +class QED_AmplitudeVector: - #def __init__(self, gs=None, elec=None, gs1=None, phot=None): - def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, gs2=None, ph2=None, pphh2=None): # also write this class with *args, **kwargs - # maybe do this via a list e.g. ph=[ph, ph1, ph2] + # TODO: initialize this class with **kwargs, and think of a functionality, which then eases up + # e.g. the matvec function in the AdcMatrix.py for arbitrarily large QED vectors. However, this is + # not necessarily required, since e.g. QED-ADC(3) is very complicated to derive and QED-ADC(1) (with + # just the single dispersion mode) is purely for academic purposes and hence not required to provide + # optimum performance + def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, gs2=None, ph2=None, pphh2=None): - #self.gs = gs_vec(0)#gs) - self.gs1 = gs_vec(gs1) - - self.gs2 = gs_vec(gs2) + self.gs1 = gs1 + self.gs2 = gs2 if pphh != None: self.elec = AmplitudeVector(ph=ph, pphh=pphh) @@ -263,42 +254,7 @@ def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, gs2=None, except AttributeError: # there are no doubles terms --> ADC(0) or ADC(1) pass - - #self.elec0 = self.gs # so the rest of the class doesn't have to be rewritten - #self.phot0 = self.gs1 # so the rest of the class doesn't have to be rewritten - - """ - if len(args) != 0: - if len(args) != 3: - raise AttributeError - else: - self.phot2 = AmplitudeVector(ph=args[1], pphh=args[2]) - self.gs2 = gs_vec(args[0]) - try: - self.ph2 = args[1] - self.pphh2 = args[2] - except AttributeError: - pass - """ - - - #if self.phot is None: - # self.phot = self.elec.zeros_like() #zeros_like(self.elec) - # self.phot0 = 0 - # if self.elec0 is None: - # self.elec0 = 0 - #if self.elec is None: - # self.elec = self.phot.zeros_like() #zeros_like(self.phot) - # self.elec0 = 0 - # if self.phot0 is None: - # self.phot0 = 0 - - #def blocks_ph(self): - # return sorted(self.keys()) - - - def dot(self, invec): def dot_(self, invec): if "pphh" in self.elec.blocks_ph: @@ -309,19 +265,10 @@ def dot_(self, invec): return (self.elec.ph.dot(invec.elec.ph) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) ) if isinstance(invec, list): - list_temp = [] # to return a np.array with different dimensions (gs,ph,pphh), we have to fill them into a list and then convert the list ??? - # even though this list should only be appended by floats, the lower approach didnt work out - #ret = np.array([]) - #(np.append(ret, dot_(self, elem)) for elem in invec) - for elem in invec: - list_temp.append(dot_(self, elem)) - return np.array(list_temp) + return np.array([dot_(self, elem) for elem in invec]) else: return dot_(self, invec) - #def __matmul__(self, invec): - # return self.dot(invec) - def __matmul__(self, other): if isinstance(other, QED_AmplitudeVector): return self.dot(other) @@ -330,26 +277,10 @@ def __matmul__(self, other): if all(isinstance(elem, QED_AmplitudeVector) for elem in other): return self.dot(other) return NotImplemented - - #def __add__(self, invec): # special add function, see AmplitudeVector __add__ - # # this is only used for the summation of gs and gs1 blocks in matvec from AdcMatrix, because the other blocks are covered by AmplitudeVector - # if isinstance(invec, QED_AmplitudeVector): - # - # else: - # return NotImplementedError("__add__ in QED_AmplitudeVector only with QED_AmplitudeVector!") - - #def __add__(self, invec): - # return QED_AmplitudeVector(self.elec0 + invec.elec0, self.elec.__add__(invec.elec), self.phot0 + invec.phot0, self.phot.__add__(invec.phot)) - - #def __radd__(self, invec): - # return QED_AmplitudeVector(self.elec0 + invec.elec0, self.elec.__add__(invec.elec), self.phot0 + invec.phot0, self.phot.__add__(invec.phot)) + def __sub__(self, invec): - if isinstance(invec, QED_AmplitudeVector): - return QED_AmplitudeVector(self.elec.__sub__(invec.elec), self.gs1 - invec.gs1, self.phot.__sub__(invec.phot), - self.gs2 - invec.gs2, self.phot2.__sub__(invec.phot2)) - elif isinstance(invec, (float, int)): # for diagonal - shift in preconditioner.py - # this results in a scalar in block pphh, if pphh is originally None + if isinstance(invec, (float, int)): # for diagonal - shift in preconditioner.py if "pphh" in self.elec.blocks_ph: return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), pphh=self.elec.pphh.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), pphh1=self.phot.pphh.__sub__(invec), @@ -358,19 +289,8 @@ def __sub__(self, invec): return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec)) - def __mul__(self, scalar): - return QED_AmplitudeVector(self.elec.__mul__(scalar), scalar * self.gs1, self.phot.__mul__(scalar)) - - def __rmul__(self, scalar): - return QED_AmplitudeVector(self.elec.__rmul__(scalar), scalar * self.gs1, self.phot.__rmul__(scalar)) def __truediv__(self, other): - #print(type(self.elec), self.elec.ph) - #print(type(other.elec), other.elec.ph) - #if "pphh" in self.elec.blocks_ph: - # return QED_AmplitudeVector(gs=self.elec0 / other.elec0, ph=self.elec.__truediv__(other.elec), - # gs1=self.phot0 / other.phot0, ph1=self.phot.__truediv__(other.phot)) - #else: if isinstance(other, QED_AmplitudeVector): if "pphh" in self.elec.blocks_ph: return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other.elec.ph), pphh=self.elec.pphh.__truediv__(other.elec.pphh), @@ -381,8 +301,6 @@ def __truediv__(self, other): gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph)) elif isinstance(other, (float, int)): - #return QED_AmplitudeVector(gs=self.elec0 / other, ph=self.elec.ph.__truediv__(other), - # gs1=self.phot0 / other, ph1=self.phot.ph.__truediv__(other)) if "pphh" in self.elec.blocks_ph: return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other), pphh=self.elec.pphh.__truediv__(other), gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), pphh1=self.phot.pphh.__truediv__(other), @@ -393,331 +311,39 @@ def __truediv__(self, other): gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) def zeros_like(self): - #if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(self.elec.zeros_like(), gs_vec(0), self.phot.zeros_like(), gs_vec(0), self.phot2.zeros_like()) - #else: - # return QED_AmplitudeVector(self.elec.zeros_like(), gs_vec(0), self.phot.zeros_like()) + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(ph=self.elec.zeros_like().ph, pphh=self.elec.zeros_like().pphh, gs1=0, + ph1=self.phot.zeros_like().ph, pphh1=self.phot.zeros_like().pphh, gs2=0, + ph2=self.phot2.zeros_like().ph, pphh2=self.phot2.zeros_like().pphh) + else: + return QED_AmplitudeVector(ph=self.elec.zeros_like().ph, pphh=None, gs1=0, + ph1=self.phot.zeros_like().ph, pphh1=None, gs2=0, + ph2=self.phot2.zeros_like().ph, pphh2=None) def empty_like(self): - #if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(self.elec.empty_like(), [], self.phot.empty_like(), [], self.phot2.empty_like()) - #else: - # return QED_AmplitudeVector(self.elec.empty_like(), [], self.phot.empty_like()) + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(ph=self.elec.empty_like().ph, pphh=self.elec.empty_like().pphh, gs1=0, + ph1=self.phot.empty_like().ph, pphh1=self.phot.empty_like().pphh, gs2=0, + ph2=self.phot2.empty_like().ph, pphh2=self.phot2.empty_like().pphh) + else: + QED_AmplitudeVector(ph=self.elec.empty_like().ph, pphh=None, gs1=0, + ph1=self.phot.empty_like().ph, pphh1=None, gs2=0, + ph2=self.phot2.empty_like().ph, pphh2=None) def copy(self): - #if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(self.elec.copy(), gs_vec(self.gs1.as_float()), self.phot.copy(), gs_vec(self.gs2.as_float()), self.phot2.copy()) - #else: - # return QED_AmplitudeVector(self.elec.copy(), self.gs1, self.phot.copy(), self.gs2, self.phot2.copy()) + if "pphh" in self.elec.blocks_ph: + return QED_AmplitudeVector(ph=self.elec.copy().ph, pphh=self.elec.copy().pphh, gs1=self.gs1, + ph1=self.phot.copy().ph, pphh1=self.phot.copy().pphh, gs2=self.gs2, + ph2=self.phot2.copy().ph, pphh2=self.phot2.copy().pphh) + else: + QED_AmplitudeVector(ph=self.elec.copy().ph, pphh=None, gs1=self.gs1, + ph1=self.phot.copy().ph, pphh1=None, gs2=self.gs2, + ph2=self.phot2.copy().ph, pphh2=None) def evaluate(self): - #print(self.elec0, self.elec, self.phot0, self.phot) - #print(type(self.elec), type(self.phot), type(self.phot2)) self.elec.evaluate() self.phot.evaluate() self.phot2.evaluate() - #self.gs.as_float() - self.gs1.as_float() - self.gs2.as_float() - #try: - # self.gs.evaluate() - # self.gs1.evaluate() - # self.gs2.evaluate() - #except: - # self.gs - # self.gs1 - # self.gs2 - #if "pphh" in self.elec.blocks_ph: - # self.phot2.evaluate() - # try: - # self.gs2.evaluate() - # except: - # self.gs2 - return self - - - - - -class gs_vec: - # this class only exists, to forward gs and gs1 to the relevant operators - def __init__(self, val): - #print("uses gs_vec init") - self.val = val - #print(type(self.val)) - if isinstance(self.val, AmplitudeVector): - TypeError("gs_vec got invoked with AmplitudeVector type") - #try: - # self.val = float(arg) - #except: - # raise NotImplementedError("gs_vec needs to be given just one number," - # "which has to be float convertible") - def __mul__(self, invec): - if isinstance(invec, (float, int)): - #print("uses gs_vec __mul__") - return self.val * invec - elif isinstance(invec, AmplitudeVector): - return invec.__mul__(self.val) - elif isinstance(invec, gs_vec): - return self.val * invec.val - - def __rmul__(self, scalar): - #print("uses gs_vec __rmul__") - return self.val * scalar - - def __add__(self, scalar): - if self.val == None or scalar == None: - return self.val - else: - #print("uses add gs_vec", self.val, scalar) - #print(type(self.val)) - return gs_vec(self.val + scalar) - - def __radd__(self, scalar): - return self.__add__(scalar) - #if self.val == None: - # return scalar - #else: - # #print("uses radd gs_vec", self.val, scalar) - # return self.val + scalar - - def __sub__(self, scalar): - if isinstance(scalar, gs_vec): - return self.val - scalar.val - elif isinstance(scalar, (float, int)): - return self.val - scalar - - def __truediv__(self, scalar): - if isinstance(scalar, gs_vec): - return self.val / scalar.val - elif isinstance(scalar, (float, int)): - return self.val / scalar - - def as_float(self): - return self.val - - def evaluate(self): - return self.val - - - - - - - -""" -class QED_AmplitudeVector(dict): - # This vector contains the structure (ph, ph1) or (ph, pphh, ph1, pphh1), where the - # zero excitation space is included at the beginning of each ph (see matrix.py). - # The upper and the lower half of the vector have photonic intermediate states 0 and 1, - # respectively, and we abbreviate ph0 and pphh0 by ph and pphh. - - # we first implement the <0|0> block, but with the groundstate terms, gs = grounstate - def __init__(self, *args, **kwargs): - """ - #Construct an AmplitudeVector. Typical use cases are - #``AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles)``. -""" - if args: - warnings.warn("Using the list interface of AmplitudeVector is " - "deprecated and will be removed in version 0.16.0. Use " - "AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles) " - "instead.") - if len(args) == 2: - super().__init__(gs=args[0], ph=args[1]) - elif len(args) == 4: - super().__init__(gs=args[0], ph=args[1], pphh1=args[2]) - else: - super().__init__(**kwargs) - - def __getattr__(self, key): - if self.__contains__(key): - return self.__getitem__(key) - raise AttributeError - - def __setattr__(self, key, item): - if self.__contains__(key): - return self.__setitem__(key, item) - raise AttributeError - - @property - def blocks(self): - warnings.warn("The blocks attribute will change behaviour in 0.16.0.") - if sorted(self.blocks_ph) == ["gs", "ph", "pphh"]: - return ["g", "s", "d"] - if sorted(self.blocks_ph) == ["gs", "ph"]: - return ["g", "s"] - if sorted(self.blocks_ph) == ["pphh"]: - return ["d"] - elif sorted(self.blocks_ph) == ["ph"]: - return ["s"] - elif sorted(self.blocks_ph) == ["gs"]: - return ["g"] - elif sorted(self.blocks_ph) == []: - return [] - else: - raise NotImplementedError(self.blocks_ph) - - @property - def blocks_ph(self): # despite the name, this function can be applied to all blocks - """ - #Return the blocks which are used inside the vector. - #Note: This is a temporary name. The attribute will be removed in 0.16.0. -""" - return sorted(self.keys()) - - def __getitem__(self, index): - if index in (0, 1, 2, "g", "s", "d"): - warnings.warn("Using the list interface of AmplitudeVector is " - "deprecated and will be removed in version 0.16.0. Use " - "block labels like 'ph', 'pphh' instead.") - if index in (0, "g"): - return self.__getitem__("gs") - elif index in (1, "s"): - return self.__getitem__("ph") - elif index in (2, "d"): - return self.__getitem__("pphh") - #elif index in (3, "d1"): - # return self.__getitem__("pphh1") - else: - raise KeyError(index) - else: - return super().__getitem__(index) - - def __setitem__(self, index, item): - if index in (0, 1, 2, "g", "s", "d"): - warnings.warn("Using the list interface of AmplitudeVector is " - "deprecated and will be removed in version 0.16.0. Use " - "block labels like 'ph', 'pphh' instead.") - if index in (0, "g"): - return self.__setitem__("gs", item) - elif index in (1, "s"): - return self.__setitem__("ph", item) - elif index in (2, "d"): - return self.__setitem__("pphh", item) - #elif index in (3, "d1"): - # return self.__setitem__("pphh1", item) - else: - raise KeyError(index) - else: - return super().__setitem__(index, item) - - def copy(self): - """#Return a copy of the AmplitudeVector -""" - return AmplitudeVector(**{k: t.copy() for k, t in self.items()}) - - def evaluate(self): - for t in self.values(): - t.evaluate() - return self - - def ones_like(self): - """#Return an empty AmplitudeVector of the same shape and symmetry -""" - return AmplitudeVector(**{k: t.ones_like() for k, t in self.items()}) - - def empty_like(self): - """#Return an empty AmplitudeVector of the same shape and symmetry -""" - return AmplitudeVector(**{k: t.empty_like() for k, t in self.items()}) - - def nosym_like(self): - """#Return an empty AmplitudeVector of the same shape and symmetry -""" - return AmplitudeVector(**{k: t.nosym_like() for k, t in self.items()}) - - def zeros_like(self): - """#Return an AmplitudeVector of the same shape and symmetry with - #all elements set to zero -""" - return AmplitudeVector(**{k: t.zeros_like() for k, t in self.items()}) - - def set_random(self): - for t in self.values(): - t.set_random() - return self - - def dot(self, other): - """#Return the dot product with another AmplitudeVector - #or the dot products with a list of AmplitudeVectors. - #In the latter case a np.ndarray is returned. -""" - if isinstance(other, list): - # Make a list where the first index is all singles parts, - # the second is all doubles parts and so on - return sum(self[b].dot([av[b] for av in other]) for b in self.keys()) - else: - return sum(self[b].dot(other[b]) for b in self.keys()) - - def __matmul__(self, other): - if isinstance(other, AmplitudeVector): - return self.dot(other) - if isinstance(other, list): - if all(isinstance(elem, AmplitudeVector) for elem in other): - return self.dot(other) - return NotImplemented - - def __forward_to_blocks(self, fname, other): - if isinstance(other, AmplitudeVector): - if sorted(other.blocks_ph) != sorted(self.blocks_ph): - raise ValueError("Blocks of both AmplitudeVector objects " - f"need to agree to perform {fname}") - ret = {k: getattr(tensor, fname)(other[k]) - for k, tensor in self.items()} - else: - ret = {k: getattr(tensor, fname)(other) for k, tensor in self.items()} - if any(r == NotImplemented for r in ret.values()): - return NotImplemented - else: - return AmplitudeVector(**ret) - - def __mul__(self, other): - return self.__forward_to_blocks("__mul__", other) - - def __rmul__(self, other): - return self.__forward_to_blocks("__rmul__", other) - - def __sub__(self, other): - return self.__forward_to_blocks("__sub__", other) - - def __rsub__(self, other): - return self.__forward_to_blocks("__rsub__", other) - - def __truediv__(self, other): - return self.__forward_to_blocks("__truediv__", other) - - def __imul__(self, other): - return self.__forward_to_blocks("__imul__", other) - - def __iadd__(self, other): - return self.__forward_to_blocks("__iadd__", other) - - def __isub__(self, other): - return self.__forward_to_blocks("__isub__", other) - - def __itruediv__(self, other): - return self.__forward_to_blocks("__itruediv__", other) - - def __repr__(self): - return "AmplitudeVector(" + "=..., ".join(self.blocks_ph) + "=...)" - - # __add__ is special because we want to be able to add AmplitudeVectors - # with missing blocks - def __add__(self, other): - if isinstance(other, AmplitudeVector): - allblocks = sorted(set(self.blocks_ph).union(other.blocks_ph)) - ret = {k: self.get(k, 0) + other.get(k, 0) for k in allblocks} - ret = {k: v for k, v in ret.items() if v != 0} - else: - ret = {k: tensor + other for k, tensor in self.items()} - return AmplitudeVector(**ret) - - def __radd__(self, other): - if isinstance(other, AmplitudeVector): - return other.__add__(self) - else: - ret = {k: other + tensor for k, tensor in self.items()} - return AmplitudeVector(**ret) - -""" \ No newline at end of file + self.gs1 + self.gs2 + return self \ No newline at end of file diff --git a/adcc/ExcitedStates.py b/adcc/ExcitedStates.py index 853baf36..bc5924a2 100644 --- a/adcc/ExcitedStates.py +++ b/adcc/ExcitedStates.py @@ -35,6 +35,7 @@ from .OneParticleOperator import product_trace from .ElectronicTransition import ElectronicTransition from .FormatDominantElements import FormatDominantElements +from .AmplitudeVector import QED_AmplitudeVector class EnergyCorrection: @@ -411,7 +412,7 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): If ``None`` an automatic selection will be made. """ eV = constants.value("Hartree energy in eV") - vector_format = FormatExcitationVector(self.matrix.elec, tolerance=tolerance, # here self.matrix.elec, due to qed + vector_format = FormatExcitationVector(self.matrix, tolerance=tolerance, index_format=index_format) #for qed @@ -420,7 +421,9 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): # which however is the standard output for the excitation vectors, so only the .elec vector is accessible this way # Optimise the formatting by pre-inspecting all tensors for tensor in self.excitation_vector: - vector_format.optimise_formatting(tensor.elec) + if isinstance(tensor, QED_AmplitudeVector): + tensor = tensor.elec + vector_format.optimise_formatting(tensor) # Determine width of a line lw = 2 + vector_format.linewidth @@ -428,6 +431,8 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): ret = separator for i, vec in enumerate(self.excitation_vector): + if isinstance(vec, QED_AmplitudeVector): + vec = vec.elec ene = self.excitation_energy[i] eev = ene * eV head = f"State {i:3d} , {ene:13.7g} au" @@ -435,7 +440,7 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): head += f", {eev:13.7} eV" ret += "| " + head + (lw - len(head) - 2) * " " + " |\n" ret += separator - formatted = vector_format.format(vec.elec).replace("\n", " |\n| ") + formatted = vector_format.format(vec).replace("\n", " |\n| ") ret += "| " + formatted + " |\n" if i != len(self.excitation_vector) - 1: ret += "\n" diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 54bf6330..a06a1349 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -728,7 +728,7 @@ def apply(ampl): return AmplitudeVector(ph=( sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) - - mp.qed_t1_df(b.ov) * ampl.gs1.as_float()) # gs_ph block + - mp.qed_t1_df(b.ov) * ampl.gs1)#.as_float()) # gs_ph block )) #add_axis0 = - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov) @@ -769,7 +769,7 @@ def apply(ampl): return AmplitudeVector(ph=( sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) - - mp.qed_t1_df(b.ov) * ampl.gs2.as_float()) # gs_ph block + - mp.qed_t1_df(b.ov) * ampl.gs2)#.as_float()) # gs_ph block )) return AdcBlock(apply, diagonal) @@ -1619,10 +1619,10 @@ def apply(ampl): #+ einsum("ijab,ia->jb", einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov), ampl.ph1) #+ einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part - + gs_part * ampl.gs1.as_float() + + gs_part * ampl.gs1#.as_float() + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs1.as_float()) # gs_ph block # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs1)#.as_float()) # gs_ph block # 1. order #+ sqrt(omega / 2) * ( #gs_ph part # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) # + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) @@ -1892,7 +1892,7 @@ def apply(ampl): + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part - + gs_part * ampl.gs2.as_float() + + gs_part * ampl.gs2#.as_float() #+ sqrt(omega / 2) * ( #gs_ph part # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) # + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) @@ -1912,7 +1912,7 @@ def apply(ampl): #) + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs2.as_float()) # gs_ph block # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs2)#.as_float()) # gs_ph block # 1. order )) return AdcBlock(apply, diagonal) diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index 52c6cb2b..367b9f99 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -151,7 +151,7 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): print("norm squared of amplitude.elec", ampl_elec_norm, " norm squared phot", ampl_phot_norm) print("norm squared of amplitude", amplitude @ amplitude) print("beware, that we normalize .elec, before giving it to the oscillator strength routine") - print("amplitude.gs1 is {} and amplitude.gs2 is {}".format(amplitude.gs1.as_float(), amplitude.gs2.as_float())) + print("amplitude.gs1 is {} and amplitude.gs2 is {}".format(amplitude.gs1, amplitude.gs2))#.as_float(), amplitude.gs2.as_float())) normalized_ampl = amplitude.elec / sqrt(ampl_elec_norm) ret = DISPATCH[method.name](ground_state, normalized_ampl, intermediates) else: diff --git a/adcc/functions.py b/adcc/functions.py index d6c474b4..1f494265 100644 --- a/adcc/functions.py +++ b/adcc/functions.py @@ -121,21 +121,14 @@ def lincomb(coefficients, tensors, evaluate=False): block: lincomb(coefficients, [ten[block] for ten in tensors], evaluate=evaluate) for block in tensors[0].blocks_ph - }) # rewrite this for QED_AmplitudeVector, where ph,pphh,ph1,pphh1 still undergo libadcc.Tensor stuff, while gs and gs1 have to be treated separately + }) elif isinstance(tensors[0], QED_AmplitudeVector): - #for qed_vec in tensors: - #print(tensors.gs, tensors.elec, tensors.gs1, tensors.phot) - # give .elec and .phot to lin_comb_strict via AmplitudeVector instance - # then coeff[0] * .gs[from first tensor/vector] + coeff[1] * .gs[from second tensor/vector] + ... - # same for .gs1 - #gs_part = 0 gs1_part = 0 gs2_part = 0 phot2_list = [] elec_list = [] phot_list = [] for coeff_ind, ten in enumerate(tensors): - #gs_part += coefficients[coeff_ind] * ten.gs gs1_part += coefficients[coeff_ind] * ten.gs1 gs2_part += coefficients[coeff_ind] * ten.gs2 elec_list.append(ten.elec) @@ -145,20 +138,10 @@ def lincomb(coefficients, tensors, evaluate=False): phot_part = lincomb(coefficients, phot_list, evaluate=evaluate) phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) if "pphh" in elec_part.blocks_ph: - # gs2_part = 0 - # phot2_list = [] - # for coeff_ind, ten in enumerate(tensors): - # gs2_part += coefficients[coeff_ind] * ten.gs2 - # phot2_list.append(ten.phot2) - # phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) - #print(qed_vec.ph) - #print(coefficients) - #print(tensors[0][block]) - #NotImplementedError("lincomb does not exist for list of QED_AmplitudeVectors yet") elif not isinstance(tensors[0], libadcc.Tensor): raise TypeError("Tensor type not supported") diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index db7fdd37..8dee5c8b 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -33,7 +33,8 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, spin_block_symmetrisation="none", - degeneracy_tolerance=1e-14, max_diagonal_value=1000): + degeneracy_tolerance=1e-14, max_diagonal_value=1000, + qed_subblock=None): """ Obtain guesses by inspecting a block of the diagonal of the passed ADC matrix. The symmetry of the returned vectors is already set-up properly. @@ -87,14 +88,26 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, guessfunction = guesses_from_diagonal_singles elif block == "pphh": guessfunction = guesses_from_diagonal_doubles - elif block == "gs": - guessfunction = guesses_from_diagonal_gs + #elif block == "gs": + # guessfunction = guesses_from_diagonal_gs #elif block == "pphh_phot": # guessfunction = guesses_from_diagonal_doubles_phot else: raise ValueError(f"Don't know how to generate guesses for block {block}") - return guessfunction(matrix, n_guesses, spin_change, + if qed_subblock == None: + diag = matrix.diagonal() + elif qed_subblock == "elec": + diag = matrix.diagonal().elec + elif qed_subblock == "phot": + diag = matrix.diagonal().phot + elif qed_subblock == "phot2": + diag = matrix.diagonal().phot2 + else: + KeyError("qed_subblock can only be None, elec, phot or phot2" + "for guesses from diagonal") + + return guessfunction(matrix, n_guesses, diag, spin_change, spin_block_symmetrisation, degeneracy_tolerance, max_diagonal_value) @@ -201,7 +214,7 @@ def telem_nospin(telem): return res -def guesses_from_diagonal_singles(matrix, n_guesses, spin_change=0, +def guesses_from_diagonal_singles(matrix, n_guesses, diag, spin_change=0, spin_block_symmetrisation="none", degeneracy_tolerance=1e-14, max_diagonal_value=1000): @@ -224,8 +237,9 @@ def pred_singles(telem): and telem.spin_change == spin_change and abs(telem.value) <= max_diagonal_value) + elements = find_smallest_matching_elements( - pred_singles, matrix.diagonal().ph, motrans, n_guesses, + pred_singles, diag.ph, motrans, n_guesses, degeneracy_tolerance=degeneracy_tolerance ) if len(elements) == 0: @@ -275,14 +289,13 @@ def telem_nospin(telem): return [evaluate(v / np.sqrt(v @ v)) for v in ret[:ivec]] -def guesses_from_diagonal_gs(matrix, n_guesses, spin_change=0, - spin_block_symmetrisation="none", - degeneracy_tolerance=1e-14): - print("care...guesses from diagonal still grant 0 for groundstate") - return 0 +#def guesses_from_diagonal_gs(matrix, n_guesses, diag, spin_change=0, +# spin_block_symmetrisation="none", +# degeneracy_tolerance=1e-14): +# return 0 -def guesses_from_diagonal_doubles(matrix, n_guesses, spin_change=0, +def guesses_from_diagonal_doubles(matrix, n_guesses, diag, spin_change=0, spin_block_symmetrisation="none", degeneracy_tolerance=1e-14, max_diagonal_value=1000): @@ -310,7 +323,7 @@ def guesses_from_diagonal_doubles(matrix, n_guesses, spin_change=0, ret = ret[:n_found] # Filter out elements above the noted diagonal value - diagonal_elements = [ret_d.pphh.dot(matrix.diagonal().pphh * ret_d.pphh) + diagonal_elements = [ret_d.pphh.dot(diag.pphh * ret_d.pphh) for ret_d in ret] return [ret[i] for (i, elem) in enumerate(diagonal_elements) if elem <= max_diagonal_value] diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 0705e99d..aa68596a 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -22,9 +22,7 @@ ## --------------------------------------------------------------------- import sys import warnings -from attr import has import numpy as np -from numpy.lib.function_base import blackman import scipy.linalg as la import scipy.sparse.linalg as sla @@ -141,7 +139,6 @@ def callback(state, identifier): # The current subspace SS = state.subspace_vectors - #print("from davidson...SS[0].pphh", SS[0].pphh) # The matrix A projected into the subspace # as a continuous array. Only the view @@ -155,9 +152,6 @@ def callback(state, identifier): callback(state, "start") state.timer.restart("iteration") - #print(matrix.shape) - #print(SS) - with state.timer.record("projection"): # Initial application of A to the subspace Ax = evaluate(matrix @ SS) @@ -168,46 +162,6 @@ def callback(state, identifier): assert len(SS) >= n_block assert len(SS) <= max_subspace - """ - if len(SS) == max_subspace: - print("subspace reached full matrix dimension -> starting to diagonalize full matrix from already obtained SS") - # just copy paste of some of the davidson functions - converged = False - while not converged: - Ass = Ass_cont[:n_ss_vec, :n_ss_vec] # Increase the work view size - for i in range(n_block): - #print(type(Ax[-n_block + i]), Ax[-n_block + i]) - #print(type(SS), SS) - Ass[:, -n_block + i] = Ax[-n_block + i] @ SS - Ass[-n_block:, :] = np.transpose(Ass[:, -n_block:]) - - rvals, rvecs = la.eigh(Ass) - - def form_residual(rval, rvec): - coefficients = np.hstack((rvec, -rval * rvec)) - return lincomb(coefficients, Ax + SS, evaluate=True) - - residuals = [form_residual(rvals[i], v) - for i, v in enumerate(np.transpose(rvecs))] - residual_norms = np.array([r @ r for r in residuals]) - - residual_converged = residual_norms < 1e-9 # same as for davidson - converged = np.all(residual_converged) - - if converged: - print("converged eigenvalues = ", rvals) - break - else: - print("intermediate eigenvalues = ", rvals) - #for i in np.arange(max_subspace): - #print("rvec ", rvecs[i]) - #print("residual", residuals[i]) - new_guesses_tmp = [rvecs[i] - residuals[i] for i in np.arange(max_subspace)] - new_guesses = [vec / np.sqrt(vec @ vec) for vec in new_guesses_tmp] - Ax = evaluate(matrix @ new_guesses) - """ - - # Project A onto the subspace, keeping in mind # that the values Ass[:-n_block, :-n_block] are already valid, @@ -215,8 +169,6 @@ def form_residual(rval, rvec): with state.timer.record("projection"): Ass = Ass_cont[:n_ss_vec, :n_ss_vec] # Increase the work view size for i in range(n_block): - #print(type(Ax[-n_block + i]), Ax[-n_block + i]) - #print(type(SS), SS) Ass[:, -n_block + i] = Ax[-n_block + i] @ SS Ass[-n_block:, :] = np.transpose(Ass[:, -n_block:]) @@ -224,29 +176,17 @@ def form_residual(rval, rvec): # and the associated ritz vector as well as residual with state.timer.record("rayleigh_ritz"): if Ass.shape == (n_block, n_block): - #print("davidson eigh", la.eigh(Ass)[0]) rvals, rvecs = la.eigh(Ass) # Do a full diagonalisation - #print("davidson eigh", rvals) - #eigenvecs = [lincomb(v, SS, evaluate=True) - # for v in np.transpose(rvecs)] - #for eigv in eigenvecs: - #print("norm of eigenvector", np.sqrt(eigv @ eigv)) else: # TODO Maybe play with precision a little here # TODO Maybe use previous vectors somehow v0 = None - #print("davidson eigh", la.eigh(Ass)[0]) rvals, rvecs = sla.eigsh(Ass, k=n_block, which=which, v0=v0) with state.timer.record("residuals"): # Form residuals, A * SS * v - λ * SS * v = Ax * v + SS * (-λ*v) def form_residual(rval, rvec): - #print("norm of residual vector = ", np.sqrt(rvec @ rvec)) - #rvec = rvec / np.sqrt(rvec @ rvec) coefficients = np.hstack((rvec, -rval * rvec)) - #print(type(coefficients), coefficients) - #print(type(SS), SS) - #print(type(Ax), Ax) return lincomb(coefficients, Ax + SS, evaluate=True) residuals = [form_residual(rvals[i], v) for i, v in enumerate(np.transpose(rvecs))] @@ -257,15 +197,13 @@ def form_residual(rval, rvec): state.eigenvalues = rvals[epair_mask] state.residuals = [residuals[i] for i in epair_mask] state.residual_norms = np.array([r @ r for r in state.residuals]) - # building the eigenvectors here is just for debugging purposes - eigenvecs = [lincomb(v, SS, evaluate=True) - for i, v in enumerate(np.transpose(rvecs)) - if i in epair_mask] - for eigv in eigenvecs: - print("norm of eigenvector", np.sqrt(eigv @ eigv)) - #print("orthogonality check 2,3 , 2,4 and 3,4", np.sqrt(eigenvecs[1] @ eigenvecs[2]), - # np.sqrt(eigenvecs[1] @ eigenvecs[3]), np.sqrt(eigenvecs[2] @ eigenvecs[3])) - #print("eigenvector.ph = ", eigv.ph) + if hasattr(matrix.reference_state, "print_eigvec_norms"): + # building the eigenvectors here is just for debugging purposes + eigenvecs = [lincomb(v, SS, evaluate=True) + for i, v in enumerate(np.transpose(rvecs)) + if i in epair_mask] + for eigv in eigenvecs: + print("norm of eigenvector", np.sqrt(eigv @ eigv)) # TODO This is misleading ... actually residual_norms contains # the norms squared. That's also the used e.g. in adcman to # check for convergence, so using the norm squared is fine, @@ -333,49 +271,6 @@ def form_residual(rval, rvec): # Explicitly symmetrise the new vectors if requested if explicit_symmetrisation: - #print(explicit_symmetrisation) - #tmp_list = [] - #tmp2 = [] - #for vec in preconds: - # tmp_list.append(vec.elec) - # tmp_list.append(vec.phot) - # tmp_list.append(vec.phot2) - #explicit_symmetrisation.symmetrise(tmp_list) - #for i, vec in enumerate(preconds): - # tmp2.append(QED_AmplitudeVector(gs=vec.gs, ph=vec.elec.ph, pphh=tmp_list[3*i].pphh, - # gs1=vec.gs1, ph1=vec.phot.ph, pphh1=tmp_list[3*i + 1].pphh, - # gs2=vec.gs2, ph2=vec.phot2.ph, pphh2=tmp_list[3*i + 2].pphh)) - #preconds = tmp2 - #if isinstance(preconds[0], QED_AmplitudeVector): - # - #else: - #if "pphh" in preconds[0].elec.blocks_ph: - # explicit_symmetrisation.symmetrise. - # for ind, vec in enumerate(preconds): - # preconds[ind] = QED_AmplitudeVector(gs=vec.gs, ph=vec.elec.ph, pphh=evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)), - # gs1=vec.gs1, ph1=vec.phot.ph, pphh1=evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)), - # gs2=vec.gs2, ph2=vec.phot2.ph, pphh2=evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh))) - - #temp = preconds - #explicit_symmetrisation.symmetrise(preconds) - #new_temp = explicit_symmetrisation.symmetrise(temp) - #if new_temp[0].elec.pphh == preconds[0].elec.pphh: - # print("in davidson symmetrise yields no change in elec") - #else: - # print("in davidson symmetrise yields a change in elec") - #else: # this is unnecessary, since symmetrization only applies for double excitation space - #tmp = preconds - #explicit_symmetrisation.symmetrise(tmp) - #if tmp[0].elec.pphh == preconds[0].elec.pphh: - # print("nothing changed") - #else: - # print("something changed") - #tmp2 = preconds[0].elec - #explicit_symmetrisation.symmetrise(tmp2) - #if tmp2.pphh == preconds[0].elec.pphh: - # print("nothing changed for elec only") - #else: - # print("something changed for elec only") explicit_symmetrisation.symmetrise(preconds) @@ -474,8 +369,6 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, raise TypeError("matrix is not of type AdcMatrixlike") for guess in guesses: if not isinstance(guess, (AmplitudeVector, QED_AmplitudeVector)): - #print(guess) - #print(guesses) raise TypeError("One of the guesses is not of type AmplitudeVector") if preconditioner is not None and isinstance(preconditioner, type): @@ -492,13 +385,14 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, if not max_subspace: # TODO Arnoldi uses this: # max_subspace = max(2 * n_ep + 1, 20) - # max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) # original - print("for qed-adc we use double the standard max_subspace, due to convergence problems," - "if the doubly excited photonic space contributes to the states") if hasattr(matrix.reference_state, "full_diagonalization"): max_subspace = len(guesses) - else: + elif hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): + print("for qed-adc we use double the standard max_subspace") + #"if the doubly excited photonic space contributes to the states") max_subspace = max(12 * n_ep, 20, 10 * len(guesses)) + else: + max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) def convergence_test(state): state.residuals_converged = state.residual_norms < conv_tol @@ -513,7 +407,6 @@ def convergence_test(state): "".format(conv_tol, matrix.shape[1] * np.finfo(float).eps) )) - #print("this is from davidson...guesses[0].pphh = ", guesses[0].pphh) state = DavidsonState(matrix, guesses) davidson_iterations(matrix, state, max_subspace, max_iter, n_ep=n_ep, is_converged=convergence_test, diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index 7316bd49..662977aa 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -23,8 +23,7 @@ from libadcc import amplitude_vector_enforce_spin_kind from adcc import evaluate -from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector, gs_vec -import numpy as np +from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector # TODO # This interface is not that great and leads to duplicate information @@ -65,70 +64,27 @@ def symm_subroutine(vec): for b in vec.blocks_ph: if b not in self.symmetrisation_functions: continue - #print(b) vec[b] = evaluate(self.symmetrisation_functions[b](vec[b])) return vec if isinstance(new_vectors, (AmplitudeVector, QED_AmplitudeVector)): return self.symmetrise([new_vectors])[0] elif isinstance(new_vectors[0], QED_AmplitudeVector): - # we dont have to symmetrise the gs blocks...actually only the pphh blocks are symmetrised here - if "pphh" in new_vectors[0].elec.blocks_ph: # not sure if this is necessary, since symm should only be requested for doubles after all - #print("non explicit symm for qed vec is called") + if "pphh" in new_vectors[0].elec.blocks_ph: for vec in new_vectors: vec.elec = self.symmetrise([vec.elec])[0] vec.phot = self.symmetrise([vec.phot])[0] vec.phot2 = self.symmetrise([vec.phot2])[0] - #test_list = new_vectors - #for ind, vec in enumerate(new_vectors): - #if vec.elec.pphh != evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)): #vec.elec.pphh != symm_subroutine(vec.elec).pphh: - # print("something changed") - #else: - # print("nothing changed") - #vec = QED_AmplitudeVector(gs=vec.gs, ph=vec.elec.ph, pphh=evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)), - # gs1=vec.gs1, ph1=vec.phot.ph, pphh1=evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)), - # gs2=vec.gs2, ph2=vec.phot2.ph, pphh2=evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh))) - # test_list[ind].elec = self.symmetrise([vec.elec])[0] #evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)) - # test_list[ind].phot = self.symmetrise([vec.phot])[0] - # test_list[ind].phot2 = self.symmetrise([vec.phot2])[0] - #test_list[ind].phot = symm_subroutine(vec.phot) #evaluate(self.symmetrisation_functions["pphh"](vec.phot.pphh)) - #test_list[ind].phot2 = symm_subroutine(vec.phot2) #evaluate(self.symmetrisation_functions["pphh"](vec.phot2.pphh)) - #if test_list[0].elec.pphh == new_vectors[0].elec.pphh: - # print("in symm nothing changed") - # if new_vectors[0].elec.pphh != evaluate(self.symmetrisation_functions["pphh"](test_list[0].elec.pphh)): - # print("but something changed for the symmetrization, which was not passed to the QED_AmplitudeVector") - #else: - # print("in symm something changed") - #if vec.elec.pphh == evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)): - # print("symm still yields no change") - #print(type(evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh))), evaluate(self.symmetrisation_functions["pphh"](vec.elec.pphh)).shape) - #if new_vec.elec == symm_subroutine(vec.elec): - # print("correctly changed in symmetrise function") - #elif new_vec.elec == vec.elec: - # print("nothing changed in symmetrise function") - #else: - # print("not correctly changed in symmetrise function") - # diff = new_vec.elec - symm_subroutine(vec.elec) - # print("squared norm of difference with symmetrise = ", np.sqrt(diff @ diff)) - # diff2 = new_vec.elec - vec.elec - # print("squared norm of difference without symmetrise = ", np.sqrt(diff2 @ diff2)) - # diff3 = vec.elec - symm_subroutine(vec.elec) - # print("squared norm of difference between no symmetrise and symmetrise = ", np.sqrt(diff3 @ diff3)) # why is this zero?????????? - # #print(type(new_vec.elec.pphh), type(symm_subroutine(vec.elec).pphh)) - #vec.elec = symm_subroutine(vec.elec) - #vec.phot = symm_subroutine(vec.phot) - #vec.phot2 = symm_subroutine(vec.phot2) elif isinstance(new_vectors[0], AmplitudeVector): - #print("non explicit symm for ampl vec is called") for vec in new_vectors: vec = symm_subroutine(vec) - #if not isinstance(vec, AmplitudeVector): - # raise TypeError("new_vectors has to be an " - # "iterable of AmplitudeVector") - #for b in vec.blocks_ph: - # if b not in self.symmetrisation_functions: - # continue - # vec[b] = evaluate(self.symmetrisation_functions[b](vec[b])) + if not isinstance(vec, AmplitudeVector): + raise TypeError("new_vectors has to be an " + "iterable of AmplitudeVector") + for b in vec.blocks_ph: + if b not in self.symmetrisation_functions: + continue + vec[b] = evaluate(self.symmetrisation_functions[b](vec[b])) return new_vectors @@ -140,7 +96,6 @@ class IndexSpinSymmetrisation(IndexSymmetrisation): def __init__(self, matrix, enforce_spin_kind="singlet"): super().__init__(matrix) self.enforce_spin_kind = enforce_spin_kind - #print("index spin symm class is used") def symmetrise(self, new_vectors): if isinstance(new_vectors, (AmplitudeVector, QED_AmplitudeVector)): @@ -153,7 +108,6 @@ def symmetrise(self, new_vectors): for vec in new_vectors: if isinstance(vec, QED_AmplitudeVector): if "pphh" in vec.elec.blocks_ph: - #print("explicit symm for qed vec is called") amplitude_vector_enforce_spin_kind( vec.elec.pphh, "d", self.enforce_spin_kind ) @@ -167,7 +121,6 @@ def symmetrise(self, new_vectors): # the other blocks are not yet implemented # or nothing needs to be done ("ph" block) elif "pphh" in vec.blocks_ph: - #print("explicit symm for ampl vec is called") # TODO: Note that the "d" is needed here because the C++ side # does not yet understand ph and pphh amplitude_vector_enforce_spin_kind( diff --git a/adcc/solver/lanczos.py b/adcc/solver/lanczos.py index 94a9aa18..4fcbfca2 100644 --- a/adcc/solver/lanczos.py +++ b/adcc/solver/lanczos.py @@ -210,21 +210,19 @@ def callback(state, identifier): state.eigenvalues = rvals[epair_mask] state.residual_norms = eigenpair_error[epair_mask] converged = np.all(is_rval_converged[epair_mask]) - #if state.eigenvalues[0] <= 0.5: - # print("for testing purposes we end the iterations here, and print the current states, since one eigenvalue is below 0.5") - # converged = True # TODO For consistency with the Davidson the residual norms are squared # again to give output in the same order of magnitude. state.residual_norms = state.residual_norms**2 - #tmp_state = amend_true_residuals(state, subspace, rvals, - # rvecs, epair_mask) + if hasattr(iterator.matrix.reference_state, "print_eigvec_norms"): + tmp_state = amend_true_residuals(state, subspace, rvals, + rvecs, epair_mask) - #print("norms of eigenvectors") - #for vec in tmp_state.eigenvectors: - # print(np.sqrt(vec @ vec)) + print("norms of eigenvectors") + for vec in tmp_state.eigenvectors: + print(np.sqrt(vec @ vec)) callback(state, "next_iter") state.timer.restart("iteration") diff --git a/adcc/workflow.py b/adcc/workflow.py index 36d00538..54274da4 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -20,7 +20,6 @@ ## along with adcc. If not, see . ## ## --------------------------------------------------------------------- -import enum import sys import warnings import numpy as np @@ -40,7 +39,7 @@ from .solver.davidson import jacobi_davidson from .solver.explicit_symmetrisation import (IndexSpinSymmetrisation, IndexSymmetrisation) -from .AmplitudeVector import QED_AmplitudeVector, gs_vec +from .AmplitudeVector import QED_AmplitudeVector __all__ = ["run_adc"] @@ -395,11 +394,12 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", if guesses is None: if n_guesses is None: n_guesses = estimate_n_guesses(matrix, n_states, n_guesses_per_state) - #guesses = obtain_guesses_by_inspection(matrix, n_guesses, kind, # guesses are obtained here - # n_guesses_doubles) - guesses = obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, # guesses are obtained here - n_guesses_doubles) - #print(guesses) + if hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): + guesses = obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, + n_guesses_doubles) + else: + guesses = obtain_guesses_by_inspection(matrix, n_guesses, kind, + n_guesses_doubles) else: if len(guesses) < n_states: raise InputError("Less guesses provided via guesses (== {}) " @@ -412,7 +412,6 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", warnings.warn("Ignoring n_guesses_doubles parameter, since guesses " "are explicitly provided.") - #print("from workflow...guesses[0].pphh = ", guesses[0].pphh) solverargs.setdefault("which", "SA") return run_eigensolver(matrix, guesses, n_ep=n_states, conv_tol=conv_tol, callback=callback, @@ -455,7 +454,7 @@ def estimate_n_guesses(matrix, n_states, singles_only=True, return max(n_states, n_guesses) -def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None): +def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None, qed_subblock=None): """ Obtain guesses by inspecting the diagonal matrix elements. If n_guesses_doubles is not None, this is number is always adhered to. @@ -479,7 +478,8 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None n_guess_singles = n_guesses if n_guesses_doubles is not None: n_guess_singles = n_guesses - n_guesses_doubles - singles_guesses = guess_function(matrix, n_guess_singles, block="ph") + singles_guesses = guess_function(matrix, n_guess_singles, block="ph", + qed_subblock=qed_subblock) doubles_guesses = [] if "pphh" in matrix.axis_blocks: @@ -489,7 +489,7 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None n_guesses_doubles = n_guesses - len(singles_guesses) if n_guesses_doubles > 0: doubles_guesses = guess_function(matrix, n_guesses_doubles, - block="pphh") + block="pphh", qed_subblock=qed_subblock) total_guesses = singles_guesses + doubles_guesses if len(total_guesses) < n_guesses: @@ -498,191 +498,80 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None return total_guesses -def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles=None): # sadly we cannot do this, since later functions - # require the matrix (AdcMatrix), instead of just the diagonal, which is a QED_Amplitude - guesses_elec = obtain_guesses_by_inspection(matrix.elec, n_guesses, kind, n_guesses_doubles) - guesses_phot = obtain_guesses_by_inspection(matrix.phot, n_guesses, kind, n_guesses_doubles) - guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) +def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles=None): + guesses_elec = obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="elec") + guesses_phot = obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot") + guesses_phot2 = obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot2") n_guess = len(guesses_elec) + + # Usually only few states are requested and most of them are close to pure electronic states, + # so we initialize the guess vectors as almost electric guesses. if not hasattr(matrix.reference_state, "full_diagonalization"): for i in np.arange(n_guess): + # TODO: maybe these values should be accessible from the input file guesses_phot[i] *= 0.02 guesses_phot2[i] *= 0.001 - #print("this is from obtain_guess_qed from workflow: guesses_elec[0].pphh", guesses_elec[0].pphh) if n_guess != len(guesses_phot): raise InputError("amount of guesses for electronic and photonic must be equal, but are" "{} electronic and {} photonic guesses".format(len(guesses_elec), len(guesses_phot))) - #guess_gs = np.zeros(n_guess) - guess_gs1 = np.zeros(n_guess) - guess_gs2 = np.zeros(n_guess) - # these guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a - # decoupled state. For gs2 this is a little less important, because the coupling is weaker. - #guess_gs[n_guess - 3] = 0 - """ - guess_gs1[n_guess - 2] = 2 # giving these two electronic ground/ photonic excited states a small value, they will only slowly appear in the convergence, - guess_gs2[n_guess - 1] = 5 # which in fact leads to a lot of numerical issues. Furthermore, they dont have to match the exact state number later...The solver takes care of that. - """ + guesses_tmp = [] - try: - dummy_var = guesses_elec[0].pphh - #print(dummy_var) - contains_doubles = True - #guesses_phot2 = obtain_guesses_by_inspection(matrix.phot2, n_guesses, kind, n_guesses_doubles) - #guess_gs2 = np.zeros(len(guesses_phot2)) - except AttributeError: - contains_doubles = False - for guess_index in np.arange(n_guess): # build QED_AmplitudeVectors from AmplitudeVector guesses - #if hasattr(guesses_elec[0], "pphh"): - if contains_doubles: - #guess_gs[n_guess - 3] = 1e+12 #8 - #print("doubles guesses are set up") - # what if this is not ok without restricting singlets/triplets only, because e.g. phot could be singlet and elec triplet ... doesnt seem to matter - #guesses_tmp.append(QED_AmplitudeVector(guess_gs[guess_index], guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, - # guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, - # guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) + + for guess_index in np.arange(n_guess): + if "pphh" in matrix.axis_blocks: + # what if this is not ok without restricting singlets/triplets only, + # because e.g. phot could be singlet and elec triplet ... doesnt seem to matter guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, - guess_gs1[guess_index], guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, - guess_gs2[guess_index], guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) + 0, guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, + 0, guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) else: - #guess_gs[n_guess - 3] = 1e+7 - #print("singles guesses are set up") guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, None, - guess_gs1[guess_index], guesses_phot[guess_index].ph, None, - guess_gs2[guess_index], guesses_phot2[guess_index].ph, None)) + 0, guesses_phot[guess_index].ph, None, + 0, guesses_phot2[guess_index].ph, None)) if hasattr(matrix.reference_state, "full_diagonalization"): full_guess = [] + for qed_vec in guesses_tmp: - tmp_vec = qed_vec.copy() - tmp_elec_vec = qed_vec.elec.copy() - tmp_phot_vec = qed_vec.phot.zeros_like() - tmp_phot2_vec = qed_vec.phot2.zeros_like() - #print("elec part", type(qed_vec.elec.ph)) - #print("phot part", type(qed_vec.phot.ph)) - #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) - #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) - tmp_elec_vec *= 10 - #tmp_elec_vec.ph *= 5 - #tmp_elec_vec.pphh *= 5 - if contains_doubles: - vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, - 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) - else: - vec = QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_phot2_vec.ph, None) - full_guess.append(vec) - for qed_vec in guesses_tmp: - tmp_elec_vec = qed_vec.elec.zeros_like() - tmp_phot_vec = qed_vec.phot.copy() - tmp_phot2_vec = qed_vec.phot2.zeros_like() - #print("elec part", type(qed_vec.elec.ph)) - #print("phot part", type(qed_vec.phot.ph)) - #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) - #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) - tmp_phot_vec *= 10 - #tmp_elec_vec.ph *= 5 - #tmp_elec_vec.pphh *= 5 - if contains_doubles: - vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, - 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) - else: - vec = QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_phot2_vec.ph, None) - full_guess.append(vec) - #tmp_vec = qed_vec.copy() - #print(type(tmp_vec.phot.ph)) - #tmp_vec.phot.ph *= 5 - #tmp_vec.phot.pphh *= 5 - #full_guess.append(tmp_vec) - for qed_vec in guesses_tmp: - tmp_elec_vec = qed_vec.elec.zeros_like() - tmp_phot_vec = qed_vec.phot.zeros_like() - tmp_phot2_vec = qed_vec.phot2.copy() - #print("elec part", type(qed_vec.elec.ph)) - #print("phot part", type(qed_vec.phot.ph)) - #print("elec copy from ampl vec", type(tmp_elec_vec.ph)) - #print("phot copy from ampl vec", type(tmp_phot_vec.ph)) - tmp_phot2_vec *= 10 - #tmp_elec_vec.ph *= 5 - #tmp_elec_vec.pphh *= 5 - if contains_doubles: - vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, - 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) + tmp_elec_vec = qed_vec.elec.copy() * 10 + tmp_phot_vec = qed_vec.phot.copy() * 10 + tmp_phot2_vec = qed_vec.phot2.copy() * 10 + + tmp_zero_vec = qed_vec.elec.zeros_like() + # TODO: Initialize via properly working zero vector + if "pphh" in matrix.axis_blocks: + full_guess.append(QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) + full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, tmp_zero_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) + full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, tmp_zero_vec.pphh, 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh)) else: - vec = QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_phot2_vec.ph, None) - full_guess.append(vec) - #tmp_vec = qed_vec.copy() - #print(type(tmp_vec.phot2.ph)) - #tmp_vec.phot2.ph *= 5 - #tmp_vec.phot2.pphh *= 5 - #full_guess.append(tmp_vec) - - tmp_elec_vec = guesses_tmp[0].elec.zeros_like() - tmp_phot_vec = guesses_tmp[0].phot.zeros_like() - tmp_phot2_vec = guesses_tmp[0].phot2.zeros_like() - - tmp_elec_vec2 = guesses_tmp[0].elec.zeros_like() - tmp_phot_vec2 = guesses_tmp[0].phot.zeros_like() - tmp_phot2_vec2 = guesses_tmp[0].phot2.zeros_like() - - if contains_doubles: - vec = QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, - 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh) - vec2 = QED_AmplitudeVector(tmp_elec_vec2.ph, tmp_elec_vec2.pphh, 0, tmp_phot_vec2.ph, tmp_phot_vec2.pphh, - 0, tmp_phot2_vec2.ph, tmp_phot2_vec2.pphh) - else: - vec = QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_phot2_vec.ph, None) - vec2 = QED_AmplitudeVector(tmp_elec_vec2.ph, None, 0, tmp_phot_vec2.ph, None, 0, tmp_phot2_vec2.ph, None) - #full_guess.append(guesses_tmp[0].copy()) - #full_guess.append(guesses_tmp[1].copy()) + full_guess.append(QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_zero_vec.ph, None, 0, tmp_zero_vec.ph, None)) + full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_zero_vec.ph, None)) + full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, None, 0, tmp_zero_vec.ph, None, 0, tmp_phot2_vec.ph, None)) - full_guess.append(vec) - full_guess.append(vec2) + full_guess.append(guesses_tmp[0].zeros_like()) + full_guess.append(guesses_tmp[0].zeros_like()) final_guesses = full_guess else: final_guesses = guesses_tmp - #guesses_tmp = full_guess - - """ - guesses_tmp = np.concatenate((guesses_tmp, guesses_tmp, guesses_tmp, guesses_tmp[:2])) - for i, guess in enumerate(guesses_tmp): - if i <= 19: - guess.elec.ph = guess.elec.ph * 5 - guess.elec.pphh = guess.elec.pphh * 5 - elif i >= 20 and i <= 39: - guess.phot.ph = guess.phot.ph * 5 - guess.phot.pphh = guess.phot.pphh * 5 - elif i >= 40 and i <= 59: - guess.phot2.ph = guess.phot2.ph * 5 - guess.phot2.pphh = guess.phot2.pphh * 5 - else: - pass - """ - #try: - final_guesses[len(final_guesses) - 2].gs1 += 2 #gs_vec(100) - final_guesses[len(final_guesses) - 1].gs2 += 5 #gs_vec(100) - #guesses_tmp = full_guess - #except: - # guesses_tmp[len(guesses_tmp) - 2].gs1 += 100 #gs_vec(100) - # guesses_tmp[len(guesses_tmp) - 1].gs2 += 100 #gs_vec(100) - -# - # guesses_tmp needs to be normalized then - #for vec in guesses_tmp: - # print(type(vec.gs), vec.gs) - # print(type(vec.ph), vec.ph) - # print(type(vec.gs1), vec.gs1) - # print(type(vec.ph1), vec.ph1) - #print("from workflow guess_qed...guesses_tmp[0].pphh = ", guesses_tmp[0].pphh) - #normalized_guesses = [vec / np.sqrt(vec @ vec) for vec in guesses_tmp] - #print("after normalization guesses[0].pphh is ", normalized_guesses[0].pphh) + + # These guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a + # decoupled state. For gs2 this is a little less important, because the coupling is weaker. + # Further notice, that the last two guesses are used for the photonic dispersion states, + # so e.g. the 3rd guess doesn't need to converge into the 1st state + # TODO: maybe manipulate these entries via input + final_guesses[len(final_guesses) - 2].gs1 += 5#2 + final_guesses[len(final_guesses) - 1].gs2 += 20#5 + for guess in final_guesses: - print(guess.gs1.as_float(), guess.gs2.as_float()) + #print(guess.gs1.as_float(), guess.gs2.as_float()) + print(guess.gs1, guess.gs2) return [vec / np.sqrt(vec @ vec) for vec in final_guesses] - #for vec in guesses_tmp: - # print("norm of guess vector = ", np.sqrt(vec @ vec)) - #return guesses_tmp def setup_solver_printing(solmethod_name, matrix, kind, default_print, output=None): From 292d3f9d72af8e9b096067f380b04cbbe8a80630 Mon Sep 17 00:00:00 2001 From: Marco Bauer Date: Wed, 8 Jun 2022 15:04:20 +0200 Subject: [PATCH 42/64] included approximation scheme, which seems to provide a tiny error, originating from slightly different excitation vectors. Lots of cleanup done as well. --- adcc/AdcMatrix.py | 13 +- adcc/AmplitudeVector.py | 3 +- adcc/DataHfProvider.py | 1 - adcc/ElectronicTransition.py | 123 ++- adcc/HfCounterData.py | 1 - adcc/LazyMp.py | 101 +- adcc/ReferenceState.py | 21 +- adcc/adc_pp/matrix.py | 1239 ++-------------------- adcc/adc_pp/state2state_transition_dm.py | 93 +- adcc/qed_matrix_from_diag_adc.py | 133 +++ adcc/solver/LanczosIterator.py | 1 - adcc/solver/davidson.py | 3 - adcc/solver/explicit_symmetrisation.py | 1 - adcc/solver/preconditioner.py | 10 +- adcc/workflow.py | 17 +- 15 files changed, 507 insertions(+), 1253 deletions(-) create mode 100644 adcc/qed_matrix_from_diag_adc.py diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index e7c7eeab..f110c4a4 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -194,7 +194,7 @@ def get_pp_blocks(disp_str): variant = None if method.is_core_valence_separated: variant = "cvs" - + # Build full QED-matrix if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): blocks = {} @@ -220,7 +220,7 @@ def get_pp_blocks(disp_str): self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, None) self.__init_space_data_qed(self.__diagonal.elec) #self.__diagonal.evaluate() - else: + else: # Build "standard" ADC-matrix blocks = { bl: get_pp_blocks("elec")[bl] for bl, order in block_orders.items() if order is not None } @@ -356,12 +356,6 @@ def axis_blocks(self): (e.g. ['ph', 'pphh']). """ return list(self.axis_spaces.keys()) - """ - @property - def axis_blocks(self): # this is only for debugging and has not been adapted yet - #print(list(self.axis_spaces.keys()) + list(self.axis_spaces.keys())) - return list(self.axis_spaces.keys()) + list(self.axis_spaces.keys()) - """ def diagonal(self, block=None): """Return the diagonal of the ADC matrix""" @@ -454,7 +448,8 @@ def mv(qed_disp_key): return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) + #return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) + return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot_part.ph * 0, None) else: TypeError("matvec needs to be invoked with AmplitudeVector or QED_AmplitudeVector") diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index 8aca8d32..fe8baf83 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -273,14 +273,13 @@ def __matmul__(self, other): if isinstance(other, QED_AmplitudeVector): return self.dot(other) if isinstance(other, list): - #print("list given in QED matmul") if all(isinstance(elem, QED_AmplitudeVector) for elem in other): return self.dot(other) return NotImplemented def __sub__(self, invec): - if isinstance(invec, (float, int)): # for diagonal - shift in preconditioner.py + if isinstance(invec, (float, int)): if "pphh" in self.elec.blocks_ph: return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), pphh=self.elec.pphh.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), pphh1=self.phot.pphh.__sub__(invec), diff --git a/adcc/DataHfProvider.py b/adcc/DataHfProvider.py index fa7eb2c3..f6c431d3 100644 --- a/adcc/DataHfProvider.py +++ b/adcc/DataHfProvider.py @@ -233,7 +233,6 @@ def get_conv_tol(self): return get_scalar_value(self.data, "threshold") def fill_occupation_f(self, out): - print("DataHfProvider.py is used") out[:] = self.data["occupation_f"] def fill_orbcoeff_fb(self, out): diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py index e889cb4b..b68adf74 100644 --- a/adcc/ElectronicTransition.py +++ b/adcc/ElectronicTransition.py @@ -28,6 +28,10 @@ from .visualisation import ExcitationSpectrum from .OneParticleOperator import product_trace from .AdcMethod import AdcMethod +from adcc.functions import einsum +from adcc import block as b +from adcc.adc_pp.state2state_transition_dm import state2state_transition_dm +from adcc.adc_pp.transition_dm import transition_dm from scipy import constants from .Excitation import mark_excitation_property @@ -165,6 +169,123 @@ def transition_dipole_moment(self): for tdm in self.transition_dm ]) + + @cached_property + @mark_excitation_property() + @timed_member_call(timer="_property_timer") + def transition_dipole_moments_qed(self): + """ + List of transition dipole moments of all computed states + to build the QED-matrix in the basis of the diagonal + purely electric subblock + """ + if hasattr(self.reference_state, "approx"): + if self.property_method.level == 0: + warnings.warn("ADC(0) transition dipole moments are known to be " + "faulty in some cases.") + dipole_integrals = self.operators.electric_dipole + def tdm(i, prop_level): + self.ground_state.tdm_contribution = prop_level + return transition_dm(self.method, self.ground_state, self.excitation_vector[i]) + if hasattr(self.reference_state, "first_order_coupling"): + + return np.array([ + [product_trace(comp, tdm(i, "adc0")) for comp in dipole_integrals] + for i in np.arange(len(self.excitation_energy)) + ]) + else: + prop_level = "adc" + str(self.property_method.level - 1) + return np.array([ + [product_trace(comp, tdm(i, prop_level)) for comp in dipole_integrals] + for i in np.arange(len(self.excitation_energy)) + ]) + else: + return ("transition_dipole_moments_qed are only calculated," + "if reference_state contains 'approx' attribute") + + @cached_property + @mark_excitation_property() + @timed_member_call(timer="_property_timer") + def s2s_dipole_moments_qed(self): + """ + List of diff_dipole moments of all computed states + to build the QED-matrix in the basis of the diagonal + purely electric subblock + """ + if hasattr(self.reference_state, "approx"): + dipole_integrals = self.operators.electric_dipole + print("note, that only the z coordinate of the dipole integrals is calculated") + n_states = len(self.excitation_energy) + + def s2s(i, f, s2s_contribution): + self.ground_state.s2s_contribution = s2s_contribution + vec = self.excitation_vector + return state2state_transition_dm(self.method, self.ground_state, vec[i], vec[f]) + + def final_block(name): + return np.array([[product_trace(dipole_integrals[2], s2s(i, j, name)) for j in np.arange(n_states)] + for i in np.arange(n_states)]) + + block_dict = {} + + block_dict["qed_adc1_off_diag"] = final_block("adc1") + + if self.method.name == "adc2" and not hasattr(self.reference_state, "first_order_coupling"): + + block_dict["qed_adc2_diag"] = final_block("qed_adc2_diag") + #print(block_dict["qed_adc2_diag"]) + block_dict["qed_adc2_edge_couple"] = final_block("qed_adc2_edge_couple") + block_dict["qed_adc2_edge_phot_couple"] = final_block("qed_adc2_edge_phot_couple") + block_dict["qed_adc2_ph_pphh"] = final_block("qed_adc2_ph_pphh") + block_dict["qed_adc2_pphh_ph"] = final_block("qed_adc2_pphh_ph") + #print(block_dict["qed_adc2_diag"].tolist()) + return block_dict + else: + return ("s2s_dipole_moments_qed are only calculated," + "if reference_state contains 'approx' attribute") + + @cached_property + @mark_excitation_property() + @timed_member_call(timer="_property_timer") + def qed_second_order_ph_ph_couplings(self): + """ + List of blocks of the expectation value of the perturbation + of the Hamiltonian for all computed states required + to build the QED-matrix in the basis of the diagonal + purely electric subblock + """ + if hasattr(self.reference_state, "approx"): + qed_t1 = self.ground_state.qed_t1(b.ov) + + def couple(qed_t1, ul, ur): + return { + b.ooov: einsum("kc,ia,ja->kjic", qed_t1, ul, ur) + einsum("ka,ia,jb->jkib", qed_t1, ul, ur), + b.ovvv: einsum("kc,ia,ib->kacb", qed_t1, ul, ur) + einsum("ic,ia,jb->jabc", qed_t1, ul, ur) + } + + def phot_couple(qed_t1, ul, ur): + return { + b.ooov: einsum("kc,ia,ja->kijc", qed_t1, ul, ur) + einsum("kb,ia,jb->ikja", qed_t1, ul, ur), + b.ovvv: einsum("kc,ia,ib->kbca", qed_t1, ul, ur) + einsum("jc,ia,jb->ibac", qed_t1, ul, ur) + } + + def prod_sum(hf, two_p_op): + return + (einsum("ijka,ijka->", hf.ooov, two_p_op[b.ooov]) + + einsum("iabc,iabc->", hf.ovvv, two_p_op[b.ovvv])) + + def final_block(func): + return np.array([[prod_sum(self.reference_state, func(qed_t1, i.ph, j.ph)) for i in self.excitation_vector] + for j in self.excitation_vector]) + + block_dict = {} + block_dict["couple"] = final_block(couple) + block_dict["phot_couple"] = final_block(phot_couple) + + return block_dict + else: + return ("qed_second_order_ph_ph_couplings are only calculated," + "if reference_state contains 'approx' attribute") + @cached_property @mark_excitation_property() @timed_member_call(timer="_property_timer") @@ -198,8 +319,6 @@ def transition_magnetic_dipole_moment(self): @mark_excitation_property() def oscillator_strength(self): """List of oscillator strengths of all computed states""" - print("final TDMs", self.transition_dipole_moment) - print("final excitation energies", self.excitation_energy) return 2. / 3. * np.array([ np.linalg.norm(tdm)**2 * np.abs(ev) for tdm, ev in zip(self.transition_dipole_moment, diff --git a/adcc/HfCounterData.py b/adcc/HfCounterData.py index 4ea9a1c0..2a158812 100644 --- a/adcc/HfCounterData.py +++ b/adcc/HfCounterData.py @@ -115,7 +115,6 @@ def fill_orbcoeff_fb(self, out): + self.get_b_range()[None, :]) def fill_occupation_f(self, out): - print("HfCounterData.py is used") n_oa = self.__n_orbs_alpha out[:] = np.zeros(2 * n_oa) out[:self.__n_alpha] = 1. diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index 5216d6fd..109c7a2c 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -69,8 +69,6 @@ def df(self, space): s1, s2 = split_spaces(space) fC = hf.fock(s1 + s1).diagonal() fv = hf.fock(s2 + s2).diagonal() - #print("occupied orbital energies", fC) - #print("unoccupied orbital energies", fv) return direct_sum("-i+a->ia", fC, fv) @cached_member_function @@ -130,18 +128,34 @@ def t2eri(self, space, contraction): @timed_member_call(timer="timer") def mp2_diffdm(self): """ - Return the MP2 differensce density in the MO basis. + Return the MP2 difference density in the MO basis. """ hf = self.reference_state ret = OneParticleOperator(self.mospaces, is_symmetric=True) # NOTE: the following 3 blocks are equivalent to the cvs_p0 intermediates # defined at the end of this file + # the following terms including omega originate from the qed correction ret.oo = -0.5 * einsum("ikab,jkab->ij", self.t2oo, self.t2oo) + #+ einsum("ia,ja->ij", self.qed_t1(b.ov), self.qed_t1(b.ov)) * omega) ret.ov = -0.5 * ( + einsum("ijbc,jabc->ia", self.t2oo, hf.ovvv) + einsum("jkib,jkab->ia", hf.ooov, self.t2oo) + #- (einsum("ib,ab->ia", self.qed_t1(b.ov), hf.get_qed_total_dip(b.vv)) + # - einsum("ji,ja->ia", hf.get_qed_total_dip(b.oo), self.qed_t1(b.ov))) * omega ) / self.df(b.ov) ret.vv = 0.5 * einsum("ijac,ijbc->ab", self.t2oo, self.t2oo) + #+ einsum("ia,ib->ab", self.qed_t1(b.ov), self.qed_t1(b.ov)) * omega) + if hasattr(hf, "coupling"):# and not hasattr(hf, "approx"): + print("mp2 diffdm has been adapted to qed") + omega = ReferenceState.get_qed_omega(hf) + + ret.oo -= 0.5 * einsum("ia,ja->ij", self.qed_t1(b.ov), self.qed_t1(b.ov)) * omega + + ret.ov += 0.5 * (einsum("ib,ab->ia", self.qed_t1(b.ov), hf.get_qed_total_dip(b.vv)) + - einsum("ji,ja->ia", hf.get_qed_total_dip(b.oo), self.qed_t1(b.ov)) * omega + ) / self.df(b.ov) + + ret.vv += 0.5 * einsum("ia,ib->ab", self.qed_t1(b.ov), self.qed_t1(b.ov)) * omega if self.has_core_occupied_space: # additional terms to "revert" CVS for ground state density @@ -177,37 +191,42 @@ def mp2_diffdm(self): ret.reference_state = self.reference_state return evaluate(ret) + @cached_property + def mp1_diffdm_qed(self): + """ + This does not really exist, but since the dipole operator also contains + the factor (b^{dagger} + b), there exists a term, which is required + in the evaluation for the corresponding dipole properties, which are + themselves needed, to perform the qed-adc(2) test + """ + ret = OneParticleOperator(self.mospaces, is_symmetric=True) + hf = self.reference_state + omega = ReferenceState.get_qed_omega(hf) + + ret.ov = self.qed_t1(b.ov) #* omega/2 #this is left out, since it is + #also left out for the s2s properties and reintroduced in the testing script + + ret.reference_state = self.reference_state + return evaluate(ret) + def density(self, level=2): """ Return the MP density in the MO basis with all corrections up to the specified order of perturbation theory """ if level == 1: - return self.reference_state.density + if hasattr(self.reference_state, "coupling"): + return self.reference_state.density# + self.mp1_diffdm_qed + else: + return self.reference_state.density elif level == 2: return self.reference_state.density + self.mp2_diffdm else: raise NotImplementedError("Only densities for level 1 and 2" " are implemented.") - def dipole_moment(self, level=2): - """ - Return the MP dipole moment at the specified level of - perturbation theory. - """ - if level == 1: - return self.reference_state.dipole_moment - elif level == 2: - return self.mp2_dipole_moment - else: - raise NotImplementedError("Only dipole moments for level 1 and 2" - " are implemented.") - @cached_member_function def qed_t1_df(self, space): - #if space != b.ov: - #raise NotImplementedError("qed_t1 term not implemented " - # f"for space {space}.") total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) if space == b.oo: total_dip.oo = self.get_qed_total_dip.oo @@ -218,15 +237,17 @@ def qed_t1_df(self, space): elif space == b.vv: total_dip.vv = self.get_qed_total_dip.vv return total_dip.vv - #return total_dip.ov #/ self.df(b.ov) @cached_member_function def qed_t1(self, space): - """ Return new electronic singly excited amplitude in the first order correction to the wavefunction for qed for N=1 """ + """ + Return new electronic singly excited amplitude in the first order correction + to the wavefunction for qed + """ if space != b.ov: raise NotImplementedError("qed_t1 term not implemented " f"for space {space}.") - return self.qed_t1_df(b.ov) / self.df(b.ov) #einsum("kc,kc->kc", self.qed_t1(b.ov), self.df(b.ov)) + return self.qed_t1_df(b.ov) / self.df(b.ov) @cached_member_function def qed_t0_df(self, space): @@ -237,9 +258,6 @@ def qed_t0_df(self, space): if space == b.ov: occ_sum = einsum("ka,ki->ia", total_dip.ov, total_dip.oo) virt_sum = einsum("ac,ic->ia", total_dip.vv, total_dip.ov) - #return einsum("ka,ki->ia", total_dip.ov, total_dip.oo) - einsum("ac,ic->ia", total_dip.vv, total_dip.ov) - #print(total_dip.ov) - #return einsum("ia,ia->ia", occ_sum, virt_sum) elif space == b.oo: occ_sum = einsum("ki,kj->ij", total_dip.oo, total_dip.oo) virt_sum = einsum("ic,jc->ij", total_dip.ov, total_dip.ov) @@ -248,6 +266,7 @@ def qed_t0_df(self, space): virt_sum = einsum("ac,bc->ab", total_dip.vv, total_dip.vv) return occ_sum - virt_sum + @cached_member_function def qed_t0(self, space): """ Return new electronic singly excited amplitude in the first order correction to the wavefunction for qed for N=0 """ @@ -256,6 +275,7 @@ def qed_t0(self, space): f"for space {space}.") return self.qed_t0_df(b.ov) / self.df(b.ov) + @cached_member_function def diff_df(self, space): if space == b.ov: @@ -266,7 +286,6 @@ def diff_df(self, space): return einsum("ia,ja->ij", self.df(b.ov), - self.df(b.ov)) - @cached_member_function def energy_correction(self, level=2): """Obtain the MP energy correction at a particular level""" @@ -274,7 +293,8 @@ def energy_correction(self, level=2): if level > 3: raise NotImplementedError(f"MP({level}) energy correction " "not implemented.") - if level < 2: #for qed_mp1 from non-qed-hf also first corrections come into play...for now done in mp2 part here + # For qed_mp1 from non-qed-hf also first corrections come into play...for now done in mp2 part here + if level < 2: return 0.0 hf = self.reference_state is_cvs = self.has_core_occupied_space @@ -285,8 +305,7 @@ def energy_correction(self, level=2): for pref, eri, t2 in terms ) if hasattr(hf, "coupling"): - print("mp2 energy with two electron qed perturbation " + str(mp2_correction)) - #check if qed-hf (psi4.core.Wavefunction) input or non-qed-hf input (standard hf) is given + #print("mp2 energy with two electron qed perturbation " + str(mp2_correction)) total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) omega, total_dip.ov = ReferenceState.get_qed_omega(hf), self.get_qed_total_dip.ov qed_terms = [(omega/2, total_dip.ov, self.qed_t1(b.ov))] @@ -295,15 +314,8 @@ def energy_correction(self, level=2): for pref, lambda_dip, qed_t in qed_terms ) if hasattr(hf, "qed_hf"): - print("full qed MP2 energy correction (qed-hf) " + str(mp2_correction + qed_mp2_correction_1)) + print("QED-MP(2) energy correction for QED-HF = " + str(mp2_correction + qed_mp2_correction_1)) else: - #total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) - #omega, total_dip.ov = ReferenceState.get_qed_omega(hf), self.get_qed_total_dip.ov - #qed_terms_1 = [(omega/2, total_dip.ov, self.qed_t1(b.ov))] - #qed_mp2_correction_1 = sum( - # -pref * lambda_dip.dot(qed_t) - # for pref, lambda_dip, qed_t in qed_terms_1 - #) qed_terms_0 = [(1.0, self.qed_t0(b.ov), self.qed_t0_df(b.ov))] qed_mp2_correction_0 = sum( -0.25 * pref * ampl_t0.dot(ampl_t0_df) @@ -315,15 +327,10 @@ def energy_correction(self, level=2): pref * lambda_dip.dot(lambda_dip) for pref, lambda_dip in qed_mp1_additional_terms ) - #print(self.qed_t0(b.ov)) - #print(self.qed_t0_df(b.ov)) - #print(qed_mp2_correction_0) - print("full qed MP2 energy correction (standard hf) " - + str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0)) - print("qed-mp1 correction, due to standard hf input " + str(qed_mp1_correction)) - print("new qed-mp2 correction compared to qed-hf " + str(qed_mp2_correction_0)) - #print("transition dipoles * coupling * sqrt(2 * freq)", total_dip.ov) - #print("orbital energy differences", self.df(b.ov)) + print("QED-MP(2) energy correction for standard HF (includes QED-MP(1) too) = " + + str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0 + qed_mp1_correction)) + #print("qed-mp1 correction, due to standard hf input " + str(qed_mp1_correction)) + #print("new qed-mp2 correction compared to qed-hf " + str(qed_mp2_correction_0)) elif level == 2 and is_cvs: terms = [(1.0, hf.oovv, self.t2oo), (2.0, hf.ocvv, self.t2oc), diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index fe1411bd..7cc86d01 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -178,32 +178,19 @@ def get_qed_total_dip(self, block): if hasattr(self, "coupling"): from . import block as b dips = self.operators.electric_dipole - #import ctypes - #dips_id_int = int(dips, base=16) - #print(ctypes.cast(dips_id_int, ctypes.py_object).value) - #print(dips[block]) couplings = self.coupling freqs = self.frequency - #test_el_dip = OneParticleOperator(self.mospaces, is_symmetric=True) - #omega_square = x**2 for x in freqs - #omega = np.linalg.norm(freqs) total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) for coupling, freq, dip in zip(couplings, freqs, dips): total_dip += coupling * np.sqrt(2 * freq) * dip - #test_el_dip = dip total_dip.evaluate() - #test_el_dip.evaluate() - #print(str(block)) - #print(test_el_dip[block]) - #print(total_dip[block]) return total_dip[block] @cached_member_function def get_qed_omega(self): if hasattr(self, "coupling"): freqs = self.frequency - omega = np.linalg.norm(freqs) - return omega + return np.linalg.norm(freqs) @cached_member_function def qed_D_object(self, block): @@ -211,7 +198,6 @@ def qed_D_object(self, block): from . import block as b from .functions import einsum total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) - #omega = ReferenceState.get_qed_omega(self) total_dip.oo = ReferenceState.get_qed_total_dip(self, b.oo) total_dip.ov = ReferenceState.get_qed_total_dip(self, b.ov) total_dip.vv = ReferenceState.get_qed_total_dip(self, b.vv) @@ -229,10 +215,11 @@ def qed_D_object(self, block): return ds[block] def eri(self, block): - if hasattr(self, "coupling"):# and not hasattr(self, "first_order_coupling") : + if hasattr(self, "coupling"): from . import block as b from .functions import einsum - ds_init = OneParticleOperator(self.mospaces, is_symmetric=True) #Since there is no TwoParticleOperator we do this + # Since there is no TwoParticleOperator object, we initialize it like this + ds_init = OneParticleOperator(self.mospaces, is_symmetric=True) ds = { b.oooo: einsum('ik,jl->ijkl', ds_init.oo, ds_init.oo), b.ooov: einsum('ik,ja->ijka', ds_init.oo, ds_init.ov), diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index a06a1349..7d27ea7c 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -22,16 +22,12 @@ ## --------------------------------------------------------------------- from math import sqrt from collections import namedtuple -from attr import has -import numpy as np -from numpy.lib.function_base import blackman from adcc import block as b from adcc.functions import direct_sum, einsum, zeros_like from adcc.Intermediates import Intermediates, register_as_intermediate -from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector from adcc.ReferenceState import ReferenceState -from adcc.OneParticleOperator import OneParticleOperator __all__ = ["block"] @@ -90,120 +86,19 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): return globals()[fn](reference_state, ground_state, intermediates) - -# Since we already have all ph and pphh routines at hand, we will construct 4 dirrefent matrices, -# which will be evaluated by the original AdcMatrix, which is now the submatrix. -# We will construct the matrix as follows: -# elec phot_couple -# elec_couple phot -# where we wont write elec explicitly. -# These blocks can then be forwarded to the AdcMatrix_submatrix, except for the gs part, which has to be -# treated separately. In the matvec we then construct the vector as: -# elec -# phot -# We therefore build an AmplitudeVector for the ph and pphh blocks, while inserting QED_AmplitudeVector.ph or .pphh, -# which are also AmplitudeVector classes -# It also seems smart, to insert ph_gs and pphh_gs into the ph_ph and pphh_ph blocks, respectively. This should also be fine with -# construct_symmetrisation_for_blocks, which enforces the correct symmetry for the doubles block, which does probably not (?) -# make a difference for the gs_pphh blocks, since they reduce to gs, so it should be fine to do so. -> It should be fine, -# since after using the Jacobi-davidson preconditioner by dividing the residuals with the shifted matrix-diagonal, the symmetry could -# be lost (?), but in the gs part, which is already only one value, the symmetry is still enforced. -# Maybe its also a good idea to pack gs_gs, gs_ph and gs_pphh into one block, which returns one number for apply and the gs_gs part -# for the diagonal. -# Using both of these "block in other block" ideas would leave one with 24 total blocks, instead of 36, for each order -# (original 4 blocks + ph_gs and pphh_gs) per "ADC submatrix" -# maybe return float from ph/pphh_gs blocks, instead of QED_AmplitudeVector.gs/.gs1 - -# For QED-ADC(2) we then require also the double energy photon block, so we introduce the naming convention: +# For QED-ADC (up to double photon dispersion) we build the matrix as follows: # elec phot_couple phot_couple_outer # elec_couple phot phot_couple_inner # elec_couple_edge elec_couple_inner phot2 +# where each block is a "standard" ADC matrix itself, including the groundstate +# and the groundstate couplings. However, the gs_ph and gs_gs blocks are merged +# into the ph_ph and gs_gs blocks, respectively, since we calculate matrix vector +# products anyway. Note, that the purely electric groundstate never appears, since +# it is always zero. -# The implementations for QED-ADC(1) and QED-ADC(2) are tested by constructing the original non-QED-ADC matrix -# of the corresponding order with the transformed ERIs, and using those eigenvectors to construct the remaining -# contributions as properties. Those are mostly dipole operator terms, but one term also provides the two electron -# term from the original H_1 perturbation from MP. With those properties the same matrix can be build in the basis -# of the eigenvectors of the non-QED-ADC matrix, yielding the same eigenvalues upon diagonalization. - - -# -# 0th order main -# - - -""" -def block_gs_gs_0(hf, mp, intermediates): # this is zero, but we want to give some test value here - omega = float(ReferenceState.get_qed_omega(hf)) - #diagonal = QED_AmplitudeVector(gs=omega) - def apply(ampl): - #print("printing type(ampl)") - #print(type(ampl), ampl) - #print(type(ampl.gs)) - #print(ampl.gs) - return QED_AmplitudeVector(gs=(0 * ampl.gs)) - return AdcBlock(apply, 0) - -def block_gs_gs_0_couple(hf, mp, intermediates): - def apply(ampl): - return QED_AmplitudeVector(gs=(0 * ampl.gs)) - return AdcBlock(apply, 0) - -def block_gs_gs_0_phot_couple(hf, mp, intermediates): - def apply(ampl): - return QED_AmplitudeVector(gs1=(0 * ampl.gs1)) - return AdcBlock(apply, 0) - -#block_gs_gs_0_phot = block_gs_gs_0 - -def block_gs_gs_0_phot(hf, mp, intermediates): # this is zero, but we want to give some test value here - omega = float(ReferenceState.get_qed_omega(hf)) - #diagonal = QED_AmplitudeVector(gs=omega) - def apply(ampl): - #print(type(ampl.gs1), ampl) - #print(ampl.gs) - return QED_AmplitudeVector(gs1=(0 * ampl.gs1)) - return AdcBlock(apply, 0) - -def block_gs_ph_0(hf, mp, intermediates): # this is zero, but we want to give some test value here - #return AdcBlock(lambda ampl: 0, 0) - #omega = float(ReferenceState.get_qed_omega(hf)) - def apply(ampl): - terms = [(mp.df(b.ov), ampl.ph)] - return QED_AmplitudeVector(gs=sum(mpdf.dot(amplph) - for mpdf, amplph in terms)) - return AdcBlock(apply, 0) - -def block_gs_ph_0_couple(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - #def apply(ampl): - # return QED_AmplitudeVector(ph=(omega * ampl.ph)) - #return AdcBlock(apply, 0) - -def block_gs_ph_0_phot_couple(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - -def block_gs_ph_0_phot(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - -def block_ph_gs_0(hf, mp, intermediates): # this is zero, but we want to give some test value here - #return AdcBlock(lambda ampl: 0, 0) - #omega = float(ReferenceState.get_qed_omega(hf)) - def apply(ampl): - return AmplitudeVector(ph=(mp.df(b.ov) * ampl.gs)) - return AdcBlock(apply, 0) - -def block_ph_gs_0_couple(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - -def block_ph_gs_0_phot_couple(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - -def block_ph_gs_0_phot(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) -""" # -# 0th order gs blocks (gs_ph blocks in ph_ph, which are zero for this order) +# 0th order gs blocks # def block_ph_gs_0(hf, mp, intermediates): @@ -214,68 +109,24 @@ def block_ph_gs_0(hf, mp, intermediates): block_ph_gs_0_couple_inner = block_ph_gs_0_phot_couple_inner = block_ph_gs_0 -def block_pphh_gs_0(hf, mp, intermediates): - return AdcBlock(lambda ampl:0, 0) - -block_pphh_gs_0_couple = block_pphh_gs_0_phot_couple = block_pphh_gs_0_phot = block_pphh_gs_0 -block_pphh_gs_0_couple_edge = block_pphh_gs_0_phot_couple_edge = block_pphh_gs_0_phot2 = block_pphh_gs_0 -block_pphh_gs_0_couple_inner = block_pphh_gs_0_phot_couple_inner = block_pphh_gs_0 - -""" -def block_gs_ph_0(hf, mp, intermediates): - def apply(ampl): - return AmplitudeVector(ph=(0 * ampl.ph)) - return AdcBlock(apply, 0) - -def block_gs_ph_0_phot_couple(hf, mp, intermediates): - def apply(ampl): - return AmplitudeVector(ph=(0 * ampl.ph)) - return AdcBlock(apply, 0) - -def block_gs_ph_0_couple(hf, mp, intermediates): - def apply(ampl): - return AmplitudeVector(ph=(0 * ampl.ph1)) - return AdcBlock(apply, 0) - -def block_gs_ph_0_phot(hf, mp, intermediates): - def apply(ampl): - return AmplitudeVector(ph=(0 * ampl.ph1)) - return AdcBlock(apply, 0) -""" -#def block_pphh_gs_0(hf, mp, intermediates): -# return AdcBlock(0, 0) - -#block_pphh_gs_0_couple = block_pphh_gs_0_phot_couple = block_pphh_gs_0_phot = block_pphh_gs_0 - # # 0th order main # def block_ph_ph_0(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - if hasattr(hf, "coupling"):# and hasattr(hf, "qed_hf"): + if hasattr(hf, "coupling"): diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) - #np.insert(diagonal, 0, 0.) - #diagonal = np.zeros(np.add(diagonal_.shape, np.array([0]))) - #diagonal[1:,1:] = diagonal_ - print("new stuff works???") - #print(diagonal) - def apply(ampl): - mvprod = AmplitudeVector(ph=( #change to QED_AmplitudeVector + return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, hf.fvv) - einsum("IJ,Ja->Ia", fCC, ampl.ph) - )) - #np.insert(mvprod, 0, 0.) - #mvprod = np.zeros(np.add(mv_prod_.shape, np.array([0]))) - #mvprod[1:,1:] = mvprod_ - return mvprod + )) else: diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) - def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, hf.fvv) @@ -286,72 +137,43 @@ def apply(ampl): block_cvs_ph_ph_0 = block_ph_ph_0 -def block_ph_ph_0_couple(hf, mp, intermediates): # we also give these blocks zero diagonals, so the submatrix routine does not require adjustments - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) - return AdcBlock(lambda ampl: 0, diagonal) -# def apply(ampl): -# return AmplitudeVector(ph=(0 * ampl.ph)) -# return AdcBlock(apply, diagonal) +def block_ph_ph_0_couple(hf, mp, intermediates): + return AdcBlock(lambda ampl: 0, 0) block_ph_ph_0_phot_couple = block_ph_ph_0_couple block_ph_ph_0_phot_couple_edge = block_ph_ph_0_phot_couple_inner = block_ph_ph_0_couple_edge = block_ph_ph_0_couple_inner = block_ph_ph_0_couple -#def block_ph_ph_0_phot_couple(hf, mp, intermediates): -# diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) -# def apply(ampl): -# return AmplitudeVector(ph=(0 * ampl.ph1)) -# return AdcBlock(apply, diagonal) - def block_ph_ph_0_phot(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - if hasattr(hf, "coupling"):# and hasattr(hf, "qed_hf"): + if hasattr(hf, "coupling"): diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) - #np.insert(diagonal, 0, 0.) - print("new stuff works???") - #print(diagonal) - def apply(ampl): - mvprod = AmplitudeVector(ph=( + return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph1, hf.fvv) - einsum("IJ,Ja->Ia", fCC, ampl.ph1) )) - #np.insert(mvprod, 0, 0.) - return mvprod else: raise NotImplementedError("coupling needs to be given to reference wavefunction in input file for QED-ADC") return AdcBlock(apply, diagonal) def block_ph_ph_0_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - if hasattr(hf, "coupling"):# and hasattr(hf, "qed_hf"): + if hasattr(hf, "coupling"): diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) - #np.insert(diagonal, 0, 0.) - print("new stuff works???") - #print(diagonal) - def apply(ampl): - mvprod = AmplitudeVector(ph=( + return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph2, hf.fvv) - einsum("IJ,Ja->Ia", fCC, ampl.ph2) )) - #np.insert(mvprod, 0, 0.) - return mvprod else: raise NotImplementedError("coupling needs to be given to reference wavefunction in input file for QED-ADC") return AdcBlock(apply, diagonal) -#def block_ph_ph1_0(hf, mp, intermediates): -# return AdcBlock(lambda ampl: 0, 0) - -#def block_ph1_ph_0(hf, mp, intermediates): -# return AdcBlock(lambda ampl: 0, 0) - - def diagonal_pphh_pphh_0(hf): # Note: adcman similarly does not symmetrise the occupied indices @@ -367,10 +189,6 @@ def diagonal_pphh_pphh_0_qed(hf, n_omega): # Note: adcman similarly does not symmetrise the occupied indices # (for both CVS and general ADC) omega = float(ReferenceState.get_qed_omega(hf)) - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) qed = n_omega * omega fCC = hf.fcc if hf.has_core_occupied_space else hf.foo @@ -390,8 +208,7 @@ def apply(ampl): def block_pphh_pphh_0_couple(hf, mp, intermediates): - diagonal = AmplitudeVector(pphh=(mp.t2oo.zeros_like())) - return AdcBlock(lambda ampl: 0, diagonal) + return AdcBlock(lambda ampl: 0, 0) block_pphh_pphh_0_phot_couple = block_pphh_pphh_0_couple @@ -454,7 +271,7 @@ def block_pphh_ph_0(hf, mp, intermediates): # -# 1st order gs blocks (gs_ph blocks in ph_ph) +# 1st order gs blocks # def block_ph_gs_1(hf, mp, intermediates): @@ -466,8 +283,6 @@ def apply(ampl): return omega * ampl.gs1 return AdcBlock(apply, omega) -#def block_ph_gs_1_phot_couple(hf, mp, intermediates): -# return AdcBlock(lambda ampl: 0, 0) block_ph_gs_1_phot_couple = block_ph_gs_1 block_ph_gs_1_phot_couple_edge = block_ph_gs_1_couple_edge = block_ph_gs_1 @@ -476,8 +291,6 @@ def block_ph_gs_1_couple(hf, mp, intermediates): def apply(ampl): return (-1) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph) return AdcBlock(apply, 0) - #return AdcBlock(lambda ampl: 0, 0) - def block_ph_gs_1_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -492,79 +305,32 @@ def apply(ampl): return AdcBlock(apply, 0) def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - #def apply(ampl): - # return (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) return AdcBlock(lambda ampl: 0, 0) - - - -def block_pphh_gs_1(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - -block_pphh_gs_1_couple = block_pphh_gs_1_phot_couple = block_pphh_gs_1_phot = block_pphh_gs_1 -block_pphh_gs_1_couple_edge = block_pphh_gs_1_couple_inner = block_pphh_gs_1 -block_pphh_gs_1_phot_couple_edge = block_pphh_gs_1_phot_couple_inner = block_pphh_gs_1_phot2 = block_pphh_gs_1 - - - -""" -def block_gs_ph_1(hf, mp, intermediates): - def apply(ampl): - return AmplitudeVector(ph=(0 * ampl.ph)) - return AdcBlock(apply, 0) - -def block_gs_ph_1_phot(hf, mp, intermediates): - def apply(ampl): - return AmplitudeVector(ph=(0 * ampl.ph1)) - return AdcBlock(apply, 0) - -def block_gs_ph_1_phot_couple(hf, mp, intermediates): - def apply(ampl): - return AmplitudeVector(ph=(mp.qed_t1_df(b.ov) * (-ampl.gs1.as_float()))) - return AdcBlock(apply, 0) - -def block_gs_ph_1_couple(hf, mp, intermediates): - def apply(ampl): - return AmplitudeVector(ph=(0 * ampl.ph)) - return AdcBlock(apply, 0) -""" - - # # 1st order main # + def block_ph_ph_1(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - #omega = float(ReferenceState.get_qed_omega(hf)) # only for test purposes if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): - #diag_qed_term = einsum("klkl->", hf.oooo) diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - #+ (1/2) * einsum("klkl->", hf.oooo) + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) - #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) #reintroduced (actually canceled from -E_0 (1)) )) - #d_vv = zeros_like(hf.fvv) - #d_vv.set_mask("aa", 1.0) - def apply(ampl): return AmplitudeVector(ph=( # PT order + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 - #+ (1/2) * einsum("klkl->", hf.oooo) * ampl.ph + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph) - (1/2) * einsum("ib,ab->ia", ampl.ph, mp.qed_t0_df(b.vv)) - #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #reintroduced (actually canceled from -E_0 (1) )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): - #omega = float(ReferenceState.get_qed_omega(hf)) # omega is only here for a test...actually omega does not appear in this block if hasattr(hf, "first_order_coupling"): i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 @@ -583,25 +349,18 @@ def apply(ampl): - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 )) else: - diagonal = AmplitudeVector(ph=( #change to QED_AmplitudeVector + diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 )) - #print(diagonal.set_random()) - #np.insert(diagonal, 0, 0) - #diagonal = np.zeros(np.add(diagonal_.shape, np.array([0]))) - #diagonal[1:,1:] = diagonal_ def apply(ampl): - return AmplitudeVector(ph=( #change to QED_AmplitudeVector # PT order + return AmplitudeVector(ph=( # PT order + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 )) - #np.insert(mvprod, 0, 0) - #mvprod = np.zeros(np.add(mv_prod_.shape, np.array([0]))) - #mvprod[1:,1:] = mvprod_ - #return mvprod + else: diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 @@ -624,9 +383,7 @@ def block_ph_ph_1_phot(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): - #diag_qed_term = einsum("klkl->", hf.oooo) omega = float(ReferenceState.get_qed_omega(hf)) - # Build two Kronecker deltas d_oo = zeros_like(hf.foo) d_vv = zeros_like(hf.fvv) @@ -636,9 +393,7 @@ def block_ph_ph_1_phot(hf, mp, intermediates): diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - #+ (1/2) * einsum("klkl->", hf.oooo) + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) - #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) #reintroduced (actually canceled from -E_0 (1)) + einsum("ii,aa->ia", d_oo, d_vv) * omega )) @@ -647,10 +402,8 @@ def apply(ampl): + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 - #+ (1/2) * einsum("klkl->", hf.oooo) * ampl.ph + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) - (1/2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) - #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #reintroduced (actually canceled from -E_0 (1) + omega * ampl.ph1 )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): @@ -686,16 +439,14 @@ def apply(ampl): - einsum("IaIa->Ia", CvCv) # order 1 + einsum("ii,aa->ia", d_oo, d_vv) * omega )) - #np.insert(diagonal, 0, omega) def apply(ampl): - return AmplitudeVector(ph=( # PT order + return AmplitudeVector(ph=( # PT order + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 + omega * ampl.ph1 )) - #np.insert(mvprod, 0, omega) else: raise NotImplementedError("and not hasattr(hf, qed_hf)") return AdcBlock(apply, diagonal) @@ -703,46 +454,29 @@ def apply(ampl): def block_ph_ph_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) - if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): + if hasattr(hf, "coupling"): def apply(ampl): return AmplitudeVector(ph=( sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) )) - - #add_axis1 = - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov) - - #np.insert(mvprod, 0, - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov), axis=1) #ground to excited state coupling - #also insert axis0 zeros, so that final matvecproduct has dimesions (i+1) x (a+1) - #else: - # raise NotImplementedError("and not hasattr(hf, qed_hf)") - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_ph_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) - if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): + if hasattr(hf, "coupling"): def apply(ampl): return AmplitudeVector(ph=( sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) - - mp.qed_t1_df(b.ov) * ampl.gs1)#.as_float()) # gs_ph block + - mp.qed_t1_df(b.ov) * ampl.gs1) )) - - #add_axis0 = - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov) - - #np.insert(mvprod, 0, - np.sqrt(omega / 2) * mp.qed_t1_df(b.ov), axis=0) #ground to excited state coupling - #also insert axis1 zeros, so that final matvecproduct has dimesions (i+1) x (a+1) - #else: - # raise NotImplementedError("and not hasattr(hf, qed_hf)") - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_ph_1_couple_edge(hf, mp, intermediates): - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) - return AdcBlock(lambda ampl: 0, diagonal) + return AdcBlock(lambda ampl: 0, 0) block_ph_ph_1_phot_couple_edge = block_ph_ph_1_couple_edge @@ -750,34 +484,30 @@ def block_ph_ph_1_couple_edge(hf, mp, intermediates): def block_ph_ph_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) - if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): + if hasattr(hf, "coupling"): def apply(ampl): return AmplitudeVector(ph=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) - #+ (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part )) - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) - if hasattr(hf, "coupling"):# and not hasattr(hf, "qed_hf"): + if hasattr(hf, "coupling"): def apply(ampl): return AmplitudeVector(ph=( sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) - - mp.qed_t1_df(b.ov) * ampl.gs2)#.as_float()) # gs_ph block + - mp.qed_t1_df(b.ov) * ampl.gs2) )) - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_ph_1_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): - #diag_qed_term = einsum("klkl->", hf.oooo) omega = float(ReferenceState.get_qed_omega(hf)) # Build two Kronecker deltas @@ -789,21 +519,17 @@ def block_ph_ph_1_phot2(hf, mp, intermediates): diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - #+ (1/2) * einsum("klkl->", hf.oooo) + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) - #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) #reintroduced (actually canceled from -E_0 (1)) + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 )) def apply(ampl): - return AmplitudeVector(ph=( # PT order + return AmplitudeVector(ph=( # PT order + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 - #+ (1/2) * einsum("klkl->", hf.oooo) * ampl.ph + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) - (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) - #+ (1/2) * einsum("ia,ia->", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph #reintroduced (actually canceled from -E_0 (1) + 2 * omega * ampl.ph2 )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): @@ -840,10 +566,9 @@ def apply(ampl): - einsum("IaIa->Ia", CvCv) # order 1 + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 )) - #np.insert(diagonal, 0, omega) def apply(ampl): - return AmplitudeVector(ph=( # PT order + return AmplitudeVector(ph=( # PT order + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 @@ -911,8 +636,6 @@ def apply(ampl): # 1st order coupling # -# these blocks of equal photonic excitation are only valid for a QED-HF reference !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - def block_ph_pphh_1(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( @@ -987,15 +710,8 @@ def block_ph_pphh_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( - # usually this would be factor 1, but additional factor 2, due to normalization of pphh vector 2 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) - #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) - #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) - #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) )) - #if hasattr(hf, "first_order_coupling"): - # return AdcBlock(lambda ampl: 0, 0) - #else: return AdcBlock(apply, 0) @@ -1003,87 +719,22 @@ def block_ph_pphh_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(ph=( - # usually this would be factor 1, but additional factor 2, due to normalization of pphh vector 2 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) - #+ einsum("jb,jiba->ia", mp.qed_t1_df(b.ov), ampl.pphh) - #- einsum("kb,ikba->ia", mp.qed_t1_df(b.ov), ampl.pphh) - #- einsum("jc,jiac->ia", mp.qed_t1_df(b.ov), ampl.pphh)) )) - #if hasattr(hf, "first_order_coupling"): - # return AdcBlock(lambda ampl: 0, 0) - #else: return AdcBlock(apply, 0) block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner = block_pphh_ph_0_couple -""" -def block_pphh_ph_1_couple(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - def apply(ampl): - return AmplitudeVector(pphh=( - 4 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1(b.ov), einsum("ia,ia->ia", mp.df(b.ov), ampl.ph)).antisymmetrise(0,1).antisymmetrise(2,3) - #+ einsum("ia,jb,jb->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph) - #- einsum("ja,ib,ib->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph) - #- einsum("ib,ja,ja->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph)) - )) - return AdcBlock(apply, 0) -""" -""" -def block_pphh_ph_1_couple_inner(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - def apply(ampl): - return AmplitudeVector(pphh=( - 4 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1(b.ov), einsum("ia,ia->ia", mp.df(b.ov), ampl.ph1)).antisymmetrise(0,1).antisymmetrise(2,3) - + (1 - sqrt(2)) * 4 * sqrt(omega / 2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) - #+ einsum("ia,jb,jb->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph) - #- einsum("ja,ib,ib->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph) - #- einsum("ib,ja,ja->ijab", mp.qed_t1(b.ov), mp.df(b.ov), ampl.ph)) - )) - return AdcBlock(apply, 0) -""" block_ph_pphh_1_phot_couple = block_ph_pphh_1_phot_couple_inner = block_ph_pphh_0_phot_couple -""" -def block_ph_pphh_1_phot_couple(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - def apply(ampl): - return AmplitudeVector(ph=( - sqrt(omega/2) * einsum("kc,ia,ikac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) - + einsum("jb,ia,jiba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) - - einsum("kb,ia,ikba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) - - einsum("jc,ia,jiac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh1) - )) - return AdcBlock(apply, 0) -""" -""" -def block_ph_pphh_1_phot_couple_inner(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - def apply(ampl): - return AmplitudeVector(ph=( - sqrt(omega/2) * einsum("kc,ia,ikac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh2) - + einsum("jb,ia,jiba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh2) - - einsum("kb,ia,ikba->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh2) - - einsum("jc,ia,jiac->ia", mp.qed_t1(b.ov), mp.df(b.ov), ampl.pphh2) - + 4 * (1 - sqrt(2)) * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh2) - )) - return AdcBlock(apply, 0) -""" - def block_pphh_ph_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( - # usually this would be factor 4, but additional factor 1/2, due to normalization of pphh vector 2 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) - #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) - #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) - #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) )) - #if hasattr(hf, "first_order_coupling"): - # return AdcBlock(lambda ampl: 0, 0) - #else: return AdcBlock(apply, 0) @@ -1091,308 +742,87 @@ def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( - # usually this would be factor 4, but additional factor 1/2, due to normalization of pphh vector 2 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) - #+ einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), ampl.ph1) - #- einsum("ja,ib->ijab", mp.qed_t1_df(b.ov), ampl.ph1) - #- einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), ampl.ph1)) )) - #if hasattr(hf, "first_order_coupling"): - # return AdcBlock(lambda ampl: 0, 0) - #else: return AdcBlock(apply, 0) -""" -def block_pphh_ph_1_couple_edge(hf, mp, intermediates): - #def apply(ampl): - # return AmplitudeVector(pphh=mp.t2oo.zeros_like()) - #return AdcBlock(apply, 0) - return AdcBlock(lambda ampl: 0, 0) - -block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge - -def block_ph_pphh_1_couple_edge(hf, mp, intermediates): - #def apply(ampl): - # return AmplitudeVector(ph=mp.df(b.ov).zeros_like()) - #return AdcBlock(apply, 0) - return AdcBlock(lambda ampl: 0, 0) -""" - block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge = block_pphh_ph_0_couple block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge = block_ph_pphh_0_phot_couple -#block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner = block_pphh_ph_1_phot_couple = block_pphh_ph_1_phot_couple_inner = block_pphh_ph_1_couple_edge -#block_ph_pphh_1_couple = block_ph_pphh_1_couple_inner = block_ph_pphh_1_phot_couple = block_ph_pphh_1_phot_couple_inner = block_ph_pphh_1_couple_edge - - - # -# 2nd order gs blocks (gs_ph blocks in ph_ph) for now these are zero for testing purposes +# 2nd order gs blocks # def block_ph_gs_2(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - - ph_gs_inter = intermediates.qed_adc2_ph_gs_intermediate - - if hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): - def apply(ampl): # pretty sure this should be zero - return (einsum("jb,jb->", ( - #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - - ph_gs_inter), - #- 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - #- 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), - ampl.ph)) - - else: - raise NotImplementedError("There are still some adjustments to be done for QED-ADC(2) from a non-QED-HF reference") - def apply(ampl): - return (einsum("jb,jb->", ( - - 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - + 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - - 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov)) # still some terms are missing !!!!!!!!!!!!! - - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), - ampl.ph)) - return AdcBlock(lambda ampl: 0, 0) #AdcBlock(apply, 0) + return AdcBlock(lambda ampl: 0, 0) def block_ph_gs_2_phot_couple(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - diagonal = 0#- sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) - def apply(ampl): # pretty sure this should be zero - return ( sqrt(omega / 2) * einsum("jb,jb->", ( - - einsum("jckb,kc->jb", hf.ovov, mp.qed_t1(b.ov)) - + omega * mp.qed_t1(b.ov) - #- 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) - #+ 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) - #- 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) - + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), - ampl.ph1)) - return AdcBlock(lambda ampl: 0, 0)#AdcBlock(apply, diagonal) + return AdcBlock(lambda ampl: 0, 0) def block_ph_gs_2_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - diagonal = 0#- sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) def apply(ampl): return ( sqrt(omega / 2) * einsum("jb,jb->", ( - #- 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph) - sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph)) # 1. order - #maybe this whole block is just + einsum("kjbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_gs_2_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - - ph_gs_inter = intermediates.qed_adc2_ph_gs_intermediate - # 1. order - diagonal = omega #- omega * (sqrt(2) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + omega def apply(ampl): - return (#(einsum("jb,jb->", ( - #+ 0.5 * omega * mp.qed_t0(b.ov) - #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - # + 2 * ( - # - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - # ) - #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - # - ph_gs_inter), - #- 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - #- 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), - # ampl.ph1) - + omega * ampl.gs1) # 1. order - #(omega * einsum("jb,jb->", einsum("jj,bb->jb", d_oo, d_vv), ampl.ph1) - #- (omega / 2) * (sqrt(2) - 1) * einsum("jb,jb->", (einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - # - einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # - einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) - # + einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo))), - # ampl.ph1)) - return AdcBlock(apply, diagonal) + return omega * ampl.gs1 # 1. order + + return AdcBlock(apply, omega) def block_ph_gs_2_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - - ph_gs_inter = intermediates.qed_adc2_ph_gs_intermediate - # 1. order - diagonal = 2 * omega #- omega * (sqrt(3) - 1) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) + 2 * omega def apply(ampl): - return (#einsum("jb,jb->", ( - # + omega * mp.qed_t0(b.ov) - #- 0.25 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - #+ 0.25 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) - # + 3 * ( - # - (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ (omega / 2) * einsum("jc,bc->jb", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # + (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- (omega / 2) * einsum("kb,jk->jb", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - # ) - #- 0.5 * einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t0_df(b.ov))), - # - ph_gs_inter), - #- 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - #- 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)), - # ampl.ph2) - + 2 * omega * ampl.gs2) # 1. order - return AdcBlock(apply, diagonal) + return 2 * omega * ampl.gs2 # 1. order + return AdcBlock(apply, 2 * omega) def block_ph_gs_2_phot_couple_inner(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - diagonal = 0#- sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) - def apply(ampl): - return ( sqrt(omega) * einsum("jb,jb->", ( - - einsum("jckb,kc->jb", hf.ovov, mp.qed_t1(b.ov)) - + 2 * omega * mp.qed_t1(b.ov) - #- 0.5 * einsum("bc,jc->jb", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) * sqrt(2) - #+ 0.5 * einsum("jk,kb->jb", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) * sqrt(2) - #- 0.5 * sqrt(2) * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * sqrt(2) * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov)) - + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), - ampl.ph2) - )#+ (1 - sqrt(2)) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2)) # 1. order - return AdcBlock(lambda ampl: 0, diagonal) - + return AdcBlock(lambda ampl: 0, 0) def block_ph_gs_2_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - - diagonal = 0#- sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) def apply(ampl): return ( sqrt(omega) * einsum("jb,jb->", ( - #- 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("jc,bc->jb", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("kb,jk->jb", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), ampl.ph1) - sqrt(omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1)) # 1. order - #maybe this whole block is just + einsum("kjbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_gs_2_phot_couple_edge(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) - - def apply(ampl): - return (- 0.5 * omega * sqrt(2) * (einsum("jc,bc,jb->", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv), ampl.ph2) - - einsum("kb,jk,jb", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo), ampl.ph2))) - return AdcBlock(lambda ampl: 0, 0) def block_ph_gs_2_couple_edge(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - diagonal = - 0.5 * omega * sqrt(2) * einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t1(b.ov)) - return AdcBlock(lambda ampl: 0, 0) - # # 2nd order main # def block_ph_ph_2(hf, mp, intermediates): - #omega = float(ReferenceState.get_qed_omega(hf)) i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 - #intermediates expect a dimensionality unequal zero, so qed_i0 cannot be cashed this way, but its just a sum over two indices - #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] - #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) - #qed_i1 = intermediates.adc2_qed_i1 - #qed_i2 = intermediates.adc2_qed_i2 - - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) term_t2_eri = intermediates.term_t2_eri - #term_t2_eri = ( - # + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) - # + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) - #).evaluate() - - if hasattr(hf, "coupling"): + if hasattr(hf, "coupling") and not hasattr(hf, "approx"): omega = float(ReferenceState.get_qed_omega(hf)) - #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] - #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) - #qed_i1 = intermediates.adc2_qed_ph_ph_2_i1 - #qed_i2 = intermediates.adc2_qed_ph_ph_2_i2 - #qed_i1 = intermediates.adc2_qed_i1 - #qed_i2 = intermediates.adc2_qed_i2 - #qed_i1_0 = intermediates.adc2_qed_i1_0 - #qed_i2_0 = intermediates.adc2_qed_i2_0 - #qed_gs_part = intermediates.adc2_qed_ph_ph_2_gs_part if hasattr(hf, "qed_hf"): qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 @@ -1400,10 +830,8 @@ def block_ph_ph_2(hf, mp, intermediates): + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * (#qed_i0 #this term and the following are additional qed terms + + (-omega/2) * ( - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) - #+ (1/2) * einsum("iaia->ia", einsum("ia,jb->iajb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) - # + einsum("jb,ia->iajb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)))) + (1/2) * 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) )) print(mp.energy_correction(2)) @@ -1413,69 +841,14 @@ def apply(ampl): - einsum("ij,ja->ia", i2, ampl.ph) - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 - + (-omega/2) * (#qed_i0 * ampl.ph #this term and the following are additional qed terms + + (-omega/2) * ( - einsum("ib,ab->ia", ampl.ph, qed_i1) - einsum("ij,ja->ia", qed_i2, ampl.ph) - #+ (1/2) * einsum("iajb,jb->ia", einsum("ia,jb->iajb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) - # + einsum("jb,ia->iajb", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)), ampl.ph)) + (1/2) * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph) + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph))) )) else: - raise NotImplementedError("QED-ADC(2) form non-QED-HF reference is not implemented") - diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) - - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) - + (-omega/2) * ( - #- direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) - + (1/2) * 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - #+ direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - - einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - - einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order - - #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) - #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) - #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) - )) - print(mp.energy_correction(2)) - def apply(ampl): - return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph, i1) - - einsum("ij,ja->ia", i2, ampl.ph) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 - - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 - + einsum("ib,ab->ia", ampl.ph, qed_i1) - + einsum("ij,ja->ia", qed_i2, ampl.ph) - + (-omega/2) * ( - #- einsum("ib,ab->ia", ampl.ph, qed_i1) - #- einsum("ij,ja->ia", qed_i2, ampl.ph) - + (1/2) * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph) - + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph))) - #+ einsum("ib,ab->ia", ampl.ph, qed_i1_0) - #+ einsum("ij,ja->ia", qed_i2_0, ampl.ph) - #+ (1/2) * (einsum("ka,jkib,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph) #this can be done by symmetrize a,b - # + einsum("kb,jkia,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph)) - #+ (1/2) * (einsum("ic,jabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph) #this can be done by symmetrize i,j - # + einsum("jc,iabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph)) - + einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) - + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph) - + qed_gs_part * ampl.gs.as_float() - + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph) # 1. order - - (1/2) * einsum("ib,ab->ia", ampl.ph, mp.qed_t0_df(b.vv)) # 1. order - #+ (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution - # - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - # - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - # #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - # #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - # - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) * ampl.gs.as_float() - #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph # reintroduced (actually canceled from -E_0 (2)) - #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) * ampl.ph - #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) * ampl.ph - )) + raise NotImplementedError("QED-ADC(2) from non-QED-HF reference is not implemented") else: diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) @@ -1483,23 +856,12 @@ def apply(ampl): - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) )) - # Not used anywhere else, so kept as an anonymous intermediate - #term_t2_eri = ( - # + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) - # + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) - #).evaluate() - def apply(ampl): - #print("using non-qed matrix vector products") return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, i1) - einsum("ij,ja->ia", i2, ampl.ph) - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 - #+ (-omega/2) * (qed_i0 * ampl.ph #this term and the following are additional qed terms - #- einsum("ib,ab->ia", ampl.ph, qed_i1) - #- einsum("ij,ja->ia", qed_i2, ampl.ph) - #+ einsum("ijab,jb->ia", einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)), ampl.ph)) )) return AdcBlock(apply, diagonal) @@ -1520,161 +882,61 @@ def apply(ampl): return AdcBlock(apply, diagonal) -def block_ph_ph_2_couple(hf, mp, intermediates): #one could cash some of the terms here - #if hasattr(hf, "qed_hf"): - # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +def block_ph_ph_2_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - #gs_part = intermediates.adc2_qed_ph_ph_2_couple_gs_part qed_i1 = intermediates.adc2_qed_couple_i1 qed_i2 = intermediates.adc2_qed_couple_i2 - #couple_inter = intermediates.qed_adc2_ph_couple_intermediate - - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): - return AmplitudeVector(ph=(#0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), - # direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph) - # - einsum("kb,ka,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph) - # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph) # this is different from mp.diff_df, but why??? - # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) - # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph)) # this is different from mp.diff_df, but why??? + return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, qed_i1) + einsum("ij,ja->ia", qed_i2, ampl.ph) - #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph - #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed - #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - + sqrt(omega / 2) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed - #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + + sqrt(omega / 2) * ( + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # electric H_1 term + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) # electric H_1 term - #+ einsum("ijab,jb->ia", - #couple_inter, - #einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov) - #+ einsum("ic,jabc->ijab", mp.qed_t1(b.ov), hf.ovvv), - #ampl.ph)) - #+ gs_part * ampl.gs.as_float() + sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) # 1. order - #+ sqrt(omega / 2) * ( # gs_ph contribution - # - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) - # + omega * mp.qed_t1(b.ov) - # - 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) - # + 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) - # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - # #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - # #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - # + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) - # * ampl.gs.as_float() )) - return AdcBlock(apply, diagonal) - -#def block_ph_ph_2_couple(hf, mp, intermediates): #for testing -# if hasattr(hf, "qed_hf"): -# raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") -# #omega = float(ReferenceState.get_qed_omega(hf)) -# diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) -# return AdcBlock(lambda ampl: 0, diagonal) + return AdcBlock(apply, 0) -def block_ph_ph_2_phot_couple(hf, mp, intermediates): #one could cash some of the terms here - #if hasattr(hf, "qed_hf"): - # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +def block_ph_ph_2_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) gs_part = intermediates.adc2_qed_ph_ph_2_phot_couple_gs_part qed_i1 = intermediates.adc2_qed_phot_couple_i1 qed_i2 = intermediates.adc2_qed_phot_couple_i2 - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): - return AmplitudeVector(ph=(#0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), - # direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph1) - # - einsum("ka,kb,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph1) - # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? - # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) - # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? + return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph1, qed_i1) + einsum("ij,ja->ia", qed_i2, ampl.ph1) - #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 - #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) - #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + sqrt(omega / 2) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) - #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + + sqrt(omega / 2) * ( + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term - #+ einsum("ijab,jb->ia", - #einsum("kb,ikja->ijab", mp.qed_t1(b.ov), hf.ooov) - #+ einsum("jc,ibac->ijab", mp.qed_t1(b.ov), hf.ovvv), - #ampl.ph1)) - #+ einsum("ijab,ia->jb", einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov), ampl.ph1) - #+ einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) - #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part - + gs_part * ampl.gs1#.as_float() + + gs_part * ampl.gs1 + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs1)#.as_float()) # gs_ph block # 1. order - #+ sqrt(omega / 2) * ( #gs_ph part - # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) - # + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) - # - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - # + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) - # * ampl.gs1.as_float() + - mp.qed_t1_df(b.ov) * ampl.gs1) # gs_ph block 1. order )) return AdcBlock(apply, diagonal) -#def block_ph_ph_2_phot_couple(hf, mp, intermediates): #for testing -# if hasattr(hf, "qed_hf"): -# raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") -# #omega = float(ReferenceState.get_qed_omega(hf)) -# diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) -# return AdcBlock(lambda ampl: 0, diagonal) - - - -def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms here - #if hasattr(hf, "qed_hf"): - # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +def block_ph_ph_2_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 - #intermediates expect a dimensionality unequal zero, so qed_i0 cannot be cashed this way, but its just a sum over two indices - #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] - #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) - #qed_i1 = intermediates.adc2_qed_i1 - #qed_i2 = intermediates.adc2_qed_i2 term_t2_eri = intermediates.term_t2_eri - #term_t2_eri = ( - # + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) - # + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) - #).evaluate() - d_oo = zeros_like(hf.foo) d_vv = zeros_like(hf.fvv) d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - #omega = float(ReferenceState.get_qed_omega(hf)) - #qed_i0_terms = [(mp.qed_t1_df(b.ov), mp.qed_t1(b.ov))] - #qed_i0 = 2 * sum(qed_t1_df.dot(qed_t1) for qed_t1_df, qed_t1 in qed_i0_terms) qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 - #qed_i1_0 = intermediates.adc2_qed_i1_0 - #qed_i2_0 = intermediates.adc2_qed_i2_0 - #gs_part = intermediates.adc2_qed_ph_ph_2_phot_gs_part + diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) @@ -1682,19 +944,9 @@ def block_ph_ph_2_phot(hf, mp, intermediates): #one could cash some of the terms + (-omega/2) * 2 * ( - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - #+ direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - #- einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - #- einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) - #+ (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) - #+ (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + einsum("ii,aa->ia", d_oo, d_vv) * omega # 1. order - #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) # reintroduced (actually canceled from -E_0 (2)) - #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) - #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) )) - #print("with d_vv", d_vv * mp.qed_t1_df(b.vv)) - #print("without d_vv", mp.qed_t1_df(b.vv)) def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph1, i1) @@ -1706,56 +958,18 @@ def apply(ampl): - einsum("ij,ja->ia", qed_i2, ampl.ph1) + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph1))) - #+ einsum("ib,ab->ia", ampl.ph1, qed_i1_0) - #+ einsum("ij,ja->ia", qed_i2_0, ampl.ph1) - #+ (1/2) * (einsum("ka,jkib,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph1) #this can be done by symmetrize a,b - # + einsum("kb,jkia,jb->ia", mp.qed_t0(b.ov), hf.ooov, ampl.ph1)) - #+ (1/2) * (einsum("ic,jabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph1) #this can be done by symmetrize i,j - # + einsum("jc,iabc,jb->ia", mp.qed_t0(b.ov), hf.ovvv, ampl.ph1)) - #+ einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) - # + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph1) - #+ (1 - sqrt(2)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 - #+ omega * einsum("ii,aa->ia", d_oo, d_vv) * ampl.gs1.as_float() #this (and following) is from the gs_ph contribution - #- (omega / 2) * (sqrt(2) - 1) * (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - # - einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) - # + einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo))) - # * ampl.gs1.as_float() - #+ gs_part * ampl.gs1.as_float() - #+ (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) # 1. order - #- (1/2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) # 1. order + omega * ampl.ph1 # 1. order - #+ (0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution - # - 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - # + sqrt(2) * ( - # - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - # + (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) - # - (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - # ) - # - 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) - # + 0.5 * omega * mp.qed_t0(b.ov)) * ampl.gs1.as_float() - #- (omega/2) * einsum("ia,ia->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph # reintroduced (actually canceled from -E_0 (2)) - #- (1/4) * einsum("ia,ia->", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) * ampl.ph - #- (1/4) * einsum("ijab,ijab->", mp.td2(b.oovv), einsum("ia,jb->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - einsum("ib,ja->ijab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov))) * ampl.ph )) return AdcBlock(apply, diagonal) -def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the terms here - #if hasattr(hf, "qed_hf"): - # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +def block_ph_ph_2_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 term_t2_eri = intermediates.term_t2_eri - #term_t2_eri = ( - # + einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) - # + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo) - #).evaluate() - d_oo = zeros_like(hf.foo) d_vv = zeros_like(hf.fvv) d_oo.set_mask("ii", 1.0) @@ -1763,9 +977,7 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the term qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 - #qed_i1_0 = intermediates.adc2_qed_i1_0 - #qed_i2_0 = intermediates.adc2_qed_i2_0 - #gs_part = intermediates.adc2_qed_ph_ph_2_phot2_gs_part + diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) @@ -1773,11 +985,6 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): #one could cash some of the term + (-omega/2) * 3 * ( - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - #+ direct_sum("a+i->ia", qed_i1_0.diagonal(), qed_i2_0.diagonal()) - #- einsum("ka,ikia->ia", mp.qed_t0(b.ov), hf.ooov) - #- einsum("ic,iaac->ia", mp.qed_t0(b.ov), hf.ovvv) - #+ (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * einsum("ii,aa->ia", d_oo, d_vv) - #+ (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) # 1. order + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 # 1. order )) @@ -1792,159 +999,71 @@ def apply(ampl): - einsum("ij,ja->ia", qed_i2, ampl.ph2) + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph2) + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph2))) - #+ einsum("ib,ab->ia", ampl.ph2, qed_i1_0) - #+ einsum("ij,ja->ia", qed_i2_0, ampl.ph2) - #+ einsum("ijab,jb->ia", einsum("jkib,ka->ijab", hf.ooov, mp.qed_t0(b.ov)).symmetrise(2, 3) - # + einsum("ic,jabc->ijab", mp.qed_t0(b.ov), hf.ovvv).symmetrise(0, 1), ampl.ph2) - #+ (1 - sqrt(3)) * omega * einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 - #+ gs_part * ampl.gs2.as_float() - #+ (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) # 1. order - #- (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) # 1. order + 2 * omega * ampl.ph2 # 1. order )) return AdcBlock(apply, diagonal) -def block_ph_ph_2_couple_inner(hf, mp, intermediates): #one could cash some of the terms here - #if hasattr(hf, "qed_hf"): - # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +def block_ph_ph_2_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - #gs_part = intermediates.adc2_qed_ph_ph_2_couple_inner_gs_part qed_i1 = intermediates.adc2_qed_couple_i1 qed_i2 = intermediates.adc2_qed_couple_i2 - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): - return AmplitudeVector(ph=(#0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), - # direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph1) - # - einsum("kb,ka,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph) - # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1) # this is different from mp.diff_df, but why??? - # - einsum("jc,ic,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph)) - # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph1)) # this is different from mp.diff_df, but why??? + return AmplitudeVector(ph=( + sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) - #- 0.5 * sqrt(omega) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph1 - #- einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph) # could be cashed - #- einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph)) # could be cashed - + sqrt(omega) * (#einsum("kc,kjic,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # could be cashed - #+ einsum("kc,kacb,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph) # could be cashed + + sqrt(omega) * ( + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term - #+ gs_part * ampl.gs1.as_float() - #+ (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) - #- (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( - # (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph1 - # - einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) - # - einsum("kb,ka,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) - # - einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) - # - einsum("jc,ic,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph1) - # + einsum("jb,jb->", mp.qed_t0(b.ov), ampl.ph1) * mp.qed_t1_df(b.ov) - # + einsum("jb,jb->", mp.qed_t0_df(b.ov), ampl.ph1) * mp.qed_t1(b.ov) - #) + sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) # 1. order - #+ (1 - sqrt(2)) * sqrt(omega / 2) * mp.qed_t1_df(b.ov) * ampl.gs1.as_float() # gs part # 1. order )) - return AdcBlock(apply, diagonal) - -#def block_ph_ph_2_couple(hf, mp, intermediates): #for testing -# if hasattr(hf, "qed_hf"): -# raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") -# #omega = float(ReferenceState.get_qed_omega(hf)) -# diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) -# return AdcBlock(lambda ampl: 0, diagonal) + return AdcBlock(apply, 0) -def block_ph_ph_2_phot_couple_inner(hf, mp, intermediates): #one could cash some of the terms here - #if hasattr(hf, "qed_hf"): - # raise NotImplementedError("QED-ADC(2) has not been implemented with qed_hf reference") +def block_ph_ph_2_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) gs_part = intermediates.adc2_qed_ph_ph_2_phot_couple_inner_gs_part qed_i1 = intermediates.adc2_qed_phot_couple_i1 qed_i2 = intermediates.adc2_qed_phot_couple_i2 - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): - return AmplitudeVector(ph=(#0.5 * sqrt(omega / 2) * (einsum("kc,kc,acik,ia->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), - # direct_sum("ia-kc->acik", mp.df(b.ov), mp.df(b.ov)), ampl.ph2) - # - einsum("ka,kb,ik,ib->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.oo), ampl.ph1) - # einsum("ic,kc->ik", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2) # this is different from mp.diff_df, but why??? - # - einsum("ic,jc,ac,ja->ia", mp.qed_t0(b.ov), mp.qed_t1(b.ov), #mp.diff_df(b.vv), ampl.ph1)) - # einsum("ka,kc->ac", mp.df(b.ov), - mp.df(b.ov)), ampl.ph2)) # this is different from mp.diff_df, but why??? + return AmplitudeVector(ph=( + sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) - #- 0.5 * sqrt(omega / 2) * einsum("kc,kc->", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 - #- einsum("kb,ka,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1) - #- einsum("jc,ic,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph1)) - + sqrt(omega) * (#einsum("kc,kijc,ja->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) - #+ einsum("kc,kbca,ib->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1) + + sqrt(omega) * ( + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term - #- sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1(b.ov), hf.oovv) * ampl.gs1.as_float() #gs_ph part - + gs_part * ampl.gs2#.as_float() - #+ sqrt(omega / 2) * ( #gs_ph part - # - 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) - # + 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - # + 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) - # - 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - # + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) - # * ampl.gs1.as_float() - #+ (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) * mp.qed_t0(b.ov) - #- (sqrt(2) - 1) * 0.5 * sqrt(omega/2) * ( - # (einsum("kc,kc->", mp.qed_t1_df(b.ov), mp.qed_t0(b.ov)) + einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t0_df(b.ov))) * ampl.ph2 - # - einsum("ka,kb,ib->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - # - einsum("ka,kb,ib->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) - # - einsum("ic,jc,ja->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - # - einsum("ic,jc,ja->ia", mp.qed_t0_df(b.ov), mp.qed_t1(b.ov), ampl.ph2) - # + einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph2) * mp.qed_t0(b.ov) - # + einsum("jb,jb->", mp.qed_t1(b.ov), ampl.ph2) * mp.qed_t0_df(b.ov) - #) + + gs_part * ampl.gs2 + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs2)#.as_float()) # gs_ph block # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs2) # gs_ph block # 1. order )) - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_ph_2_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): return - (omega/2) * sqrt(2) * ( einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph - einsum("ka,kb,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - einsum("ic,jc,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - #+ einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph) * mp.qed_t1(b.ov) - #+ (einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - # - einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo))) * ampl.gs.as_float() # gs-part ) - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_ph_2_phot_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): return - (omega/2) * sqrt(2) * ( einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 - einsum("kb,ka,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - einsum("jc,ic,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - #+ einsum("jb,ia,jb->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - #+ einsum("jb,jb->", mp.qed_t1(b.ov), ampl.ph2) * mp.qed_t1_df(b.ov) ) - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) @@ -2042,149 +1161,39 @@ def adc2_i2(hf, mp, intermediates): # This definition differs from libadc. It additionally has the hf.foo term. return hf.foo - 0.5 * einsum("ikab,jkab->ij", mp.t2oo, hf.oovv).symmetrise() + # qed intermediates for adc2, without the factor of (omega/2), which is added in the actual matrix builder @register_as_intermediate def adc2_qed_i1(hf, mp, intermediates): # maybe do this with symmetrise - #return (1/2) * einsum("kb,ka->ab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) return (1/2) * (einsum("kb,ka->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + einsum("ka,kb->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) @register_as_intermediate def adc2_qed_i2(hf, mp, intermediates): # maybe do this with symmetrise - #return (1/2) * einsum("jc,ic->ij", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) return (1/2) * (einsum("jc,ic->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + einsum("ic,jc->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) -#qed intermediates for adc2, for non-qed-hf input -""" -@register_as_intermediate -def adc2_qed_i1_0(hf, mp, intermediates): - #return (1/2) * einsum("kb,ka->ab", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - return ((1/8) * (einsum("kb,ka->ab", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) - + einsum("ka,kb->ab", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov))) - + einsum("kc,kacb->ab", mp.qed_t0(b.ov), hf.ovvv)) - - -@register_as_intermediate -def adc2_qed_i2_0(hf, mp, intermediates): - #return (1/2) * einsum("jc,ic->ij", mp.qed_t1_df(b.ov), mp.qed_t1_df(b.ov)) - return ((1/8) * (einsum("jc,ic->ij", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov)) - + einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t0_df(b.ov))) - + einsum("kc,kjic->ij", mp.qed_t0(b.ov), hf.ooov)) -""" -#@register_as_intermediate -#def adc2_qed_ph_ph_2_i1(hf, mp, intermediates): -# omega = float(ReferenceState.get_qed_omega(hf)) -# return (omega / 2) * intermediates.adc2_qed_i1.evaluate() + intermediates.adc2_qed_i1_0.evaluate() - -#@register_as_intermediate -#def adc2_qed_ph_ph_2_i2(hf, mp, intermediates): -# omega = float(ReferenceState.get_qed_omega(hf)) -# return (omega / 2) * intermediates.adc2_qed_i2.evaluate() + intermediates.adc2_qed_i2_0.evaluate() @register_as_intermediate def qed_adc2_ph_gs_intermediate(hf, mp, intermediates): return (0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) + 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) + @register_as_intermediate def term_t2_eri(hf, mp, intermediates): return (einsum("ijab,jkbc->ikac", mp.t2oo, hf.oovv) + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo)) - #).evaluate() -#@register_as_intermediate -#def qed_adc2_ph_couple_intermediate(hf, mp, intermediates): -# return (einsum("ka,jkib->ijab", mp.qed_t1(b.ov), hf.ooov) -# + einsum("ic,jabc->ijab", mp.qed_t1(b.ov), hf.ovvv)) - -#@register_as_intermediate -#def qed_adc2_ph_phot_couple_intermediate(hf, mp, intermediates): -# return "blub" -""" -@register_as_intermediate -def adc2_qed_ph_ph_2_gs_part(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - - return (#0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution - #- 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - - (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) - - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) -""" -""" -@register_as_intermediate -def adc2_qed_ph_ph_2_couple_gs_part(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - return sqrt(omega / 2) * ( # gs_ph contribution - - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) - + omega * mp.qed_t1(b.ov) - #- 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) - #+ 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) - #- 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) - #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) -""" -""" -@register_as_intermediate -def adc2_qed_ph_ph_2_couple_inner_gs_part(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - return sqrt(omega) * ( # gs_ph contribution - - einsum("kaic,kc->ia", hf.ovov, mp.qed_t1(b.ov)) - + 2 * omega * mp.qed_t1(b.ov) - #- 0.5 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t1(b.ov)) - #+ 0.5 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t1(b.ov)) - #+ sqrt(2) * ( - #- 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) - #+ einsum("jkbc,kc->jb", hf.oovv, mp.qed_t1(b.ov))) -""" @register_as_intermediate def adc2_qed_ph_ph_2_phot_couple_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - #d_oo = zeros_like(hf.foo) - #d_vv = zeros_like(hf.fvv) - #d_oo.set_mask("ii", 1.0) - #d_vv.set_mask("aa", 1.0) - - return sqrt(omega / 2) * ( #gs_ph part - #- 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + return sqrt(omega / 2) * ( + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) - #- mp.qed_t1_df(b.ov)) # 1. order @register_as_intermediate @@ -2196,116 +1205,38 @@ def adc2_qed_ph_ph_2_phot_couple_inner_gs_part(hf, mp, intermediates): d_oo.set_mask("ii", 1.0) d_vv.set_mask("aa", 1.0) - return sqrt(omega) * ( #gs_ph part - #- 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("ic,ac->ia", mp.qed_t0(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("ka,ik->ia", mp.qed_t0(b.ov), d_oo * mp.qed_t1_df(b.oo)) + return sqrt(omega) * ( + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) - #- mp.qed_t1_df(b.ov)) -""" -@register_as_intermediate -def adc2_qed_ph_ph_2_phot_gs_part(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - - return (#0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution - #- 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - #+ sqrt(2) * ( - #- (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) - #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - #) - #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) - #+ 0.5 * omega * mp.qed_t0(b.ov)) - - (omega) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) - - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) -""" -""" -@register_as_intermediate -def adc2_qed_ph_ph_2_phot2_gs_part(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - - return (#0.25 * einsum("ik,ka->ia", mp.qed_t0_df(b.oo), mp.qed_t0(b.ov)) #this is from the gs_ph contribution - #- 0.25 * einsum("ac,ic->ia", mp.qed_t0_df(b.vv), mp.qed_t0(b.ov)) - #+ sqrt(3) * ( - #- (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv)) - #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - #+ (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo)) - #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - #) - #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov)) - #+ omega * mp.qed_t0(b.ov)) - - (omega/2) * 3 * einsum("ic,ac->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.vv) - d_vv * mp.qed_t1_df(b.vv)) - #+ (omega/2) * einsum("ic,ac->ia", mp.qed_t1(b.ov), d_vv * mp.qed_t1_df(b.vv)) - + (omega/2) * 3 * einsum("ka,ik->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.oo) - d_oo * mp.qed_t1_df(b.oo)) - #- (omega/2) * einsum("ka,ik->ia", mp.qed_t1(b.ov), d_oo * mp.qed_t1_df(b.oo)) - #- 0.5 * einsum("ikac,kc->ia", mp.t2oo, mp.qed_t0_df(b.ov))) - - 0.5 * einsum("jkib,jkab->ia", hf.ooov, mp.t2oo) - - 0.5 * einsum("ijbc,jabc->ia", mp.t2oo, hf.ovvv)) -""" @register_as_intermediate def adc2_qed_couple_i1(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (#0.5 * einsum("ka,kb->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + return ( sqrt(omega / 2) * ( + einsum("kc,kacb->ab", mp.qed_t1(b.ov), hf.ovvv))) @register_as_intermediate def adc2_qed_couple_i2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (#0.5 * einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + return ( sqrt(omega / 2) * ( + einsum("kc,kjic->ij", mp.qed_t1(b.ov), hf.ooov))) @register_as_intermediate def adc2_qed_phot_couple_i1(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (#0.5 * einsum("ka,kb->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + return ( sqrt(omega / 2) * ( + einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) @register_as_intermediate def adc2_qed_phot_couple_i2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (#0.5 * einsum("ic,jc->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) + return ( sqrt(omega / 2) * ( + einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) -#@register_as_intermediate -#def adc2_qed_phot_couple_i1(hf, mp, intermediates): -# omega = float(ReferenceState.get_qed_omega(hf)) -# return ( sqrt(omega / 2) * (#0.5 * einsum("kb,ka->ab", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) -# + einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) - - -#@register_as_intermediate -#def adc2_qed_phot_couple_i2(hf, mp, intermediates): -# omega = float(ReferenceState.get_qed_omega(hf)) -# return ( sqrt(omega / 2) * (#0.5 * einsum("jc,ic->ij", mp.qed_t0(b.ov), mp.qed_t1_df(b.ov)) -# + einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) - - - - - def adc3_i1(hf, mp, intermediates): # Used for both CVS and general td2 = mp.td2(b.oovv) diff --git a/adcc/adc_pp/state2state_transition_dm.py b/adcc/adc_pp/state2state_transition_dm.py index f7ea83e7..7abb2e0d 100644 --- a/adcc/adc_pp/state2state_transition_dm.py +++ b/adcc/adc_pp/state2state_transition_dm.py @@ -42,6 +42,84 @@ def s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates): return dm +def s2s_tdm_qed_adc2_diag_part(mp, amplitude_l, amplitude_r, intermediates): + dm = s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates) + ul1 = amplitude_l.ph + ur1 = amplitude_r.ph + p0_oo = dm.oo.evaluate() + p0_vv = dm.vv.evaluate() + + dm_new = OneParticleOperator(mp, is_symmetric=False) + + dm_new.ov = ( + - einsum("kb,ab->ka", mp.qed_t1(b.ov), p0_vv) + + einsum("ji,ic->jc", p0_oo, mp.qed_t1(b.ov)) + + ul1.dot(mp.qed_t1(b.ov)) * ur1 + ) / 2 + + dm_new.vo = ( + - einsum("kb,ba->ak", mp.qed_t1(b.ov), p0_vv) + + einsum("ij,ic->cj", p0_oo, mp.qed_t1(b.ov)) + + ur1.dot(mp.qed_t1(b.ov)) * ul1.transpose() + ) / 2 + + return dm_new + +def s2s_tdm_qed_adc2_edge_part_couple(mp, amplitude_l, amplitude_r, intermediates): + dm = s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates) + ul1 = amplitude_l.ph + ur1 = amplitude_r.ph + p0_oo = dm.oo.evaluate() + p0_vv = dm.vv.evaluate() + + dm_new = OneParticleOperator(mp, is_symmetric=False) + + dm_new.ov = (mp.qed_t1(b.ov) * ul1.dot(ur1) + - einsum("kb,ab->ka", mp.qed_t1(b.ov), p0_vv) + + einsum("ji,ic->jc", p0_oo, mp.qed_t1(b.ov)) + ) + + return dm_new + +def s2s_tdm_qed_adc2_edge_part_phot_couple(mp, amplitude_l, amplitude_r, intermediates): + dm = s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates) + ul1 = amplitude_l.ph + ur1 = amplitude_r.ph + p0_oo = dm.oo.evaluate() + p0_vv = dm.vv.evaluate() + + dm_new = OneParticleOperator(mp, is_symmetric=False) + + dm_new.vo = (einsum("ia->ai", mp.qed_t1(b.ov)) * ul1.dot(ur1) + - einsum("kb,ba->ak", mp.qed_t1(b.ov), p0_vv) + + einsum("ij,ic->cj", p0_oo, mp.qed_t1(b.ov)) + ) + + return dm_new + + +def s2s_tdm_qed_adc2_ph_pphh_coupl_part(mp, amplitude_l, amplitude_r, intermediates): + ul1 = amplitude_l.ph + ur2 = amplitude_r.pphh + + dm = OneParticleOperator(mp, is_symmetric=False) + + dm.ov = -2 * einsum("jb,ijab->ia", ul1, ur2) + + return dm + + +def s2s_tdm_qed_adc2_pphh_ph_phot_coupl_part(mp, amplitude_l, amplitude_r, intermediates): + ul2 = amplitude_l.pphh + ur1 = amplitude_r.ph + + dm = OneParticleOperator(mp, is_symmetric=False) + + dm.vo = -2 * einsum("ijab,jb->ai", ul2, ur1) + + return dm + + def s2s_tdm_adc2(mp, amplitude_l, amplitude_r, intermediates): check_doubles_amplitudes([b.o, b.o, b.v, b.v], amplitude_l, amplitude_r) dm = s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates) @@ -104,6 +182,11 @@ def s2s_tdm_adc2(mp, amplitude_l, amplitude_r, intermediates): # Ref: https://doi.org/10.1080/00268976.2013.859313 DISPATCH = {"adc0": s2s_tdm_adc0, "adc1": s2s_tdm_adc0, # same as ADC(0) + "qed_adc2_diag": s2s_tdm_qed_adc2_diag_part, + "qed_adc2_edge_couple": s2s_tdm_qed_adc2_edge_part_couple, + "qed_adc2_edge_phot_couple": s2s_tdm_qed_adc2_edge_part_phot_couple, + "qed_adc2_ph_pphh": s2s_tdm_qed_adc2_ph_pphh_coupl_part, + "qed_adc2_pphh_ph": s2s_tdm_qed_adc2_pphh_ph_phot_coupl_part, "adc2": s2s_tdm_adc2, "adc2x": s2s_tdm_adc2, # same as ADC(2) } @@ -143,8 +226,12 @@ def state2state_transition_dm(method, ground_state, amplitude_from, raise NotImplementedError("state2state_transition_dm is not implemented " f"for {method.name}.") else: - # final state is on the bra side/left (complex conjugate) - # see ref https://doi.org/10.1080/00268976.2013.859313, appendix A2 - ret = DISPATCH[method.name](ground_state, amplitude_to, amplitude_from, + if hasattr(ground_state, "s2s_contribution"): + ret = DISPATCH[ground_state.s2s_contribution](ground_state, amplitude_to, amplitude_from, intermediates) + else: + # final state is on the bra side/left (complex conjugate) + # see ref https://doi.org/10.1080/00268976.2013.859313, appendix A2 + ret = DISPATCH[method.name](ground_state, amplitude_to, amplitude_from, + intermediates) return ret.evaluate() diff --git a/adcc/qed_matrix_from_diag_adc.py b/adcc/qed_matrix_from_diag_adc.py new file mode 100644 index 00000000..0296a084 --- /dev/null +++ b/adcc/qed_matrix_from_diag_adc.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab +## --------------------------------------------------------------------- +## +## Copyright (C) 2018 by the adcc authors +## +## This file is part of adcc. +## +## adcc is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## adcc is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with adcc. If not, see . +## +## --------------------------------------------------------------------- +import numpy as np +import scipy.linalg as sp + +class qed_matrix_from_diag_adc: + def __init__(self, exstates, refstate): + self.s2s = exstates.s2s_dipole_moments_qed + self.tdm = exstates.transition_dipole_moments_qed + self.h1 = exstates.qed_second_order_ph_ph_couplings + self.coupl = refstate.coupling[2] + self.freq = refstate.frequency[2] + self.n_adc = len(exstates.excitation_energy) + self.exc_en = exstates.excitation_energy + + + def first_order_coupling(self): + + # build the blocks of the matrix + + tdm_block = np.empty(self.n_adc) + + for i, tdm in enumerate(self.tdm): + tdm_block[i] = self.coupl * np.sqrt(2 * self.freq) * tdm[2] + + s2s_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] + tdm_block = - np.sqrt(self.freq/2) * tdm_block + + elec_block = np.diag(self.exc_en) + phot_block = np.diag(self.exc_en + self.freq) + + # build the matrix + + matrix_upper = np.vstack((elec_block, tdm_block.reshape((1, self.n_adc)), s2s_block)) + matrix_middle = np.concatenate((tdm_block, np.array([self.freq]), np.zeros(self.n_adc))) + matrix_lower = np.vstack((s2s_block, np.zeros((1, self.n_adc)), phot_block)) + + matrix = np.hstack((matrix_upper, matrix_middle.reshape((len(matrix_middle), 1)), matrix_lower)) + + return sp.eigh(matrix) + + + def second_order_coupling(self): + qed_adc2_tdm_vec = np.empty(self.n_adc) + + for i, tdm in enumerate(self.tdm): + qed_adc2_tdm_vec[i] = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * tdm[2] + + # s2s_dipole parts of the ph_ph blocks + + qed_adc1_off_diag_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] + + qed_adc2_diag_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_diag"] + qed_adc2_diag_block = qed_adc2_diag_block * np.sqrt(self.freq/2) # missing factor from state.s2s_dipole_moments_qed_adc2_diag + + qed_adc2_edge_block_couple = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_couple"] + qed_adc2_edge_block_couple = qed_adc2_edge_block_couple * np.sqrt(self.freq) # missing factor from state.s2s_dipole_moments_qed_adc2_edge + + qed_adc2_edge_block_phot_couple = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_phot_couple"] + qed_adc2_edge_block_phot_couple = qed_adc2_edge_block_phot_couple * np.sqrt(self.freq) # missing factor from state.s2s_dipole_moments_qed_adc2_edge + + # s2s_dipole parts of the pphh_ph and ph_pphh blocks + + qed_adc2_ph_pphh_couple_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_ph_pphh"] + + qed_adc2_pphh_ph_phot_couple_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_pphh_ph"] + + # we still need the H_1 expectation value "as property" + + qed_adc2_couple_block = np.sqrt(self.freq/2) * self.h1["couple"] + qed_adc2_phot_couple_block = np.sqrt(self.freq/2) * self.h1["phot_couple"] + + # build the blocks of the matrix + + single_excitation_states = np.ones(self.n_adc) + + elec_block = np.diag(self.exc_en) + qed_adc2_diag_block + + phot_block = np.diag(self.exc_en) + qed_adc2_diag_block * 2 + np.diag(single_excitation_states) * self.freq + + phot2_block = np.diag(self.exc_en) + qed_adc2_diag_block * 3 + np.diag(single_excitation_states) * 2 * self.freq + + couple_block = qed_adc1_off_diag_block + qed_adc2_ph_pphh_couple_block + qed_adc2_couple_block + + phot_couple_block = qed_adc1_off_diag_block + qed_adc2_pphh_ph_phot_couple_block + qed_adc2_phot_couple_block + + #print("elec_block", elec_block.tolist()) + #print("phot_block", phot_block.tolist()) + #print("phot2_block", phot2_block.tolist()) + #print("couple_block", couple_block.tolist()) + #print("phot_couple_block", phot_couple_block.tolist()) + + # build the matrix + + matrix_1 = np.vstack((elec_block, qed_adc2_tdm_vec.reshape((1, self.n_adc)), + phot_couple_block, np.zeros((1, self.n_adc)), qed_adc2_edge_block_phot_couple)) + matrix_2 = np.concatenate((qed_adc2_tdm_vec, np.array([self.freq]), + np.zeros(self.n_adc), np.array([0]), np.zeros(self.n_adc))) + matrix_3 = np.vstack((couple_block, np.zeros((1, self.n_adc)), phot_block, + np.sqrt(2) * qed_adc2_tdm_vec.reshape((1, self.n_adc)), np.sqrt(2) * phot_couple_block)) + matrix_4 = np.concatenate((np.zeros(self.n_adc), np.array([0]), np.sqrt(2) * qed_adc2_tdm_vec, + 2 * np.array([self.freq]), np.zeros(self.n_adc))) + matrix_5 = np.vstack((qed_adc2_edge_block_couple, np.zeros((1, self.n_adc)), + np.sqrt(2) * couple_block, np.zeros((1, self.n_adc)), phot2_block)) + + matrix = np.hstack((matrix_1, matrix_2.reshape((len(matrix_2), 1)), matrix_3, + matrix_4.reshape((len(matrix_4), 1)), matrix_5)) + + eigvals, eigvecs = sp.eigh(matrix) + + #print("eigvals", eigvals) + + return eigvals, eigvecs \ No newline at end of file diff --git a/adcc/solver/LanczosIterator.py b/adcc/solver/LanczosIterator.py index 8b7fa092..a91d48c0 100644 --- a/adcc/solver/LanczosIterator.py +++ b/adcc/solver/LanczosIterator.py @@ -109,7 +109,6 @@ def __next__(self): # Initialise Lanczos subspace v = self.ortho.orthogonalise(self.residual) self.lanczos_subspace = v - #print(v) r = evaluate(self.matrix @ v) alpha = np.empty((self.n_block, self.n_block)) for p in range(self.n_block): diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index aa68596a..079a68a1 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -283,8 +283,6 @@ def form_residual(rval, rvec): pvec = preconds[i] # Project out the components of the current subspace # That is form (1 - SS * SS^T) * pvec = pvec + SS * (-SS^T * pvec) - #print("pvec SS", pvec, SS) - #print(pvec.elec.ph, SS[0].elec.ph) coefficients = np.hstack(([1], -(pvec @ SS))) pvec = lincomb(coefficients, [pvec] + SS, evaluate=True) pnorm = np.sqrt(pvec @ pvec) @@ -389,7 +387,6 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, max_subspace = len(guesses) elif hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): print("for qed-adc we use double the standard max_subspace") - #"if the doubly excited photonic space contributes to the states") max_subspace = max(12 * n_ep, 20, 10 * len(guesses)) else: max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index 662977aa..12780c49 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -45,7 +45,6 @@ def __init__(self, matrix): # for the respective block self.symmetrisation_functions = \ matrix.construct_symmetrisation_for_blocks() - print("index symm class init is used") def symmetrise(self, new_vectors): """ diff --git a/adcc/solver/preconditioner.py b/adcc/solver/preconditioner.py index ab4385c6..258fea39 100644 --- a/adcc/solver/preconditioner.py +++ b/adcc/solver/preconditioner.py @@ -63,14 +63,13 @@ def update_shifts(self, shifts): self.shifts = shifts def apply(self, invecs): - #print(invecs) if isinstance(invecs, AmplitudeVector): if not isinstance(self.shifts, (float, np.number)): raise TypeError("Can only apply JacobiPreconditioner " "to a single vector if shifts is " "only a single number.") return invecs / (self.diagonal - self.shifts) - elif isinstance(invecs, QED_AmplitudeVector): # I dont think this is used anywhere + elif isinstance(invecs, QED_AmplitudeVector): if not isinstance(self.shifts, (float, np.number)): raise TypeError("Can only apply JacobiPreconditioner " "to a single vector if shifts is " @@ -82,13 +81,6 @@ def apply(self, invecs): "with number of shifts stored inside " "precoditioner. Update using the " "'update_shifts' method.") - #print(self.diagonal, self.shifts) - #for i, v in enumerate(invecs): - #test_temp = invecs[0].ph / (self.diagonal.ph - self.shifts[0]) - #print("ph/ph", test_temp) - #print(invecs[0]) - #print(self.diagonal.gs, self.diagonal.ph, self.diagonal.gs1) - #print("from precond invec.pphh is ", invecs[0].pphh) return [v / (self.diagonal - self.shifts[i]) for i, v in enumerate(invecs)] else: diff --git a/adcc/workflow.py b/adcc/workflow.py index 54274da4..4373a2bc 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -40,6 +40,7 @@ from .solver.explicit_symmetrisation import (IndexSpinSymmetrisation, IndexSymmetrisation) from .AmplitudeVector import QED_AmplitudeVector +from .qed_matrix_from_diag_adc import qed_matrix_from_diag_adc __all__ = ["run_adc"] @@ -214,6 +215,19 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, # add environment corrections to excited states exstates += env_energy_corrections + + # Build QED (approximated) matrix from "standard" ADC matrix + # and expectation values. + if hasattr(matrix.reference_state, "approx"): + qed_matrix = qed_matrix_from_diag_adc(exstates, matrix.reference_state) + if method == "adc2" and not hasattr(matrix.reference_state, "first_order_coupling"): + qed_eigvals, qed_eigvecs = qed_matrix.second_order_coupling() + else: + qed_eigvals, qed_eigvecs = qed_matrix.first_order_coupling() + + exstates.qed_excitation_energy = qed_eigvals + exstates.qed_excitation_vector = qed_eigvecs + return exstates @@ -519,8 +533,6 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= for guess_index in np.arange(n_guess): if "pphh" in matrix.axis_blocks: - # what if this is not ok without restricting singlets/triplets only, - # because e.g. phot could be singlet and elec triplet ... doesnt seem to matter guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, 0, guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, 0, guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) @@ -569,7 +581,6 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= for guess in final_guesses: - #print(guess.gs1.as_float(), guess.gs2.as_float()) print(guess.gs1, guess.gs2) return [vec / np.sqrt(vec @ vec) for vec in final_guesses] From 9ff21314de46d2cfced6eeabb312bb2c4bf0b79b Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 4 Jul 2022 13:06:23 +0200 Subject: [PATCH 43/64] minor changes --- adcc/AdcMatrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index f110c4a4..f7048e03 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -448,8 +448,8 @@ def mv(qed_disp_key): return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: - #return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) - return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot_part.ph * 0, None) + return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) + #return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot_part.ph * 0, None) else: TypeError("matvec needs to be invoked with AmplitudeVector or QED_AmplitudeVector") From ee4be7ea4e2116ee1b48a754dcde1452e8a707b6 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 4 Jul 2022 15:40:13 +0200 Subject: [PATCH 44/64] searching for numerical inconsistency in approx function --- adcc/LazyMp.py | 14 ++++++++++++++ adcc/workflow.py | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index 109c7a2c..4d0671de 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -286,6 +286,20 @@ def diff_df(self, space): return einsum("ia,ja->ij", self.df(b.ov), - self.df(b.ov)) + def dipole_moment(self, level=2): + """ + Return the MP dipole moment at the specified level of + perturbation theory. + """ + if level == 1: + return self.reference_state.dipole_moment + elif level == 2: + return self.mp2_dipole_moment + else: + raise NotImplementedError("Only dipole moments for level 1 and 2" + " are implemented.") + + @cached_member_function def energy_correction(self, level=2): """Obtain the MP energy correction at a particular level""" diff --git a/adcc/workflow.py b/adcc/workflow.py index 4373a2bc..770c6339 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -218,6 +218,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, # Build QED (approximated) matrix from "standard" ADC matrix # and expectation values. + """ if hasattr(matrix.reference_state, "approx"): qed_matrix = qed_matrix_from_diag_adc(exstates, matrix.reference_state) if method == "adc2" and not hasattr(matrix.reference_state, "first_order_coupling"): @@ -227,7 +228,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, exstates.qed_excitation_energy = qed_eigvals exstates.qed_excitation_vector = qed_eigvecs - + """ return exstates From 28ca7731ecf5a38ed259d1e2dc45d6db04fdf0f9 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 4 Jul 2022 17:15:24 +0200 Subject: [PATCH 45/64] numerical inconsistency in approx function found and removed --- adcc/adc_pp/transition_dm.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index 367b9f99..dff99705 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -117,7 +117,6 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): """ Compute the one-particle transition density matrix from ground to excited state in the MO basis. - Parameters ---------- method : str, AdcMethod @@ -133,8 +132,8 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): method = AdcMethod(method) if not isinstance(ground_state, LazyMp): raise TypeError("ground_state should be a LazyMp object.") - if not isinstance(amplitude, (AmplitudeVector, QED_AmplitudeVector)): - raise TypeError("amplitude should be an AmplitudeVector or QED_AmplitudeVector object.") + if not isinstance(amplitude, AmplitudeVector): + raise TypeError("amplitude should be an AmplitudeVector object.") if intermediates is None: intermediates = Intermediates(ground_state) @@ -142,18 +141,8 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): raise NotImplementedError("transition_dm is not implemented " f"for {method.name}.") else: - if isinstance(amplitude, QED_AmplitudeVector): - # for QED_result we are interested in .elec part only, which grants the electronic transitions. - # We also dont need .gs since it should be zero...this has to be checked!!! - ampl_elec_norm = amplitude.elec @ amplitude.elec - ampl_phot_norm = amplitude.phot @ amplitude.phot - print("care, that .gs is not used for transition_dm, but only .elec (check transition_dm.py)") - print("norm squared of amplitude.elec", ampl_elec_norm, " norm squared phot", ampl_phot_norm) - print("norm squared of amplitude", amplitude @ amplitude) - print("beware, that we normalize .elec, before giving it to the oscillator strength routine") - print("amplitude.gs1 is {} and amplitude.gs2 is {}".format(amplitude.gs1, amplitude.gs2))#.as_float(), amplitude.gs2.as_float())) - normalized_ampl = amplitude.elec / sqrt(ampl_elec_norm) - ret = DISPATCH[method.name](ground_state, normalized_ampl, intermediates) + if hasattr(ground_state, "tdm_contribution"): + ret = DISPATCH[ground_state.tdm_contribution](ground_state, amplitude, intermediates) else: ret = DISPATCH[method.name](ground_state, amplitude, intermediates) return ret.evaluate() From 89b12cc238a94e8bf4f6d9a5a0987afde4556e36 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 5 Jul 2022 14:58:06 +0200 Subject: [PATCH 46/64] reintroduced approx method --- adcc/workflow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adcc/workflow.py b/adcc/workflow.py index 770c6339..a7c52b0e 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -218,7 +218,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, # Build QED (approximated) matrix from "standard" ADC matrix # and expectation values. - """ + if hasattr(matrix.reference_state, "approx"): qed_matrix = qed_matrix_from_diag_adc(exstates, matrix.reference_state) if method == "adc2" and not hasattr(matrix.reference_state, "first_order_coupling"): @@ -228,7 +228,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, exstates.qed_excitation_energy = qed_eigvals exstates.qed_excitation_vector = qed_eigvecs - """ + return exstates From 8dfbb93d959ba3ceda4e5b973982fa6c33579f8f Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Wed, 13 Jul 2022 14:06:05 +0200 Subject: [PATCH 47/64] no printout from LazyMp anymore --- adcc/LazyMp.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index 4d0671de..893d5c9b 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -314,10 +314,10 @@ def energy_correction(self, level=2): is_cvs = self.has_core_occupied_space if level == 2 and not is_cvs: terms = [(1.0, hf.oovv, self.t2oo)] - mp2_correction = sum( - -0.25 * pref * eri.dot(t2) - for pref, eri, t2 in terms - ) + #mp2_correction = sum( + # -0.25 * pref * eri.dot(t2) + # for pref, eri, t2 in terms + #) if hasattr(hf, "coupling"): #print("mp2 energy with two electron qed perturbation " + str(mp2_correction)) total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) @@ -328,7 +328,8 @@ def energy_correction(self, level=2): for pref, lambda_dip, qed_t in qed_terms ) if hasattr(hf, "qed_hf"): - print("QED-MP(2) energy correction for QED-HF = " + str(mp2_correction + qed_mp2_correction_1)) + qed_mp2_correction = qed_mp2_correction_1 + #print("QED-MP(2) energy correction for QED-HF = " + str(mp2_correction + qed_mp2_correction_1)) else: qed_terms_0 = [(1.0, self.qed_t0(b.ov), self.qed_t0_df(b.ov))] qed_mp2_correction_0 = sum( @@ -341,8 +342,9 @@ def energy_correction(self, level=2): pref * lambda_dip.dot(lambda_dip) for pref, lambda_dip in qed_mp1_additional_terms ) - print("QED-MP(2) energy correction for standard HF (includes QED-MP(1) too) = " - + str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0 + qed_mp1_correction)) + #print("QED-MP(2) energy correction for standard HF (includes QED-MP(1) too) = " + #+ str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0 + qed_mp1_correction)) + qed_mp2_correction = qed_mp2_correction_1 + qed_mp2_correction_0 + qed_mp1_correction + qed_mp1_correction #print("qed-mp1 correction, due to standard hf input " + str(qed_mp1_correction)) #print("new qed-mp2 correction compared to qed-hf " + str(qed_mp2_correction_0)) elif level == 2 and is_cvs: From aa44ebb4feeb135edde2d3571bb499b4de9d9f88 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 28 Jul 2022 12:42:55 +0200 Subject: [PATCH 48/64] started with qed test --- adcc/adc_pp/transition_dm.py | 4 ++- adcc/test_qed.py | 61 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 adcc/test_qed.py diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index dff99705..a8b19cf6 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -132,8 +132,10 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): method = AdcMethod(method) if not isinstance(ground_state, LazyMp): raise TypeError("ground_state should be a LazyMp object.") - if not isinstance(amplitude, AmplitudeVector): + if not isinstance(amplitude, (AmplitudeVector, QED_AmplitudeVector)): raise TypeError("amplitude should be an AmplitudeVector object.") + if isinstance(amplitude, QED_AmplitudeVector): + amplitude = amplitude.elec if intermediates is None: intermediates = Intermediates(ground_state) diff --git a/adcc/test_qed.py b/adcc/test_qed.py new file mode 100644 index 00000000..6c11863b --- /dev/null +++ b/adcc/test_qed.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab +## --------------------------------------------------------------------- +## +## Copyright (C) 2018 by the adcc authors +## +## This file is part of adcc. +## +## adcc is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## adcc is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with adcc. If not, see . +## +## --------------------------------------------------------------------- +import unittest + +from adcc.misc import expand_test_templates +#from . import block as b +#from numpy.testing import assert_allclose + +#from adcc import LazyMp +from adcc.testdata.cache import cache + +#import pytest + +#from pytest import approx + +#import os + + +# This test is different from the others, since the "approx" method to +# the full matrix dimension has to equal the "standard" method. Since +# they further require different routines apart from those, which are +# already tested with absolute values, we can just test them against +# each other. + +# In order to keep this test as small as possible, we also require a +# new test case, HF sto-3g, since it contains only one virtual orbital, +# which keeps the matrix dimension low. This is important since we +# build most of the matrix in the approx method from properties, so this +# is very slow for a lot of states, compared to the standard method. + +# One should add more testcases, but they should be added to +# cache.mode_full +testcases = ["h2o_sto3g"] + +@expand_test_templates(testcases) +class qed_test(unittest.TestCase): + def __init__(self, case): + self.refstate = cache.reference_data(case) + self.refstate.coupling = [0.0, 0.0, 0.05] # set some coupling + self.refstate.frequency = [0.0, 0.0, 0.5] # set some frequency + print(self.refstate) \ No newline at end of file From cb369d131607f4b860c4b16c0ad2c4aea5a10313 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 1 Aug 2022 19:49:49 +0200 Subject: [PATCH 49/64] qed parameters can now also be set via the run_adc function. Tests for the QED method have been added as well. --- adcc/test_qed.py | 67 +++++++++++++++++++++++--------------- adcc/testdata/cache.py | 1 + adcc/testdata/qed_dump.yml | 28 ++++++++++++++++ adcc/workflow.py | 63 ++++++++++++++++++++++++++++++++--- 4 files changed, 128 insertions(+), 31 deletions(-) create mode 100644 adcc/testdata/qed_dump.yml diff --git a/adcc/test_qed.py b/adcc/test_qed.py index 6c11863b..a475a582 100644 --- a/adcc/test_qed.py +++ b/adcc/test_qed.py @@ -23,39 +23,54 @@ import unittest from adcc.misc import expand_test_templates -#from . import block as b -#from numpy.testing import assert_allclose +import adcc -#from adcc import LazyMp -from adcc.testdata.cache import cache - -#import pytest - -#from pytest import approx - -#import os +from numpy.testing import assert_allclose +from adcc.testdata.cache import cache +from adcc.testdata.cache import qed_data -# This test is different from the others, since the "approx" method to -# the full matrix dimension has to equal the "standard" method. Since -# they further require different routines apart from those, which are -# already tested with absolute values, we can just test them against -# each other. +import itertools +import pytest -# In order to keep this test as small as possible, we also require a -# new test case, HF sto-3g, since it contains only one virtual orbital, +# In principle one could also test the approx method against the full +# method, by expanding them to the full matrix dimension. The smallest +# example would be HF sto-3g, since it contains only one virtual orbital, # which keeps the matrix dimension low. This is important since we # build most of the matrix in the approx method from properties, so this # is very slow for a lot of states, compared to the standard method. +# However, even this test case would take quite some time... -# One should add more testcases, but they should be added to -# cache.mode_full -testcases = ["h2o_sto3g"] +testcases = ["methox_sto3g", "h2o_sto3g"] +methods = ["adc2"] -@expand_test_templates(testcases) +@expand_test_templates(list(itertools.product(testcases, methods))) class qed_test(unittest.TestCase): - def __init__(self, case): - self.refstate = cache.reference_data(case) - self.refstate.coupling = [0.0, 0.0, 0.05] # set some coupling - self.refstate.frequency = [0.0, 0.0, 0.5] # set some frequency - print(self.refstate) \ No newline at end of file + def set_refstate(self, case): + self.refstate = cache.refstate[case] + self.refstate.coupling = [0.0, 0.0, 0.05] + self.refstate.frequency = [0.0, 0.0, 0.5] + self.refstate.qed_hf = True + + def template_approx(self, case, method): + self.set_refstate(case) + self.refstate.approx = True + + approx = adcc.adc2(self.refstate, n_singlets = 5, conv_tol = 1e-7) + + ref_name = f"{case}_{method}_approx" + approx_ref = qed_data[ref_name]["excitation_energy"] + + assert_allclose(approx.qed_excitation_energy, + approx_ref, atol=1e-6) + + def template_full(self, case, method): + self.set_refstate(case) + + full = adcc.adc2(self.refstate, n_singlets = 3, conv_tol = 1e-7) + + ref_name = f"{case}_{method}_full" + full_ref = qed_data[ref_name]["excitation_energy"] + + assert_allclose(full.excitation_energy, + full_ref, atol=1e-6) \ No newline at end of file diff --git a/adcc/testdata/cache.py b/adcc/testdata/cache.py index 1f0e37f1..3880dbb1 100644 --- a/adcc/testdata/cache.py +++ b/adcc/testdata/cache.py @@ -260,3 +260,4 @@ def read_yaml_data(fname): tmole_data = read_yaml_data("tmole_dump.yml") psi4_data = read_yaml_data("psi4_dump.yml") pyscf_data = read_yaml_data("pyscf_dump.yml") +qed_data = read_yaml_data("qed_dump.yml") diff --git a/adcc/testdata/qed_dump.yml b/adcc/testdata/qed_dump.yml new file mode 100644 index 00000000..c2bb9f25 --- /dev/null +++ b/adcc/testdata/qed_dump.yml @@ -0,0 +1,28 @@ +h2o_sto3g_adc2_full: + basis: sto3g + method: adc2 + molecule: h2o + approx: False + excitation_energy: [0.470369, 0.572391, 0.59388 ] +h2o_sto3g_adc2_approx: + basis: sto3g + method: adc2 + molecule: h2o + approx: True + excitation_energy: [0.469793, 0.496957, 0.571511, 0.593613, 0.712661, 0.844501, + 0.970428, 0.993996, 1.0714 , 1.094744, 1.212892, 1.346747, + 1.47356 , 1.574525, 1.597913, 1.716008, 1.841888] +methox_sto3g_adc2_full: + basis: sto3g + method: adc2 + molecule: cn + approx: False + excitation_energy: [0.34166625, 0.41536805, 0.49308736] +methox_sto3g_adc2_approx: + basis: sto3g + method: adc2 + molecule: cn + approx: True + excitation_energy: [0.341774, 0.41538 , 0.491732, 0.501357, 0.511708, 0.546447, + 0.842004, 0.915563, 0.990758, 1.002418, 1.011967, 1.046802, + 1.342381, 1.416067, 1.493567, 1.512128, 1.54733 ] \ No newline at end of file diff --git a/adcc/workflow.py b/adcc/workflow.py index a7c52b0e..2cfddb4d 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -50,7 +50,8 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, n_guesses_doubles=None, output=sys.stdout, core_orbitals=None, frozen_core=None, frozen_virtual=None, method=None, n_singlets=None, n_triplets=None, n_spin_flip=None, - environment=None, **solverargs): + environment=None, coupl=None, freq=None, qed_hf=True, + qed_approx=False, qed_full_diag=False, **solverargs): """Run an ADC calculation. Main entry point to run an ADC calculation. The reference to build the ADC @@ -136,6 +137,28 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, The keywords to specify how coupling to an environment model, e.g. PE, is treated. For details see :ref:`environment`. + coupl : list or tuple, optional + Specifies the coupling for a qed calculation as [x, y, z] + + freq : list or tuple, optional + Specifies the photon energy corresponding to the coupling specified + in "coupl", which is required for a qed calculation, as [x, y, z] + + qed_hf : bool, optional + Specifies, if the mean-field solution to the Pauli-Fierz Hamiltonian + is provided for the qed calculation. False expects the standard HF + solution. + + qed_approx : bool, optional + Indicates whether the approximate solution for the qed method should + be calculated. (After paper is published, put link here) + + qed_full_diag : bool, optional + If the full solution to the qed method is required, the performance + of the standard qed guess is very poor. In that case, this guess + performs much better. + + Other parameters ---------------- max_subspace : int, optional @@ -181,11 +204,15 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, """ matrix = construct_adcmatrix( data_or_matrix, core_orbitals=core_orbitals, frozen_core=frozen_core, - frozen_virtual=frozen_virtual, method=method) + frozen_virtual=frozen_virtual, method=method, coupl=coupl, + freq=freq, qed_hf=qed_hf, qed_approx=qed_approx, + qed_full_diag=qed_full_diag) n_states, kind = validate_state_parameters( matrix.reference_state, n_states=n_states, n_singlets=n_singlets, - n_triplets=n_triplets, n_spin_flip=n_spin_flip, kind=kind) + n_triplets=n_triplets, n_spin_flip=n_spin_flip, kind=kind, coupl=coupl, + freq=freq, qed_hf=qed_hf, qed_approx=qed_approx, + qed_full_diag=qed_full_diag) # Determine spin change during excitation. If guesses is not None, # i.e. user-provided, we cannot guarantee for obtaining a particular @@ -236,7 +263,9 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, # Individual steps # def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, - frozen_virtual=None, method=None): + frozen_virtual=None, method=None, coupl=None, + freq=None, qed_hf=True, qed_approx=False, + qed_full_diag=False): """ Use the provided data or AdcMatrix object to check consistency of the other passed parameters and construct the AdcMatrix object representing @@ -265,6 +294,16 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, frozen_virtual=frozen_virtual) except ValueError as e: raise InputError(str(e)) # In case of an issue with the spaces + # for now qchem keywords are requested as refstate attributes + if coupl != None: + refstate.coupling = coupl + refstate.frequency = freq + if qed_hf: + refstate.qed_hf = True + if qed_approx: + refstate.approx = True + if qed_full_diag: + refstate.full_diagonalization = True data_or_matrix = refstate elif core_orbitals is not None: mospaces = data_or_matrix.mospaces @@ -300,7 +339,9 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, def validate_state_parameters(reference_state, n_states=None, n_singlets=None, - n_triplets=None, n_spin_flip=None, kind="any"): + n_triplets=None, n_spin_flip=None, kind="any", + coupl=None, freq=None, qed_hf=True, + qed_approx=False, qed_full_diag=False): """ Check the passed state parameters for consistency with itself and with the passed reference and normalise them. In the end return the number of @@ -359,6 +400,18 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, raise InputError("kind==spin_flip is only valid for " "ADC calculations in combination with an unrestricted " "ground state.") + + # qed sanity checks + if coupl != None or freq != None: + if not (coupl != None and freq != None): + raise InputError("qed calculation requires coupl and freq") + if len(coupl) != 3 or len(freq) != 3: + raise InputError("freq and coupl must contain 3 elements," + "i.e. x, y, z") + if qed_hf == False: + raise InputError("QED-ADC of zeroth and first level are not yet," + "properly tested and second order is not implemented") + return n_states, kind From 0d7d166b1656f358a9ea5ed450a3b629116d97d7 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 2 Aug 2022 09:21:36 +0200 Subject: [PATCH 50/64] less printout and made the max subspace for qed a fully manual option, instead of always doubling it. --- adcc/LazyMp.py | 2 +- adcc/adc_pp/matrix.py | 1 - adcc/solver/davidson.py | 6 +++--- adcc/workflow.py | 5 +---- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index 893d5c9b..d1565239 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -279,7 +279,7 @@ def qed_t0(self, space): @cached_member_function def diff_df(self, space): if space == b.ov: - raise NotImplementedError("This would not make sense to construct!!!") + raise NotImplementedError("Request this space from df") elif space == b.vv: # this returns (eps_a - eps_b) return einsum("ia,ib->ab", self.df(b.ov), - self.df(b.ov)) elif space == b.oo: # this returns (- eps_i + eps_j) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 7d27ea7c..09b1ee6f 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -834,7 +834,6 @@ def block_ph_ph_2(hf, mp, intermediates): - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + (1/2) * 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) )) - print(mp.energy_correction(2)) def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, i1) diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 079a68a1..55443212 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -385,9 +385,9 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, # max_subspace = max(2 * n_ep + 1, 20) if hasattr(matrix.reference_state, "full_diagonalization"): max_subspace = len(guesses) - elif hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): - print("for qed-adc we use double the standard max_subspace") - max_subspace = max(12 * n_ep, 20, 10 * len(guesses)) + #elif hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): + # print("for qed-adc we use double the standard max_subspace") + # max_subspace = max(12 * n_ep, 20, 10 * len(guesses)) else: max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) diff --git a/adcc/workflow.py b/adcc/workflow.py index 2cfddb4d..2a70036b 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -573,7 +573,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= n_guess = len(guesses_elec) # Usually only few states are requested and most of them are close to pure electronic states, - # so we initialize the guess vectors as almost electric guesses. + # so we initialize the guess vectors as almost purely electric guesses. if not hasattr(matrix.reference_state, "full_diagonalization"): for i in np.arange(n_guess): # TODO: maybe these values should be accessible from the input file @@ -633,9 +633,6 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= final_guesses[len(final_guesses) - 2].gs1 += 5#2 final_guesses[len(final_guesses) - 1].gs2 += 20#5 - - for guess in final_guesses: - print(guess.gs1, guess.gs2) return [vec / np.sqrt(vec @ vec) for vec in final_guesses] def setup_solver_printing(solmethod_name, matrix, kind, default_print, From c25cc1d50876e42498f6846da98021ce54756575 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 9 Aug 2022 13:40:36 +0200 Subject: [PATCH 51/64] final cleanup for initial commit to master --- adcc/AdcMatrix.py | 82 ++++--- adcc/AmplitudeVector.py | 130 ++++++----- adcc/ElectronicTransition.py | 41 ++-- adcc/ExcitedStates.py | 13 +- adcc/LazyMp.py | 84 +++---- adcc/ReferenceState.py | 46 ++-- adcc/adc_pp/matrix.py | 286 +++++++++++------------ adcc/adc_pp/state2state_transition_dm.py | 6 +- adcc/adc_pp/transition_dm.py | 7 +- adcc/backends/__init__.py | 2 + adcc/backends/psi4.py | 13 +- adcc/functions.py | 17 +- adcc/guess/guesses_from_diagonal.py | 10 - adcc/qed_matrix_from_diag_adc.py | 88 ++++--- adcc/solver/davidson.py | 3 - adcc/test_qed.py | 3 +- adcc/workflow.py | 99 +++++--- 17 files changed, 496 insertions(+), 434 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index f7048e03..8527045a 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -75,8 +75,6 @@ class AdcMatrixlike: class AdcMatrix(AdcMatrixlike): # Default perturbation-theory orders for the matrix blocks (== standard ADC-PP). default_block_orders = { - # ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), - #"adc0": dict(gs_gs=0, gs_ph=0, ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 "adc0": dict(ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 "adc1": dict(ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 "adc2": dict(ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 @@ -140,10 +138,12 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, if method.base_method.name == "adc2x" or method.base_method.name == "adc3": NotImplementedError("Neither adc2x nor adc3 are implemented for QED-ADC") - if hasattr(self.reference_state, "first_order_coupling") and method.base_method.name == "adc2": + if hasattr(self.reference_state, "first_order_coupling") and method.base_method.name == "adc2": # noqa: E501 # this way we only need to include the separate case in the ph_ph=1 blocks, # and the 4 non-zero coupling blocks - self.qed_default_block_orders["adc2"] = dict(ph_gs=1, ph_ph=1, ph_pphh=1, pphh_ph=1, pphh_pphh=0) + self.qed_default_block_orders["adc2"] = dict(ph_gs=1, ph_ph=1, + ph_pphh=1, pphh_ph=1, + pphh_pphh=0) self.intermediates = intermediates if self.intermediates is None: @@ -151,7 +151,7 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, # Determine orders of PT in the blocks if block_orders is None: - if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): + if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 block_orders = self.qed_default_block_orders[method.base_method.name] else: block_orders = self.default_block_orders[method.base_method.name] @@ -181,9 +181,16 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, } def get_pp_blocks(disp_str): + """ + Extraction of blocks from the adc_pp matrix + ---------- + disp_str : string + key specifying block to return + """ return { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + self.qed_dispatch_dict[disp_str], intermediates=self.intermediates, + order=str(order) + self.qed_dispatch_dict[disp_str], + intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None } @@ -195,7 +202,7 @@ def get_pp_blocks(disp_str): if method.is_core_valence_separated: variant = "cvs" # Build full QED-matrix - if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): + if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 blocks = {} if hasattr(hf_or_mp.reference_state, "coupling"): @@ -211,18 +218,21 @@ def get_pp_blocks(disp_str): if "ph_gs" in block and block.endswith("phot2")) if "pphh_pphh_elec" in blocks: - self.__diagonal = QED_AmplitudeVector(blocks["ph_ph_elec"].diagonal.evaluate().ph, blocks["pphh_pphh_elec"].diagonal.evaluate().pphh, - self.__diagonal_gs1, blocks["ph_ph_phot"].diagonal.evaluate().ph, blocks["pphh_pphh_phot"].diagonal.evaluate().pphh, - self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, blocks["pphh_pphh_phot2"].diagonal.evaluate().pphh) + self.__diagonal = QED_AmplitudeVector(blocks["ph_ph_elec"].diagonal.evaluate().ph, + blocks["pphh_pphh_elec"].diagonal.evaluate().pphh, + self.__diagonal_gs1, blocks["ph_ph_phot"].diagonal.evaluate().ph, + blocks["pphh_pphh_phot"].diagonal.evaluate().pphh, + self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, + blocks["pphh_pphh_phot2"].diagonal.evaluate().pphh) else: - self.__diagonal = QED_AmplitudeVector(blocks["ph_ph_elec"].diagonal.evaluate().ph, None, - self.__diagonal_gs1, blocks["ph_ph_phot"].diagonal.evaluate().ph, None, - self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, None) - self.__init_space_data_qed(self.__diagonal.elec) - #self.__diagonal.evaluate() + self.__diagonal = QED_AmplitudeVector(blocks["ph_ph_elec"].diagonal.evaluate().ph, None, # noqa: E501 + self.__diagonal_gs1, blocks["ph_ph_phot"].diagonal.evaluate().ph, None, # noqa: E501 + self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, None) # noqa: E501 + self.__init_space_data(self.__diagonal.elec) else: # Build "standard" ADC-matrix blocks = { - bl: get_pp_blocks("elec")[bl] for bl, order in block_orders.items() if order is not None + bl: get_pp_blocks("elec")[bl] for bl, order in block_orders.items() + if order is not None } if diagonal_precomputed: @@ -237,20 +247,15 @@ def get_pp_blocks(disp_str): self.blocks_ph = {bl: blocks[bl].apply for bl in blocks} - def __init_space_data_qed(self, diagonal): - self.__init_space_data(diagonal) - shape0 = self.shape - self.shape = ((shape0[0]+1) * 3 - 1, (shape0[0]+1) * 3 - 1) - #self.axis_spaces["gs"] = ["o0", "v0"] - #self.axis_lengths["gs"] = 1 - - def qed_subblock(self, qed_disp_key): # These subblocks are "standard ADC matrices", so we can use the implemented functions. # This is useful e.g. in the matvec function """ Extraction of subblocks from the full QED-ADC Matrix, without the ph_gs blocks + ---------- + qed_disp_key : string + key specifying which block to return """ return [bl for key, bl in self.blocks_ph.items() if not key.startswith("ph_gs") and key.endswith(qed_disp_key)] @@ -317,6 +322,10 @@ def __init_space_data(self, diagonal): ]) self.shape = (sum(self.axis_lengths.values()), sum(self.axis_lengths.values())) + if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 + # We leave out the the dimension with the purely electronic ground state, + # since by construction the coupling to it is always zero + self.shape = ((self.shape[0]+1) * 3 - 1, (self.shape[0]+1) * 3 - 1) def __repr__(self): ret = f"AdcMatrix({self.method.name}, " @@ -373,11 +382,10 @@ def diagonal(self, block=None): def compute_apply(self, block, tensor): warnings.warn("The compute_apply function is deprecated and " "will be removed in 0.16.0.") - if block in ("sg" ,"ss", "sd", "ds", "dd"): + if block in ("ss", "sd", "ds", "dd"): warnings.warn("The singles-doubles interface is deprecated and " "will be removed in 0.16.0.") - block = {"sg": "ph_gs", - "ss": "ph_ph", "sd": "ph_pphh", + block = {"ss": "ph_ph", "sd": "ph_pphh", "ds": "pphh_ph", "dd": "pphh_pphh"}[block] return self.block_apply(block, tensor) @@ -392,7 +400,7 @@ def block_apply(self, block, tensor): with self.timer.record(f"apply/{block}"): outblock, inblock = block.split("_") - ampl = QED_AmplitudeVector(**{inblock: tensor}) + ampl = AmplitudeVector(**{inblock: tensor}) ret = self.blocks_ph[block](ampl) return getattr(ret, outblock) @@ -411,11 +419,14 @@ def mv(qed_disp_key): phot_part = mv("elec_couple") + mv("phot") + mv("phot_couple_inner") - if "pphh_pphh_elec" in self.blocks_ph.keys() and not hasattr(self.reference_state, "first_order_coupling"): - phot_couple_edge_with_doubles = AmplitudeVector(ph=mv("phot_couple_edge"), pphh=v.pphh.zeros_like()) - elec_couple_edge_with_doubles = AmplitudeVector(ph=mv("elec_couple_edge"), pphh=v.pphh.zeros_like()) + if "pphh_pphh_elec" in self.blocks_ph.keys() and not hasattr(self.reference_state, "first_order_coupling"): # noqa: E501 + phot_couple_edge_with_doubles = AmplitudeVector(ph=mv("phot_couple_edge"), + pphh=v.pphh.zeros_like()) + elec_couple_edge_with_doubles = AmplitudeVector(ph=mv("elec_couple_edge"), + pphh=v.pphh.zeros_like()) elec_part = mv("elec") + mv("phot_couple") + phot_couple_edge_with_doubles - phot2_part = elec_couple_edge_with_doubles + mv("elec_couple_inner") + mv("phot2") + phot2_part = elec_couple_edge_with_doubles + \ + mv("elec_couple_inner") + mv("phot2") else: elec_part = mv("elec") + mv("phot_couple") phot2_part = mv("elec_couple_inner") + mv("phot2") @@ -445,11 +456,12 @@ def mv(qed_disp_key): continue if "pphh_pphh_elec" in self.blocks_ph.keys(): - return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, + return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, + gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) - #return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot_part.ph * 0, None) + return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, + gs2_part, phot2_part.ph, None) else: TypeError("matvec needs to be invoked with AmplitudeVector or QED_AmplitudeVector") diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index fe8baf83..3f6a0c85 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -223,15 +223,15 @@ def __radd__(self, other): class QED_AmplitudeVector: - # TODO: initialize this class with **kwargs, and think of a functionality, which then eases up - # e.g. the matvec function in the AdcMatrix.py for arbitrarily large QED vectors. However, this is - # not necessarily required, since e.g. QED-ADC(3) is very complicated to derive and QED-ADC(1) (with - # just the single dispersion mode) is purely for academic purposes and hence not required to provide - # optimum performance - def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, gs2=None, ph2=None, pphh2=None): + # TODO: Initialize this class with **kwargs, and think of a functionality, + # which then eases up e.g. the matvec function in the AdcMatrix.py for + # arbitrarily large QED vectors. However, this is not necessarily required, + # since e.g. QED-ADC(3) is very complicated to derive and QED-ADC(1) (with + # just the single dispersion mode) is purely for academic purposes and + # hence not required to provide optimum performance. - self.gs1 = gs1 - self.gs2 = gs2 + def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, + gs2=None, ph2=None, pphh2=None): if pphh != None: self.elec = AmplitudeVector(ph=ph, pphh=pphh) @@ -241,28 +241,28 @@ def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, gs2=None, self.elec = AmplitudeVector(ph=ph) self.phot = AmplitudeVector(ph=ph1) self.phot2 = AmplitudeVector(ph=ph2) - try: - self.ph = ph - self.ph1 = ph1 - self.ph2 = ph2 - except AttributeError: - pass - try: - self.pphh = pphh - self.pphh1 = pphh1 - self.pphh2 = pphh2 - except AttributeError: - # there are no doubles terms --> ADC(0) or ADC(1) - pass - + + self.gs1 = gs1 + self.gs2 = gs2 + self.ph = ph + self.ph1 = ph1 + self.ph2 = ph2 + self.pphh = pphh + self.pphh1 = pphh1 + self.pphh2 = pphh2 + + def dot(self, invec): def dot_(self, invec): if "pphh" in self.elec.blocks_ph: return (self.elec.ph.dot(invec.elec.ph) + self.elec.pphh.dot(invec.elec.pphh) - + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + self.phot.pphh.dot(invec.phot.pphh) - + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) + self.phot2.pphh.dot(invec.phot2.pphh)) + + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + + self.phot.pphh.dot(invec.phot.pphh) + + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) + + self.phot2.pphh.dot(invec.phot2.pphh)) else: - return (self.elec.ph.dot(invec.elec.ph) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + return (self.elec.ph.dot(invec.elec.ph) + + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) ) if isinstance(invec, list): return np.array([dot_(self, elem) for elem in invec]) @@ -281,68 +281,78 @@ def __matmul__(self, other): def __sub__(self, invec): if isinstance(invec, (float, int)): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), pphh=self.elec.pphh.__sub__(invec), - gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), pphh1=self.phot.pphh.__sub__(invec), - gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec), pphh2=self.phot2.pphh.__sub__(invec)) + return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), + pphh=self.elec.pphh.__sub__(invec), + gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), + pphh1=self.phot.pphh.__sub__(invec), + gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec), + pphh2=self.phot2.pphh.__sub__(invec)) else: - return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), - gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec)) + return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), + gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), + gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec)) def __truediv__(self, other): if isinstance(other, QED_AmplitudeVector): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other.elec.ph), pphh=self.elec.pphh.__truediv__(other.elec.pphh), - gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), pphh1=self.phot.pphh.__truediv__(other.phot.pphh), - gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph), pphh2=self.phot2.pphh.__truediv__(other.phot2.pphh)) + return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other.elec.ph), + pphh=self.elec.pphh.__truediv__(other.elec.pphh), + gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), + pphh1=self.phot.pphh.__truediv__(other.phot.pphh), + gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph), + pphh2=self.phot2.pphh.__truediv__(other.phot2.pphh)) else: return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other.elec.ph), - gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), - gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph)) + gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), + gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph)) elif isinstance(other, (float, int)): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other), pphh=self.elec.pphh.__truediv__(other), - gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), pphh1=self.phot.pphh.__truediv__(other), - gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other), pphh2=self.phot2.pphh.__truediv__(other)) + return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other), + pphh=self.elec.pphh.__truediv__(other), + gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), + pphh1=self.phot.pphh.__truediv__(other), + gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other), + pphh2=self.phot2.pphh.__truediv__(other)) else: return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other), - gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), - gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) + gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), + gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) def zeros_like(self): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.zeros_like().ph, pphh=self.elec.zeros_like().pphh, gs1=0, - ph1=self.phot.zeros_like().ph, pphh1=self.phot.zeros_like().pphh, gs2=0, - ph2=self.phot2.zeros_like().ph, pphh2=self.phot2.zeros_like().pphh) + return QED_AmplitudeVector(ph=self.elec.zeros_like().ph, + pphh=self.elec.zeros_like().pphh, + gs1=0, ph1=self.phot.zeros_like().ph, pphh1=self.phot.zeros_like().pphh, + gs2=0, ph2=self.phot2.zeros_like().ph, pphh2=self.phot2.zeros_like().pphh) else: - return QED_AmplitudeVector(ph=self.elec.zeros_like().ph, pphh=None, gs1=0, - ph1=self.phot.zeros_like().ph, pphh1=None, gs2=0, - ph2=self.phot2.zeros_like().ph, pphh2=None) + return QED_AmplitudeVector(ph=self.elec.zeros_like().ph, pphh=None, + gs1=0, ph1=self.phot.zeros_like().ph, pphh1=None, + gs2=0, ph2=self.phot2.zeros_like().ph, pphh2=None) def empty_like(self): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.empty_like().ph, pphh=self.elec.empty_like().pphh, gs1=0, - ph1=self.phot.empty_like().ph, pphh1=self.phot.empty_like().pphh, gs2=0, - ph2=self.phot2.empty_like().ph, pphh2=self.phot2.empty_like().pphh) + return QED_AmplitudeVector(ph=self.elec.empty_like().ph, + pphh=self.elec.empty_like().pphh, + gs1=0, ph1=self.phot.empty_like().ph, pphh1=self.phot.empty_like().pphh, + gs2=0, ph2=self.phot2.empty_like().ph, pphh2=self.phot2.empty_like().pphh) else: - QED_AmplitudeVector(ph=self.elec.empty_like().ph, pphh=None, gs1=0, - ph1=self.phot.empty_like().ph, pphh1=None, gs2=0, - ph2=self.phot2.empty_like().ph, pphh2=None) + QED_AmplitudeVector(ph=self.elec.empty_like().ph, pphh=None, + gs1=0, ph1=self.phot.empty_like().ph, pphh1=None, + gs2=0, ph2=self.phot2.empty_like().ph, pphh2=None) def copy(self): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.copy().ph, pphh=self.elec.copy().pphh, gs1=self.gs1, - ph1=self.phot.copy().ph, pphh1=self.phot.copy().pphh, gs2=self.gs2, - ph2=self.phot2.copy().ph, pphh2=self.phot2.copy().pphh) + return QED_AmplitudeVector(ph=self.elec.copy().ph, pphh=self.elec.copy().pphh, + gs1=self.gs1, ph1=self.phot.copy().ph, pphh1=self.phot.copy().pphh, + gs2=self.gs2, ph2=self.phot2.copy().ph, pphh2=self.phot2.copy().pphh) else: - QED_AmplitudeVector(ph=self.elec.copy().ph, pphh=None, gs1=self.gs1, - ph1=self.phot.copy().ph, pphh1=None, gs2=self.gs2, - ph2=self.phot2.copy().ph, pphh2=None) + QED_AmplitudeVector(ph=self.elec.copy().ph, pphh=None, + gs1=self.gs1, ph1=self.phot.copy().ph, pphh1=None, + gs2=self.gs2, ph2=self.phot2.copy().ph, pphh2=None) def evaluate(self): self.elec.evaluate() self.phot.evaluate() self.phot2.evaluate() - self.gs1 - self.gs2 return self \ No newline at end of file diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py index b68adf74..e88170c9 100644 --- a/adcc/ElectronicTransition.py +++ b/adcc/ElectronicTransition.py @@ -180,13 +180,13 @@ def transition_dipole_moments_qed(self): purely electric subblock """ if hasattr(self.reference_state, "approx"): - if self.property_method.level == 0: - warnings.warn("ADC(0) transition dipole moments are known to be " - "faulty in some cases.") + dipole_integrals = self.operators.electric_dipole def tdm(i, prop_level): self.ground_state.tdm_contribution = prop_level - return transition_dm(self.method, self.ground_state, self.excitation_vector[i]) + return transition_dm(self.method, self.ground_state, + self.excitation_vector[i]) + if hasattr(self.reference_state, "first_order_coupling"): return np.array([ @@ -208,7 +208,7 @@ def tdm(i, prop_level): @timed_member_call(timer="_property_timer") def s2s_dipole_moments_qed(self): """ - List of diff_dipole moments of all computed states + List of s2s transition dipole moments of all computed states to build the QED-matrix in the basis of the diagonal purely electric subblock """ @@ -220,25 +220,24 @@ def s2s_dipole_moments_qed(self): def s2s(i, f, s2s_contribution): self.ground_state.s2s_contribution = s2s_contribution vec = self.excitation_vector - return state2state_transition_dm(self.method, self.ground_state, vec[i], vec[f]) + return state2state_transition_dm(self.method, self.ground_state, + vec[i], vec[f]) def final_block(name): - return np.array([[product_trace(dipole_integrals[2], s2s(i, j, name)) for j in np.arange(n_states)] - for i in np.arange(n_states)]) + return np.array([[product_trace(dipole_integrals[2], s2s(i, j, name)) + for j in np.arange(n_states)] for i in np.arange(n_states)]) block_dict = {} block_dict["qed_adc1_off_diag"] = final_block("adc1") - if self.method.name == "adc2" and not hasattr(self.reference_state, "first_order_coupling"): + if self.method.name == "adc2" and not hasattr(self.reference_state, "first_order_coupling"): # noqa: E501 block_dict["qed_adc2_diag"] = final_block("qed_adc2_diag") - #print(block_dict["qed_adc2_diag"]) block_dict["qed_adc2_edge_couple"] = final_block("qed_adc2_edge_couple") - block_dict["qed_adc2_edge_phot_couple"] = final_block("qed_adc2_edge_phot_couple") + block_dict["qed_adc2_edge_phot_couple"] = final_block("qed_adc2_edge_phot_couple") # noqa: E501 block_dict["qed_adc2_ph_pphh"] = final_block("qed_adc2_ph_pphh") block_dict["qed_adc2_pphh_ph"] = final_block("qed_adc2_pphh_ph") - #print(block_dict["qed_adc2_diag"].tolist()) return block_dict else: return ("s2s_dipole_moments_qed are only calculated," @@ -249,7 +248,7 @@ def final_block(name): @timed_member_call(timer="_property_timer") def qed_second_order_ph_ph_couplings(self): """ - List of blocks of the expectation value of the perturbation + List of blocks containing the expectation value of the perturbation of the Hamiltonian for all computed states required to build the QED-matrix in the basis of the diagonal purely electric subblock @@ -259,14 +258,18 @@ def qed_second_order_ph_ph_couplings(self): def couple(qed_t1, ul, ur): return { - b.ooov: einsum("kc,ia,ja->kjic", qed_t1, ul, ur) + einsum("ka,ia,jb->jkib", qed_t1, ul, ur), - b.ovvv: einsum("kc,ia,ib->kacb", qed_t1, ul, ur) + einsum("ic,ia,jb->jabc", qed_t1, ul, ur) + b.ooov: einsum("kc,ia,ja->kjic", qed_t1, ul, ur) + \ + einsum("ka,ia,jb->jkib", qed_t1, ul, ur), + b.ovvv: einsum("kc,ia,ib->kacb", qed_t1, ul, ur) + \ + einsum("ic,ia,jb->jabc", qed_t1, ul, ur) } def phot_couple(qed_t1, ul, ur): return { - b.ooov: einsum("kc,ia,ja->kijc", qed_t1, ul, ur) + einsum("kb,ia,jb->ikja", qed_t1, ul, ur), - b.ovvv: einsum("kc,ia,ib->kbca", qed_t1, ul, ur) + einsum("jc,ia,jb->ibac", qed_t1, ul, ur) + b.ooov: einsum("kc,ia,ja->kijc", qed_t1, ul, ur) + \ + einsum("kb,ia,jb->ikja", qed_t1, ul, ur), + b.ovvv: einsum("kc,ia,ib->kbca", qed_t1, ul, ur) + \ + einsum("jc,ia,jb->ibac", qed_t1, ul, ur) } def prod_sum(hf, two_p_op): @@ -274,8 +277,8 @@ def prod_sum(hf, two_p_op): + einsum("iabc,iabc->", hf.ovvv, two_p_op[b.ovvv])) def final_block(func): - return np.array([[prod_sum(self.reference_state, func(qed_t1, i.ph, j.ph)) for i in self.excitation_vector] - for j in self.excitation_vector]) + return np.array([[prod_sum(self.reference_state, func(qed_t1, i.ph, j.ph)) + for i in self.excitation_vector] for j in self.excitation_vector]) block_dict = {} block_dict["couple"] = final_block(couple) diff --git a/adcc/ExcitedStates.py b/adcc/ExcitedStates.py index bc5924a2..aa933b4c 100644 --- a/adcc/ExcitedStates.py +++ b/adcc/ExcitedStates.py @@ -415,13 +415,13 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): vector_format = FormatExcitationVector(self.matrix, tolerance=tolerance, index_format=index_format) - #for qed - #for i, vec in enumerate(self.excitation_vector): - # self.excitation_vector[i] = vec.elec # using this leaves the excitation_vector object like this, - # which however is the standard output for the excitation vectors, so only the .elec vector is accessible this way # Optimise the formatting by pre-inspecting all tensors for tensor in self.excitation_vector: if isinstance(tensor, QED_AmplitudeVector): + # TODO: Implement tdm and s2s_tdm for QED, so properties can also + # be evaluated for QED_AmplitudeVector objects. For now only + # use AmplitudeVector describing the electric part, so the + # formatting does not have to be adapted. tensor = tensor.elec vector_format.optimise_formatting(tensor) @@ -432,6 +432,11 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): ret = separator for i, vec in enumerate(self.excitation_vector): if isinstance(vec, QED_AmplitudeVector): + # TODO: Implement tdm and s2s_tdm for QED, so properties can also + # be evaluated for QED_AmplitudeVector objects. For now only + # use AmplitudeVector describing the electric part, since + # most low-energy states are almost purely electric, so the + # formatting does not have to be adapted. vec = vec.elec ene = self.excitation_energy[i] eev = ene * eV diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index d1565239..f8328b3d 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -47,7 +47,7 @@ def __init__(self, hf): self.mospaces = hf.mospaces self.timer = Timer() self.has_core_occupied_space = hf.has_core_occupied_space - #for qed mp2 + # for qed mp self.get_qed_total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) self.get_qed_total_dip.oo = hf.get_qed_total_dip(b.oo) self.get_qed_total_dip.ov = hf.get_qed_total_dip(b.ov) @@ -134,28 +134,29 @@ def mp2_diffdm(self): ret = OneParticleOperator(self.mospaces, is_symmetric=True) # NOTE: the following 3 blocks are equivalent to the cvs_p0 intermediates # defined at the end of this file - # the following terms including omega originate from the qed correction ret.oo = -0.5 * einsum("ikab,jkab->ij", self.t2oo, self.t2oo) - #+ einsum("ia,ja->ij", self.qed_t1(b.ov), self.qed_t1(b.ov)) * omega) ret.ov = -0.5 * ( + einsum("ijbc,jabc->ia", self.t2oo, hf.ovvv) + einsum("jkib,jkab->ia", hf.ooov, self.t2oo) - #- (einsum("ib,ab->ia", self.qed_t1(b.ov), hf.get_qed_total_dip(b.vv)) - # - einsum("ji,ja->ia", hf.get_qed_total_dip(b.oo), self.qed_t1(b.ov))) * omega ) / self.df(b.ov) ret.vv = 0.5 * einsum("ijac,ijbc->ab", self.t2oo, self.t2oo) - #+ einsum("ia,ib->ab", self.qed_t1(b.ov), self.qed_t1(b.ov)) * omega) - if hasattr(hf, "coupling"):# and not hasattr(hf, "approx"): + + if hasattr(hf, "coupling"): + # qed correction print("mp2 diffdm has been adapted to qed") omega = ReferenceState.get_qed_omega(hf) - ret.oo -= 0.5 * einsum("ia,ja->ij", self.qed_t1(b.ov), self.qed_t1(b.ov)) * omega + ret.oo -= 0.5 * einsum("ia,ja->ij", self.qed_t1(b.ov), + self.qed_t1(b.ov)) * omega - ret.ov += 0.5 * (einsum("ib,ab->ia", self.qed_t1(b.ov), hf.get_qed_total_dip(b.vv)) - - einsum("ji,ja->ia", hf.get_qed_total_dip(b.oo), self.qed_t1(b.ov)) * omega + ret.ov += 0.5 * (einsum("ib,ab->ia", self.qed_t1(b.ov), + hf.get_qed_total_dip(b.vv)) + - einsum("ji,ja->ia", hf.get_qed_total_dip(b.oo), + self.qed_t1(b.ov)) * omega ) / self.df(b.ov) - ret.vv += 0.5 * einsum("ia,ib->ab", self.qed_t1(b.ov), self.qed_t1(b.ov)) * omega + ret.vv += 0.5 * einsum("ia,ib->ab", self.qed_t1(b.ov), + self.qed_t1(b.ov)) * omega if self.has_core_occupied_space: # additional terms to "revert" CVS for ground state density @@ -227,6 +228,9 @@ def density(self, level=2): @cached_member_function def qed_t1_df(self, space): + """ + qed_t1 amplitude times df + """ total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) if space == b.oo: total_dip.oo = self.get_qed_total_dip.oo @@ -241,8 +245,8 @@ def qed_t1_df(self, space): @cached_member_function def qed_t1(self, space): """ - Return new electronic singly excited amplitude in the first order correction - to the wavefunction for qed + Return new electronic singly excited amplitude in the first + order correction to the wavefunction for qed """ if space != b.ov: raise NotImplementedError("qed_t1 term not implemented " @@ -251,6 +255,9 @@ def qed_t1(self, space): @cached_member_function def qed_t0_df(self, space): + """ + qed_t1 amplitude times df + """ total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) total_dip.oo = self.get_qed_total_dip.oo total_dip.ov = self.get_qed_total_dip.ov @@ -269,23 +276,17 @@ def qed_t0_df(self, space): @cached_member_function def qed_t0(self, space): - """ Return new electronic singly excited amplitude in the first order correction to the wavefunction for qed for N=0 """ + """ + Return second new electronic singly excited amplitude in the first + order correction to the wavefunction for qed from the standard + HF reference + """ if space != b.ov: raise NotImplementedError("qed_t0 term not implemented " f"for space {space}.") return self.qed_t0_df(b.ov) / self.df(b.ov) - @cached_member_function - def diff_df(self, space): - if space == b.ov: - raise NotImplementedError("Request this space from df") - elif space == b.vv: # this returns (eps_a - eps_b) - return einsum("ia,ib->ab", self.df(b.ov), - self.df(b.ov)) - elif space == b.oo: # this returns (- eps_i + eps_j) - return einsum("ia,ja->ij", self.df(b.ov), - self.df(b.ov)) - - def dipole_moment(self, level=2): """ Return the MP dipole moment at the specified level of @@ -303,25 +304,24 @@ def dipole_moment(self, level=2): @cached_member_function def energy_correction(self, level=2): """Obtain the MP energy correction at a particular level""" - qed_mp2_correction = 0 if level > 3: raise NotImplementedError(f"MP({level}) energy correction " "not implemented.") - # For qed_mp1 from non-qed-hf also first corrections come into play...for now done in mp2 part here + # For qed_mp1 from non-qed-hf also first order corrections come into play, + # which is for now done in mp2 part here if level < 2: return 0.0 hf = self.reference_state is_cvs = self.has_core_occupied_space + qed_mp2_correction = 0 if level == 2 and not is_cvs: terms = [(1.0, hf.oovv, self.t2oo)] - #mp2_correction = sum( - # -0.25 * pref * eri.dot(t2) - # for pref, eri, t2 in terms - #) if hasattr(hf, "coupling"): - #print("mp2 energy with two electron qed perturbation " + str(mp2_correction)) + # print("mp2 energy with two electron qed perturbation " + + # str(mp2_correction)) total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) - omega, total_dip.ov = ReferenceState.get_qed_omega(hf), self.get_qed_total_dip.ov + omega = ReferenceState.get_qed_omega(hf) + total_dip.ov = self.get_qed_total_dip.ov qed_terms = [(omega/2, total_dip.ov, self.qed_t1(b.ov))] qed_mp2_correction_1 = sum( -pref * lambda_dip.dot(qed_t) @@ -329,24 +329,30 @@ def energy_correction(self, level=2): ) if hasattr(hf, "qed_hf"): qed_mp2_correction = qed_mp2_correction_1 - #print("QED-MP(2) energy correction for QED-HF = " + str(mp2_correction + qed_mp2_correction_1)) + # print("QED-MP(2) energy correction for QED-HF = " + + # str(mp2_correction + qed_mp2_correction_1)) else: qed_terms_0 = [(1.0, self.qed_t0(b.ov), self.qed_t0_df(b.ov))] qed_mp2_correction_0 = sum( -0.25 * pref * ampl_t0.dot(ampl_t0_df) for pref, ampl_t0, ampl_t0_df in qed_terms_0 ) - #mp1 terms: + # mp1 terms: qed_mp1_additional_terms = [(0.5, total_dip.ov)] qed_mp1_correction = sum( pref * lambda_dip.dot(lambda_dip) for pref, lambda_dip in qed_mp1_additional_terms ) - #print("QED-MP(2) energy correction for standard HF (includes QED-MP(1) too) = " - #+ str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0 + qed_mp1_correction)) - qed_mp2_correction = qed_mp2_correction_1 + qed_mp2_correction_0 + qed_mp1_correction + qed_mp1_correction - #print("qed-mp1 correction, due to standard hf input " + str(qed_mp1_correction)) - #print("new qed-mp2 correction compared to qed-hf " + str(qed_mp2_correction_0)) + # print("QED-MP(2) energy correction for standard HF + # (includes QED-MP(1) too) = " + # + str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0 + # + qed_mp1_correction)) + qed_mp2_correction = qed_mp2_correction_1 + qed_mp2_correction_0 +\ + qed_mp1_correction + qed_mp1_correction + # print("qed-mp1 correction, due to standard hf input " + # + str(qed_mp1_correction)) + # print("new qed-mp2 correction compared to qed-hf " + # + str(qed_mp2_correction_0)) elif level == 2 and is_cvs: terms = [(1.0, hf.oovv, self.t2oo), (2.0, hf.ocvv, self.t2oc), diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index 7cc86d01..cb13ebc0 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -175,6 +175,16 @@ def __getattr__(self, attr): @cached_member_function def get_qed_total_dip(self, block): + """ + Return qed coupling strength times dipole operator + """ + # TODO: Here we always multiply with sqrt(2 * omega), since this + # eases up the Hamiltonian and is required if you provide a hilbert + # package QED-HF input. This can differ between QED-HF implementations, + # e.g. the psi4numpy QED-RHF helper does not do that. Therefore, this + # factor needs to be adjusted depending on the input, but since the + # hilbert package is currently the best in terms of performance, at + # least to my knowledge, the factor should be included here. if hasattr(self, "coupling"): from . import block as b dips = self.operators.electric_dipole @@ -188,12 +198,18 @@ def get_qed_total_dip(self, block): @cached_member_function def get_qed_omega(self): + """ + Return the cavity frequency + """ if hasattr(self, "coupling"): freqs = self.frequency return np.linalg.norm(freqs) @cached_member_function def qed_D_object(self, block): + """ + Return the object, which is added to the ERIs in a PT QED calculation + """ if hasattr(self, "coupling"): from . import block as b from .functions import einsum @@ -201,16 +217,23 @@ def qed_D_object(self, block): total_dip.oo = ReferenceState.get_qed_total_dip(self, b.oo) total_dip.ov = ReferenceState.get_qed_total_dip(self, b.ov) total_dip.vv = ReferenceState.get_qed_total_dip(self, b.vv) - # We have to define all the blocks from the D_{pqrs} = d_{pr} d_{qs} - d_{ps} d_{qr} object, which has the + # We have to define all the blocks from the + # D_{pqrs} = d_{pr} d_{qs} - d_{ps} d_{qr} object, which has the # same symmetry properties as the ERI object - # Actually in b.ovov: second term: ib,ja would be ib,aj , but d_{ia} = d_{ai}, and d.ov is implemented (as usual) + # Actually in b.ovov: second term: ib,ja would be ib,aj , but d_{ia} = d_{ai} ds = { - b.oooo: einsum('ik,jl->ijkl', total_dip.oo, total_dip.oo) - einsum('il,jk->ijkl', total_dip.oo, total_dip.oo), - b.ooov: einsum('ik,ja->ijka', total_dip.oo, total_dip.ov) - einsum('ia,jk->ijka', total_dip.ov, total_dip.oo), - b.oovv: einsum('ia,jb->ijab', total_dip.ov, total_dip.ov) - einsum('ib,ja->ijab', total_dip.ov, total_dip.ov), - b.ovvv: einsum('ib,ac->iabc', total_dip.ov, total_dip.vv) - einsum('ic,ab->iabc', total_dip.ov, total_dip.vv), - b.ovov: einsum('ij,ab->iajb', total_dip.oo, total_dip.vv) - einsum('ib,ja->iajb', total_dip.ov, total_dip.ov), - b.vvvv: einsum('ac,bd->abcd', total_dip.vv, total_dip.vv) - einsum('ad,bc->abcd', total_dip.vv, total_dip.vv), + b.oooo: einsum('ik,jl->ijkl', total_dip.oo, total_dip.oo) - \ + einsum('il,jk->ijkl', total_dip.oo, total_dip.oo), + b.ooov: einsum('ik,ja->ijka', total_dip.oo, total_dip.ov) - \ + einsum('ia,jk->ijka', total_dip.ov, total_dip.oo), + b.oovv: einsum('ia,jb->ijab', total_dip.ov, total_dip.ov) - \ + einsum('ib,ja->ijab', total_dip.ov, total_dip.ov), + b.ovvv: einsum('ib,ac->iabc', total_dip.ov, total_dip.vv) - \ + einsum('ic,ab->iabc', total_dip.ov, total_dip.vv), + b.ovov: einsum('ij,ab->iajb', total_dip.oo, total_dip.vv) - \ + einsum('ib,ja->iajb', total_dip.ov, total_dip.ov), + b.vvvv: einsum('ac,bd->abcd', total_dip.vv, total_dip.vv) - \ + einsum('ad,bc->abcd', total_dip.vv, total_dip.vv), } return ds[block] @@ -231,13 +254,6 @@ def eri(self, block): ds[block] = ReferenceState.qed_D_object(self, block) return super().eri(block) + ds[block] else: - #raise InvalidReference( - #"Please define the attribute coupling and frequency to the Psi4 wfn object, before importing it in adcc!" - #"e.g. : refstate = adcc.ReferenceState(wfn)" - #"refstate.coupling = [x, y, z] # Just as you did in hilbert" - #"refstate.frequency = [x, y, z] # Just as you did in hilbert" - #"state = adcc.adc2(refstate, n_singlets=3)" - #) return super().eri(block) @property diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 09b1ee6f..086c097d 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -104,9 +104,10 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): def block_ph_gs_0(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) -block_ph_gs_0_couple = block_ph_gs_0_phot_couple = block_ph_gs_0_phot = block_ph_gs_0 -block_ph_gs_0_couple_edge = block_ph_gs_0_phot_couple_edge = block_ph_gs_0_phot2 = block_ph_gs_0 -block_ph_gs_0_couple_inner = block_ph_gs_0_phot_couple_inner = block_ph_gs_0 +block_ph_gs_0_couple = block_ph_gs_0_phot_couple = block_ph_gs_0_phot =\ +block_ph_gs_0_couple_edge = block_ph_gs_0_phot_couple_edge =\ +block_ph_gs_0_phot2 = block_ph_gs_0_couple_inner = block_ph_gs_0_phot_couple_inner =\ + block_ph_gs_0 # @@ -140,8 +141,9 @@ def apply(ampl): def block_ph_ph_0_couple(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) -block_ph_ph_0_phot_couple = block_ph_ph_0_couple -block_ph_ph_0_phot_couple_edge = block_ph_ph_0_phot_couple_inner = block_ph_ph_0_couple_edge = block_ph_ph_0_couple_inner = block_ph_ph_0_couple +block_ph_ph_0_phot_couple = block_ph_ph_0_phot_couple_edge =\ +block_ph_ph_0_phot_couple_inner = block_ph_ph_0_couple_edge =\ +block_ph_ph_0_couple_inner = block_ph_ph_0_couple def block_ph_ph_0_phot(hf, mp, intermediates): @@ -156,45 +158,37 @@ def apply(ampl): - einsum("IJ,Ja->Ia", fCC, ampl.ph1) )) else: - raise NotImplementedError("coupling needs to be given to reference wavefunction in input file for QED-ADC") + raise NotImplementedError("block_ph_ph_0_phot is requested, " + "but ReferenceState has no coupling attribute") return AdcBlock(apply, diagonal) def block_ph_ph_0_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - if hasattr(hf, "coupling"): - diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), - fCC.diagonal())) + diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), + fCC.diagonal())) - def apply(ampl): - return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph2, hf.fvv) - - einsum("IJ,Ja->Ia", fCC, ampl.ph2) - )) - else: - raise NotImplementedError("coupling needs to be given to reference wavefunction in input file for QED-ADC") + def apply(ampl): + return AmplitudeVector(ph=( + + einsum("ib,ab->ia", ampl.ph2, hf.fvv) + - einsum("IJ,Ja->Ia", fCC, ampl.ph2) + )) return AdcBlock(apply, diagonal) -def diagonal_pphh_pphh_0(hf): - # Note: adcman similarly does not symmetrise the occupied indices - # (for both CVS and general ADC) - fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - res = direct_sum("-i-J+a+b->iJab", - hf.foo.diagonal(), fCC.diagonal(), - hf.fvv.diagonal(), hf.fvv.diagonal()) - return AmplitudeVector(pphh=res.symmetrise(2, 3)) - - -def diagonal_pphh_pphh_0_qed(hf, n_omega): +def diagonal_pphh_pphh_0(hf, n_omega=None): # Note: adcman similarly does not symmetrise the occupied indices # (for both CVS and general ADC) - omega = float(ReferenceState.get_qed_omega(hf)) - qed = n_omega * omega - fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - res = direct_sum("-i-J+a+b->iJab", - hf.foo.diagonal() + qed, fCC.diagonal() + qed, - hf.fvv.diagonal() + qed, hf.fvv.diagonal() + qed) + if n_omega is not None: + omega = float(ReferenceState.get_qed_omega(hf)) + qed = n_omega * omega + res = direct_sum("-i-J+a+b->iJab", + hf.foo.diagonal() + qed, fCC.diagonal() + qed, + hf.fvv.diagonal() + qed, hf.fvv.diagonal() + qed) + else: + res = direct_sum("-i-J+a+b->iJab", + hf.foo.diagonal(), fCC.diagonal(), + hf.fvv.diagonal(), hf.fvv.diagonal()) return AmplitudeVector(pphh=res.symmetrise(2, 3)) @@ -211,9 +205,9 @@ def block_pphh_pphh_0_couple(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) -block_pphh_pphh_0_phot_couple = block_pphh_pphh_0_couple -block_pphh_pphh_0_phot_couple_edge = block_pphh_pphh_0_phot_couple_inner = block_pphh_pphh_0_couple -block_pphh_pphh_0_couple_edge = block_pphh_pphh_0_couple_inner = block_pphh_pphh_0_couple +block_pphh_pphh_0_phot_couple = block_pphh_pphh_0_phot_couple_edge =\ +block_pphh_pphh_0_phot_couple_inner = block_pphh_pphh_0_couple_edge =\ +block_pphh_pphh_0_couple_inner = block_pphh_pphh_0_couple def block_pphh_pphh_0_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -223,7 +217,7 @@ def apply(ampl): - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh1).antisymmetrise(0, 1) + omega * ampl.pphh1 )) - return AdcBlock(apply, diagonal_pphh_pphh_0_qed(hf, 1)) + return AdcBlock(apply, diagonal_pphh_pphh_0(hf, 1)) def block_cvs_pphh_pphh_0(hf, mp, intermediates): @@ -244,7 +238,7 @@ def apply(ampl): - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh2).antisymmetrise(0, 1) + 2 * omega * ampl.pphh2 )) - return AdcBlock(apply, diagonal_pphh_pphh_0_qed(hf, 2)) + return AdcBlock(apply, diagonal_pphh_pphh_0(hf, 2)) # @@ -261,12 +255,15 @@ def block_pphh_ph_0(hf, mp, intermediates): block_cvs_ph_pphh_0 = block_ph_pphh_0 block_cvs_pphh_ph_0 = block_pphh_ph_0 -block_pphh_ph_0_couple = block_pphh_ph_0_phot_couple = block_pphh_ph_0_phot = block_pphh_ph_0 -block_pphh_ph_0_couple_edge = block_pphh_ph_0_couple_inner = block_pphh_ph_0 -block_pphh_ph_0_phot_couple_edge = block_pphh_ph_0_phot_couple_inner = block_pphh_ph_0_phot2 = block_pphh_ph_0 -block_ph_pphh_0_couple = block_ph_pphh_0_phot_couple = block_ph_pphh_0_phot = block_ph_pphh_0 -block_ph_pphh_0_couple_edge = block_ph_pphh_0_couple_inner = block_ph_pphh_0 -block_ph_pphh_0_phot_couple_edge = block_ph_pphh_0_phot_couple_inner = block_ph_pphh_0_phot2 = block_ph_pphh_0 +block_pphh_ph_0_couple = block_pphh_ph_0_phot_couple = block_pphh_ph_0_phot =\ +block_pphh_ph_0_couple_edge = block_pphh_ph_0_couple_inner =\ +block_pphh_ph_0_phot_couple_edge = block_pphh_ph_0_phot_couple_inner =\ +block_pphh_ph_0_phot2 = block_pphh_ph_0 + +block_ph_pphh_0_couple = block_ph_pphh_0_phot_couple = block_ph_pphh_0_phot =\ +block_ph_pphh_0_couple_edge = block_ph_pphh_0_couple_inner =\ +block_ph_pphh_0_phot_couple_edge = block_ph_pphh_0_phot_couple_inner =\ +block_ph_pphh_0_phot2 = block_ph_pphh_0 @@ -283,13 +280,13 @@ def apply(ampl): return omega * ampl.gs1 return AdcBlock(apply, omega) -block_ph_gs_1_phot_couple = block_ph_gs_1 -block_ph_gs_1_phot_couple_edge = block_ph_gs_1_couple_edge = block_ph_gs_1 +block_ph_gs_1_phot_couple = block_ph_gs_1_phot_couple_edge =\ +block_ph_gs_1_couple_edge = block_ph_gs_1 def block_ph_gs_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return (-1) * sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph) + return (-1) * sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph) return AdcBlock(apply, 0) def block_ph_gs_1_phot2(hf, mp, intermediates): @@ -301,7 +298,7 @@ def apply(ampl): def block_ph_gs_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return (-1) * sqrt(omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1) + return (-1) * sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1) return AdcBlock(apply, 0) def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): @@ -319,7 +316,8 @@ def block_ph_ph_1(hf, mp, intermediates): diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), + einsum("aa->a", mp.qed_t0_df(b.vv))) )) def apply(ampl): @@ -384,17 +382,13 @@ def block_ph_ph_1_phot(hf, mp, intermediates): CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): omega = float(ReferenceState.get_qed_omega(hf)) - # Build two Kronecker deltas - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) - + einsum("ii,aa->ia", d_oo, d_vv) * omega + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), + einsum("aa->a", mp.qed_t0_df(b.vv))) + + intermediates.delta_ia_omega )) def apply(ampl): @@ -408,11 +402,6 @@ def apply(ampl): )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): omega = float(ReferenceState.get_qed_omega(hf)) - # Build two Kronecker deltas - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) if hasattr(hf, "first_order_coupling"): i1 = intermediates.adc2_i1 @@ -422,7 +411,7 @@ def apply(ampl): + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + einsum("ii,aa->ia", d_oo, d_vv) * omega + + intermediates.delta_ia_omega )) def apply(ampl): @@ -437,7 +426,7 @@ def apply(ampl): diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - + einsum("ii,aa->ia", d_oo, d_vv) * omega + + intermediates.delta_ia_omega )) def apply(ampl): @@ -448,7 +437,8 @@ def apply(ampl): + omega * ampl.ph1 )) else: - raise NotImplementedError("and not hasattr(hf, qed_hf)") + raise NotImplementedError("block_ph_ph_1_phot is requested, " + "but ReferenceState has no coupling attribute") return AdcBlock(apply, diagonal) @@ -458,7 +448,7 @@ def block_ph_ph_1_couple(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) )) return AdcBlock(apply, 0) @@ -488,7 +478,7 @@ def block_ph_ph_1_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) )) return AdcBlock(apply, 0) @@ -499,8 +489,8 @@ def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) - - mp.qed_t1_df(b.ov) * ampl.gs2) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) + - mp.qed_t1_df(b.ov) * ampl.gs2) )) return AdcBlock(apply, 0) @@ -510,17 +500,12 @@ def block_ph_ph_1_phot2(hf, mp, intermediates): if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): omega = float(ReferenceState.get_qed_omega(hf)) - # Build two Kronecker deltas - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), einsum("aa->a", mp.qed_t0_df(b.vv))) - + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 + + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), + einsum("aa->a", mp.qed_t0_df(b.vv))) + + intermediates.delta_ia_omega * 2 )) def apply(ampl): @@ -535,12 +520,6 @@ def apply(ampl): elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): omega = float(ReferenceState.get_qed_omega(hf)) - # Build two Kronecker deltas - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - if hasattr(hf, "first_order_coupling"): i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 @@ -549,7 +528,7 @@ def apply(ampl): + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 + + intermediates.delta_ia_omega * 2 )) def apply(ampl): @@ -564,7 +543,7 @@ def apply(ampl): diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 + + intermediates.delta_ia_omega * 2 )) def apply(ampl): @@ -724,16 +703,21 @@ def apply(ampl): return AdcBlock(apply, 0) -block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner = block_pphh_ph_0_couple +block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner =\ +block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge =\ +block_pphh_ph_0_couple -block_ph_pphh_1_phot_couple = block_ph_pphh_1_phot_couple_inner = block_ph_pphh_0_phot_couple +block_ph_pphh_1_phot_couple = block_ph_pphh_1_phot_couple_inner =\ +block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge =\ +block_ph_pphh_0_phot_couple def block_pphh_ph_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( - 2 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) + 2 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), + ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) )) return AdcBlock(apply, 0) @@ -742,16 +726,12 @@ def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): return AmplitudeVector(pphh=( - 2 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) + 2 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), + ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) )) return AdcBlock(apply, 0) -block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge = block_pphh_ph_0_couple - -block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge = block_ph_pphh_0_phot_couple - - # # 2nd order gs blocks # @@ -768,10 +748,9 @@ def block_ph_gs_2_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return ( sqrt(omega / 2) * einsum("jb,jb->", ( - + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), - ampl.ph) - - sqrt(0.5 * omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph)) # 1. order + return ( sqrt(omega / 2) * einsum("jkbc,kc->jb", mp.t2oo, + mp.qed_t1_df(b.ov)).dot(ampl.ph) + - sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph)) # 1. order return AdcBlock(apply, 0) def block_ph_gs_2_phot(hf, mp, intermediates): @@ -798,10 +777,9 @@ def block_ph_gs_2_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return ( sqrt(omega) * einsum("jb,jb->", ( - + einsum("jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov))), - ampl.ph1) - - sqrt(omega) * einsum("jb,jb->", mp.qed_t1_df(b.ov), ampl.ph1)) # 1. order + return ( sqrt(omega) * einsum("jkbc,kc->jb", mp.t2oo, + mp.qed_t1_df(b.ov)).dot(ampl.ph1) + - sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1)) # 1. order return AdcBlock(apply, 0) @@ -847,7 +825,8 @@ def apply(ampl): + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph))) )) else: - raise NotImplementedError("QED-ADC(2) from non-QED-HF reference is not implemented") + raise NotImplementedError("QED-ADC(2) from non-QED-HF reference" + "is not implemented") else: diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) @@ -891,10 +870,11 @@ def apply(ampl): + einsum("ib,ab->ia", ampl.ph, qed_i1) + einsum("ij,ja->ia", qed_i2, ampl.ph) + sqrt(omega / 2) * ( - + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) # electric H_1 term - + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) # electric H_1 term - + sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) # 1. order - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) # 1. order + + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) + + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) + + sqrt(omega / 2) * ( + - einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) # 1. order )) return AdcBlock(apply, 0) @@ -905,20 +885,20 @@ def block_ph_ph_2_phot_couple(hf, mp, intermediates): qed_i1 = intermediates.adc2_qed_phot_couple_i1 qed_i2 = intermediates.adc2_qed_phot_couple_i2 - diagonal = AmplitudeVector(ph=mp.df(b.ov).zeros_like()) def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph1, qed_i1) + einsum("ij,ja->ia", qed_i2, ampl.ph1) + sqrt(omega / 2) * ( - + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term - + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term + + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + gs_part * ampl.gs1 - + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs1) # gs_ph block 1. order + + sqrt(omega / 2) * ( + - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs1) # gs_ph block 1. order )) - return AdcBlock(apply, diagonal) + return AdcBlock(apply, 0) def block_ph_ph_2_phot(hf, mp, intermediates): @@ -928,11 +908,6 @@ def block_ph_ph_2_phot(hf, mp, intermediates): term_t2_eri = intermediates.term_t2_eri - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 @@ -943,7 +918,7 @@ def block_ph_ph_2_phot(hf, mp, intermediates): + (-omega/2) * 2 * ( - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - + einsum("ii,aa->ia", d_oo, d_vv) * omega # 1. order + + intermediates.delta_ia_omega # 1. order )) def apply(ampl): @@ -959,6 +934,10 @@ def apply(ampl): + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph1))) + omega * ampl.ph1 # 1. order )) + + if not hasattr(hf, "coupling"): + raise NotImplementedError("block_ph_ph_2_phot is requested, " + "but ReferenceState has no coupling attribute") return AdcBlock(apply, diagonal) @@ -969,11 +948,6 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): term_t2_eri = intermediates.term_t2_eri - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 @@ -984,7 +958,7 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): + (-omega/2) * 3 * ( - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - + einsum("ii,aa->ia", d_oo, d_vv) * omega * 2 # 1. order + + intermediates.delta_ia_omega * 2 # 1. order )) def apply(ampl): @@ -1013,10 +987,11 @@ def apply(ampl): + sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) + sqrt(omega) * ( - + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) # electric H_1 term - + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) # electric H_1 term - + sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) # 1. order + + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + + sqrt(omega) * ( + - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) # 1. order )) return AdcBlock(apply, 0) @@ -1032,12 +1007,13 @@ def apply(ampl): + sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) + sqrt(omega) * ( - + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) # electric H_1 term - + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) # electric H_1 term + + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) + + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) + gs_part * ampl.gs2 - + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs2) # gs_ph block # 1. order + + sqrt(omega) * ( + - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs2) # gs_ph block # 1. order )) return AdcBlock(apply, 0) @@ -1104,11 +1080,11 @@ def apply(ampl): return AmplitudeVector(pphh=( ( + einsum("ic,jcab->ijab", ampl.ph, pib_ovvv) - + einsum("lkic,kc,jlab->ijab", hf.ooov, ampl.ph, mp.t2oo) # 2st + + einsum("lkic,kc,jlab->ijab", hf.ooov, ampl.ph, mp.t2oo) # 2nd ).antisymmetrise(0, 1) + ( - einsum("ijka,kb->ijab", pia_ooov, ampl.ph) - - einsum("ijac,kbcd,kd->ijab", mp.t2oo, hf.ovvv, ampl.ph) # 2st + - einsum("ijac,kbcd,kd->ijab", mp.t2oo, hf.ovvv, ampl.ph) # 2nd ).antisymmetrise(2, 3) )) return AdcBlock(apply, 0) @@ -1161,7 +1137,8 @@ def adc2_i2(hf, mp, intermediates): return hf.foo - 0.5 * einsum("ikab,jkab->ij", mp.t2oo, hf.oovv).symmetrise() -# qed intermediates for adc2, without the factor of (omega/2), which is added in the actual matrix builder +# qed intermediates for adc2, without the factor of (omega/2), +# which is added in the actual matrix builder @register_as_intermediate def adc2_qed_i1(hf, mp, intermediates): # maybe do this with symmetrise return (1/2) * (einsum("kb,ka->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) @@ -1191,49 +1168,50 @@ def term_t2_eri(hf, mp, intermediates): def adc2_qed_ph_ph_2_phot_couple_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return sqrt(omega / 2) * ( - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + return sqrt(omega / 2) * (einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) @register_as_intermediate def adc2_qed_ph_ph_2_phot_couple_inner_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - d_oo = zeros_like(hf.foo) - d_vv = zeros_like(hf.fvv) - d_oo.set_mask("ii", 1.0) - d_vv.set_mask("aa", 1.0) - - return sqrt(omega) * ( - + einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) + return sqrt(omega) * (einsum("ikac,kc->ia", mp.t2oo, mp.qed_t1_df(b.ov))) @register_as_intermediate def adc2_qed_couple_i1(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * ( - + einsum("kc,kacb->ab", mp.qed_t1(b.ov), hf.ovvv))) + return ( sqrt(omega / 2) * (einsum("kc,kacb->ab", mp.qed_t1(b.ov), hf.ovvv))) @register_as_intermediate def adc2_qed_couple_i2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * ( - + einsum("kc,kjic->ij", mp.qed_t1(b.ov), hf.ooov))) + return ( sqrt(omega / 2) * (einsum("kc,kjic->ij", mp.qed_t1(b.ov), hf.ooov))) @register_as_intermediate def adc2_qed_phot_couple_i1(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * ( - + einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) + return ( sqrt(omega / 2) * (einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) @register_as_intermediate def adc2_qed_phot_couple_i2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * ( - + einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) + return ( sqrt(omega / 2) * (einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) + + +@register_as_intermediate +def delta_ia_omega(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + # Build two Kronecker deltas + d_oo = zeros_like(hf.foo) + d_vv = zeros_like(hf.fvv) + d_oo.set_mask("ii", 1.0) + d_vv.set_mask("aa", 1.0) + + return einsum("ii,aa->ia", d_oo, d_vv) * omega def adc3_i1(hf, mp, intermediates): diff --git a/adcc/adc_pp/state2state_transition_dm.py b/adcc/adc_pp/state2state_transition_dm.py index 7abb2e0d..cb019f4d 100644 --- a/adcc/adc_pp/state2state_transition_dm.py +++ b/adcc/adc_pp/state2state_transition_dm.py @@ -182,6 +182,8 @@ def s2s_tdm_adc2(mp, amplitude_l, amplitude_r, intermediates): # Ref: https://doi.org/10.1080/00268976.2013.859313 DISPATCH = {"adc0": s2s_tdm_adc0, "adc1": s2s_tdm_adc0, # same as ADC(0) + # The following qed terms are not the actual s2s densities, + # but those required for the approx function "qed_adc2_diag": s2s_tdm_qed_adc2_diag_part, "qed_adc2_edge_couple": s2s_tdm_qed_adc2_edge_part_couple, "qed_adc2_edge_phot_couple": s2s_tdm_qed_adc2_edge_part_phot_couple, @@ -227,8 +229,8 @@ def state2state_transition_dm(method, ground_state, amplitude_from, f"for {method.name}.") else: if hasattr(ground_state, "s2s_contribution"): - ret = DISPATCH[ground_state.s2s_contribution](ground_state, amplitude_to, amplitude_from, - intermediates) + ret = DISPATCH[ground_state.s2s_contribution](ground_state, amplitude_to, + amplitude_from, intermediates) else: # final state is on the bra side/left (complex conjugate) # see ref https://doi.org/10.1080/00268976.2013.859313, appendix A2 diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index a8b19cf6..66bdbb0d 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -135,6 +135,8 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): if not isinstance(amplitude, (AmplitudeVector, QED_AmplitudeVector)): raise TypeError("amplitude should be an AmplitudeVector object.") if isinstance(amplitude, QED_AmplitudeVector): + # TODO: Implement the actual transition densities for the qed methods, + # but for now just return the electric contribution amplitude = amplitude.elec if intermediates is None: intermediates = Intermediates(ground_state) @@ -143,8 +145,5 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): raise NotImplementedError("transition_dm is not implemented " f"for {method.name}.") else: - if hasattr(ground_state, "tdm_contribution"): - ret = DISPATCH[ground_state.tdm_contribution](ground_state, amplitude, intermediates) - else: - ret = DISPATCH[method.name](ground_state, amplitude, intermediates) + ret = DISPATCH[method.name](ground_state, amplitude, intermediates) return ret.evaluate() diff --git a/adcc/backends/__init__.py b/adcc/backends/__init__.py index 30bf1dd1..d4ad1118 100644 --- a/adcc/backends/__init__.py +++ b/adcc/backends/__init__.py @@ -83,6 +83,8 @@ def import_scf_results(res): import psi4 from . import psi4 as backend_psi4 + # Here we also have to import the .core.Wavefunction object, + # since this is the one returned by the hilbert package if isinstance(res, (psi4.core.HF, psi4.core.Wavefunction)): return backend_psi4.import_scf(res) diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index ef2b26c0..04037c8d 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -31,12 +31,6 @@ from ..exceptions import InvalidReference from ..ExcitedStates import EnergyCorrection -#global qed_from_qed_hf_input -#qed_from_qed_hf_input = False - -#if isinstance(wfn, psi4.core.Wavefunction): -# #We need this global variable later, to either perform QED-ADC from qed-hf input or standard non-qed-hf input -# qed_from_qed_hf_input = True class Psi4OperatorIntegralProvider: def __init__(self, wfn): @@ -176,6 +170,9 @@ def get_restricted(self): if isinstance(self.wfn, (psi4.core.RHF, psi4.core.ROHF)): return True elif isinstance(self.wfn, (psi4.core.Wavefunction)): + # This is the object returned by the hilbert package, which does + # not provide a restricted indicator, so we determine it here + # and print the result orben_a = np.asarray(self.wfn.epsilon_a()) orben_b = np.asarray(self.wfn.epsilon_b()) if all(orben_a == orben_b): @@ -274,7 +271,9 @@ def import_scf(wfn): # the actual set of options ... theoretically they could differ scf_type = psi4.core.get_global_option('SCF_TYPE') # CD = Choleski, DF = density-fitting - unsupported_scf_types = ["CD"]#, "DISK_DF", "MEM_DF"] + unsupported_scf_types = ["CD"] + if not isinstance(wfn, psi4.core.Wavefunction): # hilbert package only uses DF + unsupported_scf_types += ["DISK_DF", "MEM_DF"] if scf_type in unsupported_scf_types: raise InvalidReference("Unsupported Psi4 SCF_TYPE, should not be one " f"of {unsupported_scf_types}") diff --git a/adcc/functions.py b/adcc/functions.py index 1f494265..75fcc3fc 100644 --- a/adcc/functions.py +++ b/adcc/functions.py @@ -125,23 +125,22 @@ def lincomb(coefficients, tensors, evaluate=False): elif isinstance(tensors[0], QED_AmplitudeVector): gs1_part = 0 gs2_part = 0 - phot2_list = [] - elec_list = [] - phot_list = [] + elec_list = [ten.elec for ten in tensors] + phot_list = [ten.phot for ten in tensors] + phot2_list = [ten.phot2 for ten in tensors] for coeff_ind, ten in enumerate(tensors): gs1_part += coefficients[coeff_ind] * ten.gs1 gs2_part += coefficients[coeff_ind] * ten.gs2 - elec_list.append(ten.elec) - phot_list.append(ten.phot) - phot2_list.append(ten.phot2) elec_part = lincomb(coefficients, elec_list, evaluate=evaluate) phot_part = lincomb(coefficients, phot_list, evaluate=evaluate) phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) if "pphh" in elec_part.blocks_ph: - return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, - gs2_part, phot2_part.ph, phot2_part.pphh) + return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, + gs1_part, phot_part.ph, phot_part.pphh, + gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) + return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, + gs2_part, phot2_part.ph, None) elif not isinstance(tensors[0], libadcc.Tensor): raise TypeError("Tensor type not supported") diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index 8dee5c8b..876018f9 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -88,10 +88,6 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, guessfunction = guesses_from_diagonal_singles elif block == "pphh": guessfunction = guesses_from_diagonal_doubles - #elif block == "gs": - # guessfunction = guesses_from_diagonal_gs - #elif block == "pphh_phot": - # guessfunction = guesses_from_diagonal_doubles_phot else: raise ValueError(f"Don't know how to generate guesses for block {block}") @@ -289,12 +285,6 @@ def telem_nospin(telem): return [evaluate(v / np.sqrt(v @ v)) for v in ret[:ivec]] -#def guesses_from_diagonal_gs(matrix, n_guesses, diag, spin_change=0, -# spin_block_symmetrisation="none", -# degeneracy_tolerance=1e-14): -# return 0 - - def guesses_from_diagonal_doubles(matrix, n_guesses, diag, spin_change=0, spin_block_symmetrisation="none", degeneracy_tolerance=1e-14, diff --git a/adcc/qed_matrix_from_diag_adc.py b/adcc/qed_matrix_from_diag_adc.py index 0296a084..5983226a 100644 --- a/adcc/qed_matrix_from_diag_adc.py +++ b/adcc/qed_matrix_from_diag_adc.py @@ -43,7 +43,8 @@ def first_order_coupling(self): for i, tdm in enumerate(self.tdm): tdm_block[i] = self.coupl * np.sqrt(2 * self.freq) * tdm[2] - s2s_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] + s2s_block = - np.sqrt(self.freq/2) * self.coupl *\ + np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] tdm_block = - np.sqrt(self.freq/2) * tdm_block elec_block = np.diag(self.exc_en) @@ -51,39 +52,57 @@ def first_order_coupling(self): # build the matrix - matrix_upper = np.vstack((elec_block, tdm_block.reshape((1, self.n_adc)), s2s_block)) - matrix_middle = np.concatenate((tdm_block, np.array([self.freq]), np.zeros(self.n_adc))) - matrix_lower = np.vstack((s2s_block, np.zeros((1, self.n_adc)), phot_block)) + matrix_upper = np.vstack((elec_block, tdm_block.reshape((1, self.n_adc)), + s2s_block)) + matrix_middle = np.concatenate((tdm_block, np.array([self.freq]), + np.zeros(self.n_adc))) + matrix_lower = np.vstack((s2s_block, np.zeros((1, self.n_adc)), + phot_block)) - matrix = np.hstack((matrix_upper, matrix_middle.reshape((len(matrix_middle), 1)), matrix_lower)) + matrix = np.hstack((matrix_upper, matrix_middle.reshape((len(matrix_middle), 1)), + matrix_lower)) return sp.eigh(matrix) def second_order_coupling(self): + + # tdm part + qed_adc2_tdm_vec = np.empty(self.n_adc) for i, tdm in enumerate(self.tdm): - qed_adc2_tdm_vec[i] = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * tdm[2] + qed_adc2_tdm_vec[i] = - np.sqrt(self.freq/2) * self.coupl *\ + np.sqrt(2 * self.freq) * tdm[2] # s2s_dipole parts of the ph_ph blocks - qed_adc1_off_diag_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] + qed_adc1_off_diag_block = - np.sqrt(self.freq/2) * self.coupl *\ + np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] - qed_adc2_diag_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_diag"] - qed_adc2_diag_block = qed_adc2_diag_block * np.sqrt(self.freq/2) # missing factor from state.s2s_dipole_moments_qed_adc2_diag + qed_adc2_diag_block = - np.sqrt(self.freq/2) * self.coupl *\ + np.sqrt(2 * self.freq) * self.s2s["qed_adc2_diag"] + # missing factor from state.s2s_dipole_moments_qed_adc2_diag + # TODO: commit to one way of defining these factors within the approx method + qed_adc2_diag_block *= np.sqrt(self.freq/2) - qed_adc2_edge_block_couple = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_couple"] - qed_adc2_edge_block_couple = qed_adc2_edge_block_couple * np.sqrt(self.freq) # missing factor from state.s2s_dipole_moments_qed_adc2_edge + qed_adc2_edge_block_couple = - np.sqrt(self.freq/2) * self.coupl *\ + np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_couple"] + # missing factor from state.s2s_dipole_moments_qed_adc2_edge + qed_adc2_edge_block_couple *= np.sqrt(self.freq) - qed_adc2_edge_block_phot_couple = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_phot_couple"] - qed_adc2_edge_block_phot_couple = qed_adc2_edge_block_phot_couple * np.sqrt(self.freq) # missing factor from state.s2s_dipole_moments_qed_adc2_edge + qed_adc2_edge_block_phot_couple = - np.sqrt(self.freq/2) * self.coupl *\ + np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_phot_couple"] + # missing factor from state.s2s_dipole_moments_qed_adc2_edge + qed_adc2_edge_block_phot_couple *= np.sqrt(self.freq) # s2s_dipole parts of the pphh_ph and ph_pphh blocks - qed_adc2_ph_pphh_couple_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_ph_pphh"] + qed_adc2_ph_pphh_couple_block = - np.sqrt(self.freq/2) * self.coupl *\ + np.sqrt(2 * self.freq) * self.s2s["qed_adc2_ph_pphh"] - qed_adc2_pphh_ph_phot_couple_block = - np.sqrt(self.freq/2) * self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_pphh_ph"] + qed_adc2_pphh_ph_phot_couple_block = - np.sqrt(self.freq/2) * self.coupl *\ + np.sqrt(2 * self.freq) * self.s2s["qed_adc2_pphh_ph"] # we still need the H_1 expectation value "as property" @@ -96,38 +115,35 @@ def second_order_coupling(self): elec_block = np.diag(self.exc_en) + qed_adc2_diag_block - phot_block = np.diag(self.exc_en) + qed_adc2_diag_block * 2 + np.diag(single_excitation_states) * self.freq - - phot2_block = np.diag(self.exc_en) + qed_adc2_diag_block * 3 + np.diag(single_excitation_states) * 2 * self.freq + phot_block = np.diag(self.exc_en) + qed_adc2_diag_block * 2 +\ + np.diag(single_excitation_states) * self.freq - couple_block = qed_adc1_off_diag_block + qed_adc2_ph_pphh_couple_block + qed_adc2_couple_block + phot2_block = np.diag(self.exc_en) + qed_adc2_diag_block * 3 +\ + np.diag(single_excitation_states) * 2 * self.freq - phot_couple_block = qed_adc1_off_diag_block + qed_adc2_pphh_ph_phot_couple_block + qed_adc2_phot_couple_block + couple_block = qed_adc1_off_diag_block + qed_adc2_ph_pphh_couple_block +\ + qed_adc2_couple_block - #print("elec_block", elec_block.tolist()) - #print("phot_block", phot_block.tolist()) - #print("phot2_block", phot2_block.tolist()) - #print("couple_block", couple_block.tolist()) - #print("phot_couple_block", phot_couple_block.tolist()) + phot_couple_block = qed_adc1_off_diag_block + qed_adc2_pphh_ph_phot_couple_block +\ + qed_adc2_phot_couple_block # build the matrix matrix_1 = np.vstack((elec_block, qed_adc2_tdm_vec.reshape((1, self.n_adc)), - phot_couple_block, np.zeros((1, self.n_adc)), qed_adc2_edge_block_phot_couple)) + phot_couple_block, np.zeros((1, self.n_adc)), + qed_adc2_edge_block_phot_couple)) matrix_2 = np.concatenate((qed_adc2_tdm_vec, np.array([self.freq]), - np.zeros(self.n_adc), np.array([0]), np.zeros(self.n_adc))) + np.zeros(self.n_adc), np.array([0]), np.zeros(self.n_adc))) matrix_3 = np.vstack((couple_block, np.zeros((1, self.n_adc)), phot_block, - np.sqrt(2) * qed_adc2_tdm_vec.reshape((1, self.n_adc)), np.sqrt(2) * phot_couple_block)) - matrix_4 = np.concatenate((np.zeros(self.n_adc), np.array([0]), np.sqrt(2) * qed_adc2_tdm_vec, - 2 * np.array([self.freq]), np.zeros(self.n_adc))) + np.sqrt(2) * qed_adc2_tdm_vec.reshape((1, self.n_adc)), + np.sqrt(2) * phot_couple_block)) + matrix_4 = np.concatenate((np.zeros(self.n_adc), np.array([0]), + np.sqrt(2) * qed_adc2_tdm_vec, + 2 * np.array([self.freq]), np.zeros(self.n_adc))) matrix_5 = np.vstack((qed_adc2_edge_block_couple, np.zeros((1, self.n_adc)), - np.sqrt(2) * couple_block, np.zeros((1, self.n_adc)), phot2_block)) + np.sqrt(2) * couple_block, np.zeros((1, self.n_adc)), phot2_block)) matrix = np.hstack((matrix_1, matrix_2.reshape((len(matrix_2), 1)), matrix_3, matrix_4.reshape((len(matrix_4), 1)), matrix_5)) - eigvals, eigvecs = sp.eigh(matrix) - - #print("eigvals", eigvals) - - return eigvals, eigvecs \ No newline at end of file + return sp.eigh(matrix) \ No newline at end of file diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 55443212..71821ea1 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -385,9 +385,6 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, # max_subspace = max(2 * n_ep + 1, 20) if hasattr(matrix.reference_state, "full_diagonalization"): max_subspace = len(guesses) - #elif hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): - # print("for qed-adc we use double the standard max_subspace") - # max_subspace = max(12 * n_ep, 20, 10 * len(guesses)) else: max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) diff --git a/adcc/test_qed.py b/adcc/test_qed.py index a475a582..3dc37aee 100644 --- a/adcc/test_qed.py +++ b/adcc/test_qed.py @@ -39,7 +39,8 @@ # which keeps the matrix dimension low. This is important since we # build most of the matrix in the approx method from properties, so this # is very slow for a lot of states, compared to the standard method. -# However, even this test case would take quite some time... +# However, even this test case would take quite some time, so we leave +# it out for now. testcases = ["methox_sto3g", "h2o_sto3g"] methods = ["adc2"] diff --git a/adcc/workflow.py b/adcc/workflow.py index 2a70036b..a889a954 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -244,7 +244,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, exstates += env_energy_corrections # Build QED (approximated) matrix from "standard" ADC matrix - # and expectation values. + # and expectation values, if requested. if hasattr(matrix.reference_state, "approx"): qed_matrix = qed_matrix_from_diag_adc(exstates, matrix.reference_state) @@ -252,7 +252,12 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, qed_eigvals, qed_eigvecs = qed_matrix.second_order_coupling() else: qed_eigvals, qed_eigvecs = qed_matrix.first_order_coupling() - + # TODO: This is a bad solution, but filtering out the final excitation + # vectors and properly feeding them back into the corresponding libtensor is + # usually just unnecessary, especially since consistent qed properties + # are not implemented yet. This way the user is at least provided with + # the ADC result without polaritonic coupling between the states and can + # then request the energies and vectors from the exstates object. exstates.qed_excitation_energy = qed_eigvals exstates.qed_excitation_vector = qed_eigvecs @@ -294,7 +299,9 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, frozen_virtual=frozen_virtual) except ValueError as e: raise InputError(str(e)) # In case of an issue with the spaces - # for now qchem keywords are requested as refstate attributes + # TODO: for now qed keywords are requested as refstate attributes, which + # should be forwarded to the adcc_ReferenceState via the above call, and + # maybe stored in a dict as e.g. refstate.qed_keys["approx"] = True if coupl != None: refstate.coupling = coupl refstate.frequency = freq @@ -462,7 +469,7 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", if guesses is None: if n_guesses is None: n_guesses = estimate_n_guesses(matrix, n_states, n_guesses_per_state) - if hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): + if hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): # noqa: E501 guesses = obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles) else: @@ -522,10 +529,10 @@ def estimate_n_guesses(matrix, n_states, singles_only=True, return max(n_states, n_guesses) -def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None, qed_subblock=None): +def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None, qed_subblock=None): # noqa: E501 """ Obtain guesses by inspecting the diagonal matrix elements. - If n_guesses_doubles is not None, this is number is always adhered to. + If n_guesses_doubles is not None, this number is always adhered to. Otherwise the number of doubles guesses is adjusted to fill up whatever the singles guesses cannot provide to reach n_guesses. Internal function called from run_adc. @@ -567,33 +574,46 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles=None): - guesses_elec = obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="elec") - guesses_phot = obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot") - guesses_phot2 = obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot2") + """ + Obtain guesses for QED_AmplitudeVectors, by pushing the subblocks + into obtain_guesses_by_inspection. + Internal function called from run_adc. + """ + guesses_elec = obtain_guesses_by_inspection(matrix, n_guesses, kind, + n_guesses_doubles, qed_subblock="elec") + guesses_phot = obtain_guesses_by_inspection(matrix, n_guesses, kind, + n_guesses_doubles, qed_subblock="phot") + guesses_phot2 = obtain_guesses_by_inspection(matrix, n_guesses, kind, + n_guesses_doubles, qed_subblock="phot2") n_guess = len(guesses_elec) - # Usually only few states are requested and most of them are close to pure electronic states, - # so we initialize the guess vectors as almost purely electric guesses. + # Usually only few states are requested and most of them are close + # to pure electronic states, so we initialize the guess vectors + # as almost purely electric guesses. if not hasattr(matrix.reference_state, "full_diagonalization"): for i in np.arange(n_guess): - # TODO: maybe these values should be accessible from the input file + # TODO: maybe make these values accessible by a keyword, since + # they can tune the performance. From my experience these work + # very well though guesses_phot[i] *= 0.02 guesses_phot2[i] *= 0.001 if n_guess != len(guesses_phot): - raise InputError("amount of guesses for electronic and photonic must be equal, but are" - "{} electronic and {} photonic guesses".format(len(guesses_elec), len(guesses_phot))) + raise InputError("amount of guesses for electronic and photonic must be " + "equal, but are {} electronic and {} photonic " + "guesses".format(len(guesses_elec), len(guesses_phot))) guesses_tmp = [] for guess_index in np.arange(n_guess): if "pphh" in matrix.axis_blocks: - guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, - 0, guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, - 0, guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) + guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, + guesses_elec[guess_index].pphh, + 0, guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, + 0, guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) else: guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, None, - 0, guesses_phot[guess_index].ph, None, - 0, guesses_phot2[guess_index].ph, None)) + 0, guesses_phot[guess_index].ph, None, + 0, guesses_phot2[guess_index].ph, None)) if hasattr(matrix.reference_state, "full_diagonalization"): @@ -605,18 +625,24 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= tmp_phot2_vec = qed_vec.phot2.copy() * 10 tmp_zero_vec = qed_vec.elec.zeros_like() - # TODO: Initialize via properly working zero vector + if "pphh" in matrix.axis_blocks: - full_guess.append(QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) - full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, tmp_zero_vec.pphh, 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) - full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, tmp_zero_vec.pphh, 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh)) + full_guess.append(QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) + full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) + full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh)) else: - full_guess.append(QED_AmplitudeVector(tmp_elec_vec.ph, None, 0, tmp_zero_vec.ph, None, 0, tmp_zero_vec.ph, None)) - full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, None, 0, tmp_phot_vec.ph, None, 0, tmp_zero_vec.ph, None)) - full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, None, 0, tmp_zero_vec.ph, None, 0, tmp_phot2_vec.ph, None)) + full_guess.append(QED_AmplitudeVector(tmp_elec_vec.ph, None, + 0, tmp_zero_vec.ph, None, 0, tmp_zero_vec.ph, None)) + full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, None, + 0, tmp_phot_vec.ph, None, 0, tmp_zero_vec.ph, None)) + full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, None, + 0, tmp_zero_vec.ph, None, 0, tmp_phot2_vec.ph, None)) full_guess.append(guesses_tmp[0].zeros_like()) full_guess.append(guesses_tmp[0].zeros_like()) @@ -625,13 +651,14 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= else: final_guesses = guesses_tmp - # These guesses need a manual option, because if the gs1 state is close to a state it couples with, the gs1 guess needs to be smaller, than for a - # decoupled state. For gs2 this is a little less important, because the coupling is weaker. - # Further notice, that the last two guesses are used for the photonic dispersion states, - # so e.g. the 3rd guess doesn't need to converge into the 1st state - # TODO: maybe manipulate these entries via input - final_guesses[len(final_guesses) - 2].gs1 += 5#2 - final_guesses[len(final_guesses) - 1].gs2 += 20#5 + # TODO: maybe make these values accessible by a keyword, since + # they can tune the performance. From my experience these work + # very well though, but depending on how strong a state couples + # to the single photon dispersion mode and how many states one + # requests, adjusting these values can significantly increase the + # convergence rate + final_guesses[len(final_guesses) - 2].gs1 += 5 # for stronger coupling e.g. 2 + final_guesses[len(final_guesses) - 1].gs2 += 20 # for stronger coupling e.g. 5 return [vec / np.sqrt(vec @ vec) for vec in final_guesses] From 671183b6902dde3b279e07204e38599c3ba9e01e Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 9 Aug 2022 13:56:26 +0200 Subject: [PATCH 52/64] removed mp2_diffdm for qed, since it has not been tested thoroughly yet --- adcc/LazyMp.py | 50 ++------------------------------------------------ 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index f8328b3d..7622acef 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -141,23 +141,6 @@ def mp2_diffdm(self): ) / self.df(b.ov) ret.vv = 0.5 * einsum("ijac,ijbc->ab", self.t2oo, self.t2oo) - if hasattr(hf, "coupling"): - # qed correction - print("mp2 diffdm has been adapted to qed") - omega = ReferenceState.get_qed_omega(hf) - - ret.oo -= 0.5 * einsum("ia,ja->ij", self.qed_t1(b.ov), - self.qed_t1(b.ov)) * omega - - ret.ov += 0.5 * (einsum("ib,ab->ia", self.qed_t1(b.ov), - hf.get_qed_total_dip(b.vv)) - - einsum("ji,ja->ia", hf.get_qed_total_dip(b.oo), - self.qed_t1(b.ov)) * omega - ) / self.df(b.ov) - - ret.vv += 0.5 * einsum("ia,ib->ab", self.qed_t1(b.ov), - self.qed_t1(b.ov)) * omega - if self.has_core_occupied_space: # additional terms to "revert" CVS for ground state density ret.oo += -0.5 * einsum("iLab,jLab->ij", self.t2oc, self.t2oc) @@ -192,23 +175,6 @@ def mp2_diffdm(self): ret.reference_state = self.reference_state return evaluate(ret) - @cached_property - def mp1_diffdm_qed(self): - """ - This does not really exist, but since the dipole operator also contains - the factor (b^{dagger} + b), there exists a term, which is required - in the evaluation for the corresponding dipole properties, which are - themselves needed, to perform the qed-adc(2) test - """ - ret = OneParticleOperator(self.mospaces, is_symmetric=True) - hf = self.reference_state - omega = ReferenceState.get_qed_omega(hf) - - ret.ov = self.qed_t1(b.ov) #* omega/2 #this is left out, since it is - #also left out for the s2s properties and reintroduced in the testing script - - ret.reference_state = self.reference_state - return evaluate(ret) def density(self, level=2): """ @@ -217,7 +183,7 @@ def density(self, level=2): """ if level == 1: if hasattr(self.reference_state, "coupling"): - return self.reference_state.density# + self.mp1_diffdm_qed + return self.reference_state.density else: return self.reference_state.density elif level == 2: @@ -256,7 +222,7 @@ def qed_t1(self, space): @cached_member_function def qed_t0_df(self, space): """ - qed_t1 amplitude times df + qed_t0 amplitude times df """ total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) total_dip.oo = self.get_qed_total_dip.oo @@ -317,8 +283,6 @@ def energy_correction(self, level=2): if level == 2 and not is_cvs: terms = [(1.0, hf.oovv, self.t2oo)] if hasattr(hf, "coupling"): - # print("mp2 energy with two electron qed perturbation " + - # str(mp2_correction)) total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) omega = ReferenceState.get_qed_omega(hf) total_dip.ov = self.get_qed_total_dip.ov @@ -329,8 +293,6 @@ def energy_correction(self, level=2): ) if hasattr(hf, "qed_hf"): qed_mp2_correction = qed_mp2_correction_1 - # print("QED-MP(2) energy correction for QED-HF = " + - # str(mp2_correction + qed_mp2_correction_1)) else: qed_terms_0 = [(1.0, self.qed_t0(b.ov), self.qed_t0_df(b.ov))] qed_mp2_correction_0 = sum( @@ -343,16 +305,8 @@ def energy_correction(self, level=2): pref * lambda_dip.dot(lambda_dip) for pref, lambda_dip in qed_mp1_additional_terms ) - # print("QED-MP(2) energy correction for standard HF - # (includes QED-MP(1) too) = " - # + str(mp2_correction + qed_mp2_correction_1 + qed_mp2_correction_0 - # + qed_mp1_correction)) qed_mp2_correction = qed_mp2_correction_1 + qed_mp2_correction_0 +\ qed_mp1_correction + qed_mp1_correction - # print("qed-mp1 correction, due to standard hf input " - # + str(qed_mp1_correction)) - # print("new qed-mp2 correction compared to qed-hf " - # + str(qed_mp2_correction_0)) elif level == 2 and is_cvs: terms = [(1.0, hf.oovv, self.t2oo), (2.0, hf.ocvv, self.t2oc), From ec2f2dd982a078c360ba5bf3475ddebf129490d2 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 9 Aug 2022 18:14:51 +0200 Subject: [PATCH 53/64] lgtm corrections --- adcc/AdcMatrix.py | 6 ++++-- adcc/guess/guesses_from_diagonal.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 8527045a..442a6536 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -136,7 +136,8 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, if hasattr(self.reference_state, "coupling"): if method.base_method.name == "adc2x" or method.base_method.name == "adc3": - NotImplementedError("Neither adc2x nor adc3 are implemented for QED-ADC") + raise NotImplementedError("Neither adc2x nor adc3 " + "are implemented for QED-ADC") if hasattr(self.reference_state, "first_order_coupling") and method.base_method.name == "adc2": # noqa: E501 # this way we only need to include the separate case in the ph_ph=1 blocks, @@ -463,7 +464,8 @@ def mv(qed_disp_key): return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) else: - TypeError("matvec needs to be invoked with AmplitudeVector or QED_AmplitudeVector") + raise TypeError("matvec needs to be invoked with " + "AmplitudeVector or QED_AmplitudeVector") def rmatvec(self, v): # ADC matrix is symmetric diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index 876018f9..40612133 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -91,7 +91,7 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, else: raise ValueError(f"Don't know how to generate guesses for block {block}") - if qed_subblock == None: + if qed_subblock is None: diag = matrix.diagonal() elif qed_subblock == "elec": diag = matrix.diagonal().elec @@ -100,7 +100,7 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, elif qed_subblock == "phot2": diag = matrix.diagonal().phot2 else: - KeyError("qed_subblock can only be None, elec, phot or phot2" + raise KeyError("qed_subblock can only be None, elec, phot or phot2" "for guesses from diagonal") return guessfunction(matrix, n_guesses, diag, spin_change, From da9555ab366c5c24b067f7b6938d9b04fb46b6ee Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Wed, 10 Aug 2022 18:41:38 +0200 Subject: [PATCH 54/64] making the linter happy --- adcc/AdcMatrix.py | 125 ++++---- adcc/AmplitudeVector.py | 142 +++++---- adcc/ElectronicTransition.py | 59 ++-- adcc/ExcitedStates.py | 4 +- adcc/LazyMp.py | 22 +- adcc/ReferenceState.py | 33 +- adcc/adc_pp/matrix.py | 378 +++++++++++++---------- adcc/adc_pp/state2state_transition_dm.py | 42 +-- adcc/backends/__init__.py | 2 +- adcc/backends/psi4.py | 7 +- adcc/functions.py | 5 +- adcc/guess/guesses_from_diagonal.py | 3 +- adcc/qed_matrix_from_diag_adc.py | 70 +++-- adcc/solver/davidson.py | 5 +- adcc/solver/explicit_symmetrisation.py | 4 +- adcc/solver/lanczos.py | 1 - adcc/solver/preconditioner.py | 2 +- adcc/test_qed.py | 8 +- adcc/workflow.py | 122 ++++---- 19 files changed, 564 insertions(+), 470 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 442a6536..7d384f87 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -135,16 +135,15 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, self.extra_terms = [] if hasattr(self.reference_state, "coupling"): - if method.base_method.name == "adc2x" or method.base_method.name == "adc3": + if method.base_method.name == "adc2x" or method.base_method.name == "adc3": # noqa: E501 raise NotImplementedError("Neither adc2x nor adc3 " "are implemented for QED-ADC") - if hasattr(self.reference_state, "first_order_coupling") and method.base_method.name == "adc2": # noqa: E501 - # this way we only need to include the separate case in the ph_ph=1 blocks, - # and the 4 non-zero coupling blocks - self.qed_default_block_orders["adc2"] = dict(ph_gs=1, ph_ph=1, - ph_pphh=1, pphh_ph=1, - pphh_pphh=0) + if hasattr(self.reference_state, "first_order_coupling") and method.base_method.name == "adc2": # noqa: E501 + # this way we only need to include the separate case + # in the ph_ph=1 blocks, and the 4 non-zero coupling blocks + self.qed_default_block_orders["adc2"] = dict( + ph_gs=1, ph_ph=1, ph_pphh=1, pphh_ph=1, pphh_pphh=0) self.intermediates = intermediates if self.intermediates is None: @@ -152,8 +151,8 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, # Determine orders of PT in the blocks if block_orders is None: - if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 - block_orders = self.qed_default_block_orders[method.base_method.name] + if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 + block_orders = self.qed_default_block_orders[method.base_method.name] # noqa: E501 else: block_orders = self.default_block_orders[method.base_method.name] else: @@ -174,10 +173,12 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, self.block_orders = block_orders self.qed_dispatch_dict = { - "elec": "", + "elec": "", "elec_couple": "_couple", "phot_couple": "_phot_couple", - "phot": "_phot", "phot2": "_phot2", "elec_couple_inner": "_couple_inner", - "elec_couple_edge": "_couple_edge", "phot_couple_inner": "_phot_couple_inner", + "phot": "_phot", "phot2": "_phot2", + "elec_couple_inner": "_couple_inner", + "elec_couple_edge": "_couple_edge", + "phot_couple_inner": "_phot_couple_inner", "phot_couple_edge": "_phot_couple_edge" } @@ -190,7 +191,8 @@ def get_pp_blocks(disp_str): """ return { # TODO Rename to self.block in 0.16.0 block: ppmatrix.block(self.ground_state, block.split("_"), - order=str(order) + self.qed_dispatch_dict[disp_str], + order=str(order) +\ + self.qed_dispatch_dict[disp_str], intermediates=self.intermediates, variant=variant) for block, order in block_orders.items() if order is not None @@ -203,7 +205,7 @@ def get_pp_blocks(disp_str): if method.is_core_valence_separated: variant = "cvs" # Build full QED-matrix - if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 + if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 blocks = {} if hasattr(hf_or_mp.reference_state, "coupling"): @@ -212,27 +214,35 @@ def get_pp_blocks(disp_str): if order is not None: blocks[bl + "_" + key] = get_pp_blocks(key)[bl] - self.__diagonal_gs1 = sum(blocks[block].diagonal for block in blocks - if "ph_gs" in block and block.endswith("phot")) + if "ph_gs" in block + and block.endswith("phot")) self.__diagonal_gs2 = sum(blocks[block].diagonal for block in blocks - if "ph_gs" in block and block.endswith("phot2")) - + if "ph_gs" in block + and block.endswith("phot2")) + if "pphh_pphh_elec" in blocks: - self.__diagonal = QED_AmplitudeVector(blocks["ph_ph_elec"].diagonal.evaluate().ph, - blocks["pphh_pphh_elec"].diagonal.evaluate().pphh, - self.__diagonal_gs1, blocks["ph_ph_phot"].diagonal.evaluate().ph, - blocks["pphh_pphh_phot"].diagonal.evaluate().pphh, - self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, - blocks["pphh_pphh_phot2"].diagonal.evaluate().pphh) + self.__diagonal = QED_AmplitudeVector( + blocks["ph_ph_elec"].diagonal.evaluate().ph, + blocks["pphh_pphh_elec"].diagonal.evaluate().pphh, + self.__diagonal_gs1, + blocks["ph_ph_phot"].diagonal.evaluate().ph, + blocks["pphh_pphh_phot"].diagonal.evaluate().pphh, + self.__diagonal_gs2, + blocks["ph_ph_phot2"].diagonal.evaluate().ph, + blocks["pphh_pphh_phot2"].diagonal.evaluate().pphh) else: - self.__diagonal = QED_AmplitudeVector(blocks["ph_ph_elec"].diagonal.evaluate().ph, None, # noqa: E501 - self.__diagonal_gs1, blocks["ph_ph_phot"].diagonal.evaluate().ph, None, # noqa: E501 - self.__diagonal_gs2, blocks["ph_ph_phot2"].diagonal.evaluate().ph, None) # noqa: E501 - self.__init_space_data(self.__diagonal.elec) - else: # Build "standard" ADC-matrix + self.__diagonal = QED_AmplitudeVector( + blocks["ph_ph_elec"].diagonal.evaluate().ph, None, + self.__diagonal_gs1, + blocks["ph_ph_phot"].diagonal.evaluate().ph, None, + self.__diagonal_gs2, + blocks["ph_ph_phot2"].diagonal.evaluate().ph, None) + self.__init_space_data(self.__diagonal.elec) + else: # Build "standard" ADC-matrix blocks = { - bl: get_pp_blocks("elec")[bl] for bl, order in block_orders.items() + bl: get_pp_blocks("elec")[bl] + for bl, order in block_orders.items() if order is not None } @@ -243,13 +253,13 @@ def get_pp_blocks(disp_str): if bl.diagonal) self.__diagonal.evaluate() self.__init_space_data(self.__diagonal) - + # TODO Rename to self.block in 0.16.0 self.blocks_ph = {bl: blocks[bl].apply for bl in blocks} - def qed_subblock(self, qed_disp_key): - # These subblocks are "standard ADC matrices", so we can use the implemented functions. + # These subblocks are "standard ADC matrices", + # so we can use the implemented functions. # This is useful e.g. in the matvec function """ Extraction of subblocks from the full QED-ADC Matrix, @@ -258,9 +268,8 @@ def qed_subblock(self, qed_disp_key): qed_disp_key : string key specifying which block to return """ - return [bl for key, bl in self.blocks_ph.items() + return [bl for key, bl in self.blocks_ph.items() if not key.startswith("ph_gs") and key.endswith(qed_disp_key)] - def __iadd__(self, other): """In-place addition of an :py:class:`AdcExtraTerm` @@ -323,10 +332,11 @@ def __init_space_data(self, diagonal): ]) self.shape = (sum(self.axis_lengths.values()), sum(self.axis_lengths.values())) - if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 - # We leave out the the dimension with the purely electronic ground state, + if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 + # We leave out the the dimension with the + # purely electronic ground state, # since by construction the coupling to it is always zero - self.shape = ((self.shape[0]+1) * 3 - 1, (self.shape[0]+1) * 3 - 1) + self.shape = ((self.shape[0] + 1) * 3 - 1, (self.shape[0] + 1) * 3 - 1) def __repr__(self): ret = f"AdcMatrix({self.method.name}, " @@ -373,7 +383,7 @@ def diagonal(self, block=None): warnings.warn("Support for the block argument will be dropped " "in 0.16.0.") if block == "g": - return self.__diagonal.gs + return self.__diagonal.gs if block == "s": return self.__diagonal.ph if block == "d": @@ -420,14 +430,15 @@ def mv(qed_disp_key): phot_part = mv("elec_couple") + mv("phot") + mv("phot_couple_inner") - if "pphh_pphh_elec" in self.blocks_ph.keys() and not hasattr(self.reference_state, "first_order_coupling"): # noqa: E501 - phot_couple_edge_with_doubles = AmplitudeVector(ph=mv("phot_couple_edge"), - pphh=v.pphh.zeros_like()) - elec_couple_edge_with_doubles = AmplitudeVector(ph=mv("elec_couple_edge"), - pphh=v.pphh.zeros_like()) - elec_part = mv("elec") + mv("phot_couple") + phot_couple_edge_with_doubles - phot2_part = elec_couple_edge_with_doubles + \ - mv("elec_couple_inner") + mv("phot2") + if "pphh_pphh_elec" in self.blocks_ph.keys() and not hasattr(self.reference_state, "first_order_coupling"): # noqa: E501 + phot_couple_edge_with_doubles = AmplitudeVector( + ph=mv("phot_couple_edge"), pphh=v.pphh.zeros_like()) + elec_couple_edge_with_doubles = AmplitudeVector( + ph=mv("elec_couple_edge"), pphh=v.pphh.zeros_like()) + elec_part = mv("elec") + mv("phot_couple") +\ + phot_couple_edge_with_doubles + phot2_part = elec_couple_edge_with_doubles +\ + mv("elec_couple_inner") + mv("phot2") else: elec_part = mv("elec") + mv("phot_couple") phot2_part = mv("elec_couple_inner") + mv("phot2") @@ -453,16 +464,18 @@ def mv(qed_disp_key): gs1_part += self.blocks_ph[block](v) elif block.endswith("couple"): gs1_part += self.blocks_ph[block](v) - else: # elec + else: # elec continue - + if "pphh_pphh_elec" in self.blocks_ph.keys(): - return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, - gs1_part, phot_part.ph, phot_part.pphh, - gs2_part, phot2_part.ph, phot2_part.pphh) + return QED_AmplitudeVector( + elec_part.ph, elec_part.pphh, + gs1_part, phot_part.ph, phot_part.pphh, + gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, - gs2_part, phot2_part.ph, None) + return QED_AmplitudeVector( + elec_part.ph, None, gs1_part, phot_part.ph, None, + gs2_part, phot2_part.ph, None) else: raise TypeError("matvec needs to be invoked with " "AmplitudeVector or QED_AmplitudeVector") @@ -484,7 +497,7 @@ def __matmul__(self, other): if isinstance(other, (AmplitudeVector, QED_AmplitudeVector)): return self.matvec(other) if isinstance(other, list): - if all(isinstance(elem, (AmplitudeVector, QED_AmplitudeVector)) for elem in other): + if all(isinstance(elem, (AmplitudeVector, QED_AmplitudeVector)) for elem in other): # noqa: E501 return [self.matvec(ov) for ov in other] return NotImplemented @@ -849,4 +862,4 @@ def block_view(self, block): raise NotImplementedError("Block-view not yet implemented for " "projected ADC matrices.") # TODO The way to implement this is to ask the inner matrix to - # a block_view and then wrap that in an AdcMatrixProjected. \ No newline at end of file + # a block_view and then wrap that in an AdcMatrixProjected. diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index 3f6a0c85..24003399 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -224,16 +224,16 @@ def __radd__(self, other): class QED_AmplitudeVector: # TODO: Initialize this class with **kwargs, and think of a functionality, - # which then eases up e.g. the matvec function in the AdcMatrix.py for - # arbitrarily large QED vectors. However, this is not necessarily required, + # which then eases up e.g. the matvec function in the AdcMatrix.py for + # arbitrarily large QED vectors. However, this is not necessarily required, # since e.g. QED-ADC(3) is very complicated to derive and QED-ADC(1) (with # just the single dispersion mode) is purely for academic purposes and # hence not required to provide optimum performance. def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, - gs2=None, ph2=None, pphh2=None): + gs2=None, ph2=None, pphh2=None): - if pphh != None: + if pphh is not None: self.elec = AmplitudeVector(ph=ph, pphh=pphh) self.phot = AmplitudeVector(ph=ph1, pphh=pphh1) self.phot2 = AmplitudeVector(ph=ph2, pphh=pphh2) @@ -251,23 +251,23 @@ def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, self.pphh1 = pphh1 self.pphh2 = pphh2 - def dot(self, invec): def dot_(self, invec): if "pphh" in self.elec.blocks_ph: - return (self.elec.ph.dot(invec.elec.ph) + self.elec.pphh.dot(invec.elec.pphh) - + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + return (self.elec.ph.dot(invec.elec.ph) + + self.elec.pphh.dot(invec.elec.pphh) + + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) + self.phot.pphh.dot(invec.phot.pphh) - + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) + + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) + self.phot2.pphh.dot(invec.phot2.pphh)) else: - return (self.elec.ph.dot(invec.elec.ph) + return (self.elec.ph.dot(invec.elec.ph) + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) - + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) ) + + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph)) if isinstance(invec, list): return np.array([dot_(self, elem) for elem in invec]) else: - return dot_(self, invec) + return dot_(self, invec) def __matmul__(self, other): if isinstance(other, QED_AmplitudeVector): @@ -276,83 +276,103 @@ def __matmul__(self, other): if all(isinstance(elem, QED_AmplitudeVector) for elem in other): return self.dot(other) return NotImplemented - def __sub__(self, invec): if isinstance(invec, (float, int)): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), - pphh=self.elec.pphh.__sub__(invec), - gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), - pphh1=self.phot.pphh.__sub__(invec), - gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec), - pphh2=self.phot2.pphh.__sub__(invec)) + return QED_AmplitudeVector( + ph=self.elec.ph.__sub__(invec), + pphh=self.elec.pphh.__sub__(invec), + gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), + pphh1=self.phot.pphh.__sub__(invec), + gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec), + pphh2=self.phot2.pphh.__sub__(invec)) else: - return QED_AmplitudeVector(ph=self.elec.ph.__sub__(invec), - gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), - gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec)) - + return QED_AmplitudeVector( + ph=self.elec.ph.__sub__(invec), + gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), + gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec)) def __truediv__(self, other): if isinstance(other, QED_AmplitudeVector): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other.elec.ph), - pphh=self.elec.pphh.__truediv__(other.elec.pphh), - gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), - pphh1=self.phot.pphh.__truediv__(other.phot.pphh), - gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph), - pphh2=self.phot2.pphh.__truediv__(other.phot2.pphh)) + return QED_AmplitudeVector( + ph=self.elec.ph.__truediv__(other.elec.ph), + pphh=self.elec.pphh.__truediv__(other.elec.pphh), + gs1=self.gs1 / other.gs1, + ph1=self.phot.ph.__truediv__(other.phot.ph), + pphh1=self.phot.pphh.__truediv__(other.phot.pphh), + gs2=self.gs2 / other.gs2, + ph2=self.phot2.ph.__truediv__(other.phot2.ph), + pphh2=self.phot2.pphh.__truediv__(other.phot2.pphh)) else: - return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other.elec.ph), - gs1=self.gs1 / other.gs1, ph1=self.phot.ph.__truediv__(other.phot.ph), - gs2=self.gs2 / other.gs2, ph2=self.phot2.ph.__truediv__(other.phot2.ph)) + return QED_AmplitudeVector( + ph=self.elec.ph.__truediv__(other.elec.ph), + gs1=self.gs1 / other.gs1, + ph1=self.phot.ph.__truediv__(other.phot.ph), + gs2=self.gs2 / other.gs2, + ph2=self.phot2.ph.__truediv__(other.phot2.ph)) elif isinstance(other, (float, int)): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other), - pphh=self.elec.pphh.__truediv__(other), - gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), - pphh1=self.phot.pphh.__truediv__(other), - gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other), - pphh2=self.phot2.pphh.__truediv__(other)) + return QED_AmplitudeVector( + ph=self.elec.ph.__truediv__(other), + pphh=self.elec.pphh.__truediv__(other), + gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), + pphh1=self.phot.pphh.__truediv__(other), + gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other), + pphh2=self.phot2.pphh.__truediv__(other)) else: - return QED_AmplitudeVector(ph=self.elec.ph.__truediv__(other), - gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), - gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) + return QED_AmplitudeVector( + ph=self.elec.ph.__truediv__(other), + gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), + gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) def zeros_like(self): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.zeros_like().ph, - pphh=self.elec.zeros_like().pphh, - gs1=0, ph1=self.phot.zeros_like().ph, pphh1=self.phot.zeros_like().pphh, - gs2=0, ph2=self.phot2.zeros_like().ph, pphh2=self.phot2.zeros_like().pphh) + return QED_AmplitudeVector( + ph=self.elec.zeros_like().ph, + pphh=self.elec.zeros_like().pphh, + gs1=0, ph1=self.phot.zeros_like().ph, + pphh1=self.phot.zeros_like().pphh, + gs2=0, ph2=self.phot2.zeros_like().ph, + pphh2=self.phot2.zeros_like().pphh) else: - return QED_AmplitudeVector(ph=self.elec.zeros_like().ph, pphh=None, - gs1=0, ph1=self.phot.zeros_like().ph, pphh1=None, - gs2=0, ph2=self.phot2.zeros_like().ph, pphh2=None) + return QED_AmplitudeVector( + ph=self.elec.zeros_like().ph, pphh=None, + gs1=0, ph1=self.phot.zeros_like().ph, pphh1=None, + gs2=0, ph2=self.phot2.zeros_like().ph, pphh2=None) def empty_like(self): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.empty_like().ph, - pphh=self.elec.empty_like().pphh, - gs1=0, ph1=self.phot.empty_like().ph, pphh1=self.phot.empty_like().pphh, - gs2=0, ph2=self.phot2.empty_like().ph, pphh2=self.phot2.empty_like().pphh) + return QED_AmplitudeVector( + ph=self.elec.empty_like().ph, + pphh=self.elec.empty_like().pphh, + gs1=0, ph1=self.phot.empty_like().ph, + pphh1=self.phot.empty_like().pphh, + gs2=0, ph2=self.phot2.empty_like().ph, + pphh2=self.phot2.empty_like().pphh) else: - QED_AmplitudeVector(ph=self.elec.empty_like().ph, pphh=None, - gs1=0, ph1=self.phot.empty_like().ph, pphh1=None, - gs2=0, ph2=self.phot2.empty_like().ph, pphh2=None) + QED_AmplitudeVector( + ph=self.elec.empty_like().ph, pphh=None, + gs1=0, ph1=self.phot.empty_like().ph, pphh1=None, + gs2=0, ph2=self.phot2.empty_like().ph, pphh2=None) def copy(self): if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector(ph=self.elec.copy().ph, pphh=self.elec.copy().pphh, - gs1=self.gs1, ph1=self.phot.copy().ph, pphh1=self.phot.copy().pphh, - gs2=self.gs2, ph2=self.phot2.copy().ph, pphh2=self.phot2.copy().pphh) + return QED_AmplitudeVector( + ph=self.elec.copy().ph, pphh=self.elec.copy().pphh, + gs1=self.gs1, ph1=self.phot.copy().ph, + pphh1=self.phot.copy().pphh, + gs2=self.gs2, ph2=self.phot2.copy().ph, + pphh2=self.phot2.copy().pphh) else: - QED_AmplitudeVector(ph=self.elec.copy().ph, pphh=None, - gs1=self.gs1, ph1=self.phot.copy().ph, pphh1=None, - gs2=self.gs2, ph2=self.phot2.copy().ph, pphh2=None) + QED_AmplitudeVector( + ph=self.elec.copy().ph, pphh=None, + gs1=self.gs1, ph1=self.phot.copy().ph, pphh1=None, + gs2=self.gs2, ph2=self.phot2.copy().ph, pphh2=None) def evaluate(self): self.elec.evaluate() self.phot.evaluate() self.phot2.evaluate() - return self \ No newline at end of file + return self diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py index e88170c9..541c5b08 100644 --- a/adcc/ElectronicTransition.py +++ b/adcc/ElectronicTransition.py @@ -169,7 +169,6 @@ def transition_dipole_moment(self): for tdm in self.transition_dm ]) - @cached_property @mark_excitation_property() @timed_member_call(timer="_property_timer") @@ -182,21 +181,24 @@ def transition_dipole_moments_qed(self): if hasattr(self.reference_state, "approx"): dipole_integrals = self.operators.electric_dipole + def tdm(i, prop_level): self.ground_state.tdm_contribution = prop_level - return transition_dm(self.method, self.ground_state, + return transition_dm(self.method, self.ground_state, self.excitation_vector[i]) if hasattr(self.reference_state, "first_order_coupling"): return np.array([ - [product_trace(comp, tdm(i, "adc0")) for comp in dipole_integrals] + [product_trace(comp, tdm(i, "adc0")) + for comp in dipole_integrals] for i in np.arange(len(self.excitation_energy)) ]) else: prop_level = "adc" + str(self.property_method.level - 1) return np.array([ - [product_trace(comp, tdm(i, prop_level)) for comp in dipole_integrals] + [product_trace(comp, tdm(i, prop_level)) + for comp in dipole_integrals] for i in np.arange(len(self.excitation_energy)) ]) else: @@ -214,28 +216,29 @@ def s2s_dipole_moments_qed(self): """ if hasattr(self.reference_state, "approx"): dipole_integrals = self.operators.electric_dipole - print("note, that only the z coordinate of the dipole integrals is calculated") + print("note, that only the z coordinate of the " + "dipole integrals is calculated") n_states = len(self.excitation_energy) def s2s(i, f, s2s_contribution): self.ground_state.s2s_contribution = s2s_contribution vec = self.excitation_vector - return state2state_transition_dm(self.method, self.ground_state, + return state2state_transition_dm(self.method, self.ground_state, vec[i], vec[f]) def final_block(name): - return np.array([[product_trace(dipole_integrals[2], s2s(i, j, name)) - for j in np.arange(n_states)] for i in np.arange(n_states)]) + return np.array([[product_trace(dipole_integrals[2], s2s(i, j, name)) # noqa: E501 + for j in np.arange(n_states)] + for i in np.arange(n_states)]) block_dict = {} block_dict["qed_adc1_off_diag"] = final_block("adc1") - if self.method.name == "adc2" and not hasattr(self.reference_state, "first_order_coupling"): # noqa: E501 - - block_dict["qed_adc2_diag"] = final_block("qed_adc2_diag") - block_dict["qed_adc2_edge_couple"] = final_block("qed_adc2_edge_couple") - block_dict["qed_adc2_edge_phot_couple"] = final_block("qed_adc2_edge_phot_couple") # noqa: E501 + if self.method.name == "adc2" and not hasattr(self.reference_state, "first_order_coupling"): # noqa: E501 + block_dict["qed_adc2_diag"] = final_block("qed_adc2_diag") + block_dict["qed_adc2_edge_couple"] = final_block("qed_adc2_edge_couple") # noqa: E501 + block_dict["qed_adc2_edge_phot_couple"] = final_block("qed_adc2_edge_phot_couple") # noqa: E501 block_dict["qed_adc2_ph_pphh"] = final_block("qed_adc2_ph_pphh") block_dict["qed_adc2_pphh_ph"] = final_block("qed_adc2_pphh_ph") return block_dict @@ -255,31 +258,33 @@ def qed_second_order_ph_ph_couplings(self): """ if hasattr(self.reference_state, "approx"): qed_t1 = self.ground_state.qed_t1(b.ov) - + def couple(qed_t1, ul, ur): return { - b.ooov: einsum("kc,ia,ja->kjic", qed_t1, ul, ur) + \ - einsum("ka,ia,jb->jkib", qed_t1, ul, ur), - b.ovvv: einsum("kc,ia,ib->kacb", qed_t1, ul, ur) + \ - einsum("ic,ia,jb->jabc", qed_t1, ul, ur) + b.ooov: einsum("kc,ia,ja->kjic", qed_t1, ul, ur) + + einsum("ka,ia,jb->jkib", qed_t1, ul, ur), + b.ovvv: einsum("kc,ia,ib->kacb", qed_t1, ul, ur) + + einsum("ic,ia,jb->jabc", qed_t1, ul, ur) } def phot_couple(qed_t1, ul, ur): return { - b.ooov: einsum("kc,ia,ja->kijc", qed_t1, ul, ur) + \ - einsum("kb,ia,jb->ikja", qed_t1, ul, ur), - b.ovvv: einsum("kc,ia,ib->kbca", qed_t1, ul, ur) + \ - einsum("jc,ia,jb->ibac", qed_t1, ul, ur) + b.ooov: einsum("kc,ia,ja->kijc", qed_t1, ul, ur) + + einsum("kb,ia,jb->ikja", qed_t1, ul, ur), + b.ovvv: einsum("kc,ia,ib->kbca", qed_t1, ul, ur) + + einsum("jc,ia,jb->ibac", qed_t1, ul, ur) } def prod_sum(hf, two_p_op): - return + (einsum("ijka,ijka->", hf.ooov, two_p_op[b.ooov]) - + einsum("iabc,iabc->", hf.ovvv, two_p_op[b.ovvv])) + return (einsum("ijka,ijka->", hf.ooov, two_p_op[b.ooov]) + + einsum("iabc,iabc->", hf.ovvv, two_p_op[b.ovvv])) def final_block(func): - return np.array([[prod_sum(self.reference_state, func(qed_t1, i.ph, j.ph)) - for i in self.excitation_vector] for j in self.excitation_vector]) - + return np.array([ + [prod_sum(self.reference_state, func(qed_t1, i.ph, j.ph)) + for i in self.excitation_vector] + for j in self.excitation_vector]) + block_dict = {} block_dict["couple"] = final_block(couple) block_dict["phot_couple"] = final_block(phot_couple) diff --git a/adcc/ExcitedStates.py b/adcc/ExcitedStates.py index aa933b4c..f796e87f 100644 --- a/adcc/ExcitedStates.py +++ b/adcc/ExcitedStates.py @@ -411,7 +411,7 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): to index relative on the HOMO / LUMO / HOCO orbitals. If ``None`` an automatic selection will be made. """ - eV = constants.value("Hartree energy in eV") + eV = constants.value("Hartree energy in eV") vector_format = FormatExcitationVector(self.matrix, tolerance=tolerance, index_format=index_format) @@ -434,7 +434,7 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): if isinstance(vec, QED_AmplitudeVector): # TODO: Implement tdm and s2s_tdm for QED, so properties can also # be evaluated for QED_AmplitudeVector objects. For now only - # use AmplitudeVector describing the electric part, since + # use AmplitudeVector describing the electric part, since # most low-energy states are almost purely electric, so the # formatting does not have to be adapted. vec = vec.elec diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index 7622acef..f3cbcb21 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -48,7 +48,7 @@ def __init__(self, hf): self.timer = Timer() self.has_core_occupied_space = hf.has_core_occupied_space # for qed mp - self.get_qed_total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) + self.get_qed_total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) # noqa: E501 self.get_qed_total_dip.oo = hf.get_qed_total_dip(b.oo) self.get_qed_total_dip.ov = hf.get_qed_total_dip(b.ov) self.get_qed_total_dip.vv = hf.get_qed_total_dip(b.vv) @@ -175,7 +175,6 @@ def mp2_diffdm(self): ret.reference_state = self.reference_state return evaluate(ret) - def density(self, level=2): """ Return the MP density in the MO basis with all corrections @@ -210,8 +209,8 @@ def qed_t1_df(self, space): @cached_member_function def qed_t1(self, space): - """ - Return new electronic singly excited amplitude in the first + """ + Return new electronic singly excited amplitude in the first order correction to the wavefunction for qed """ if space != b.ov: @@ -239,11 +238,10 @@ def qed_t0_df(self, space): virt_sum = einsum("ac,bc->ab", total_dip.vv, total_dip.vv) return occ_sum - virt_sum - @cached_member_function def qed_t0(self, space): - """ - Return second new electronic singly excited amplitude in the first + """ + Return second new electronic singly excited amplitude in the first order correction to the wavefunction for qed from the standard HF reference """ @@ -252,7 +250,6 @@ def qed_t0(self, space): f"for space {space}.") return self.qed_t0_df(b.ov) / self.df(b.ov) - def dipole_moment(self, level=2): """ Return the MP dipole moment at the specified level of @@ -266,7 +263,6 @@ def dipole_moment(self, level=2): raise NotImplementedError("Only dipole moments for level 1 and 2" " are implemented.") - @cached_member_function def energy_correction(self, level=2): """Obtain the MP energy correction at a particular level""" @@ -286,7 +282,7 @@ def energy_correction(self, level=2): total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) omega = ReferenceState.get_qed_omega(hf) total_dip.ov = self.get_qed_total_dip.ov - qed_terms = [(omega/2, total_dip.ov, self.qed_t1(b.ov))] + qed_terms = [(omega / 2, total_dip.ov, self.qed_t1(b.ov))] qed_mp2_correction_1 = sum( -pref * lambda_dip.dot(qed_t) for pref, lambda_dip, qed_t in qed_terms @@ -305,8 +301,10 @@ def energy_correction(self, level=2): pref * lambda_dip.dot(lambda_dip) for pref, lambda_dip in qed_mp1_additional_terms ) - qed_mp2_correction = qed_mp2_correction_1 + qed_mp2_correction_0 +\ - qed_mp1_correction + qed_mp1_correction + qed_mp2_correction = qed_mp2_correction_1 +\ + qed_mp2_correction_0 +\ + qed_mp1_correction +\ + qed_mp1_correction elif level == 2 and is_cvs: terms = [(1.0, hf.oovv, self.t2oo), (2.0, hf.ocvv, self.t2oc), diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index cb13ebc0..7cbed3cd 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -186,7 +186,6 @@ def get_qed_total_dip(self, block): # hilbert package is currently the best in terms of performance, at # least to my knowledge, the factor should be included here. if hasattr(self, "coupling"): - from . import block as b dips = self.operators.electric_dipole couplings = self.coupling freqs = self.frequency @@ -217,23 +216,24 @@ def qed_D_object(self, block): total_dip.oo = ReferenceState.get_qed_total_dip(self, b.oo) total_dip.ov = ReferenceState.get_qed_total_dip(self, b.ov) total_dip.vv = ReferenceState.get_qed_total_dip(self, b.vv) - # We have to define all the blocks from the + # We have to define all the blocks from the # D_{pqrs} = d_{pr} d_{qs} - d_{ps} d_{qr} object, which has the # same symmetry properties as the ERI object - # Actually in b.ovov: second term: ib,ja would be ib,aj , but d_{ia} = d_{ai} + # Actually in b.ovov: second term: ib,ja would be ib,aj , + # but d_{ia} = d_{ai} ds = { - b.oooo: einsum('ik,jl->ijkl', total_dip.oo, total_dip.oo) - \ - einsum('il,jk->ijkl', total_dip.oo, total_dip.oo), - b.ooov: einsum('ik,ja->ijka', total_dip.oo, total_dip.ov) - \ - einsum('ia,jk->ijka', total_dip.ov, total_dip.oo), - b.oovv: einsum('ia,jb->ijab', total_dip.ov, total_dip.ov) - \ - einsum('ib,ja->ijab', total_dip.ov, total_dip.ov), - b.ovvv: einsum('ib,ac->iabc', total_dip.ov, total_dip.vv) - \ - einsum('ic,ab->iabc', total_dip.ov, total_dip.vv), - b.ovov: einsum('ij,ab->iajb', total_dip.oo, total_dip.vv) - \ - einsum('ib,ja->iajb', total_dip.ov, total_dip.ov), - b.vvvv: einsum('ac,bd->abcd', total_dip.vv, total_dip.vv) - \ - einsum('ad,bc->abcd', total_dip.vv, total_dip.vv), + b.oooo: einsum('ik,jl->ijkl', total_dip.oo, total_dip.oo) + - einsum('il,jk->ijkl', total_dip.oo, total_dip.oo), + b.ooov: einsum('ik,ja->ijka', total_dip.oo, total_dip.ov) + - einsum('ia,jk->ijka', total_dip.ov, total_dip.oo), + b.oovv: einsum('ia,jb->ijab', total_dip.ov, total_dip.ov) + - einsum('ib,ja->ijab', total_dip.ov, total_dip.ov), + b.ovvv: einsum('ib,ac->iabc', total_dip.ov, total_dip.vv) + - einsum('ic,ab->iabc', total_dip.ov, total_dip.vv), + b.ovov: einsum('ij,ab->iajb', total_dip.oo, total_dip.vv) + - einsum('ib,ja->iajb', total_dip.ov, total_dip.ov), + b.vvvv: einsum('ac,bd->abcd', total_dip.vv, total_dip.vv) + - einsum('ad,bc->abcd', total_dip.vv, total_dip.vv), } return ds[block] @@ -241,7 +241,8 @@ def eri(self, block): if hasattr(self, "coupling"): from . import block as b from .functions import einsum - # Since there is no TwoParticleOperator object, we initialize it like this + # Since there is no TwoParticleOperator object, + # we initialize it like this ds_init = OneParticleOperator(self.mospaces, is_symmetric=True) ds = { b.oooo: einsum('ik,jl->ijkl', ds_init.oo, ds_init.oo), diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index b63a0df6..f6484777 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -104,9 +104,11 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): def block_ph_gs_0(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) + block_ph_gs_0_couple = block_ph_gs_0_phot_couple = block_ph_gs_0_phot =\ -block_ph_gs_0_couple_edge = block_ph_gs_0_phot_couple_edge =\ -block_ph_gs_0_phot2 = block_ph_gs_0_couple_inner = block_ph_gs_0_phot_couple_inner =\ + block_ph_gs_0_couple_edge = block_ph_gs_0_phot_couple_edge =\ + block_ph_gs_0_phot2 = block_ph_gs_0_couple_inner =\ + block_ph_gs_0_phot_couple_inner =\ block_ph_gs_0 @@ -117,17 +119,18 @@ def block_ph_gs_0(hf, mp, intermediates): def block_ph_ph_0(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo if hasattr(hf, "coupling"): - diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), - fCC.diagonal())) + diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), + fCC.diagonal())) def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, hf.fvv) - einsum("IJ,Ja->Ia", fCC, ampl.ph) - )) + )) else: diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), - fCC.diagonal())) + fCC.diagonal())) + def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, hf.fvv) @@ -138,19 +141,21 @@ def apply(ampl): block_cvs_ph_ph_0 = block_ph_ph_0 -def block_ph_ph_0_couple(hf, mp, intermediates): + +def block_ph_ph_0_couple(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) + block_ph_ph_0_phot_couple = block_ph_ph_0_phot_couple_edge =\ -block_ph_ph_0_phot_couple_inner = block_ph_ph_0_couple_edge =\ -block_ph_ph_0_couple_inner = block_ph_ph_0_couple + block_ph_ph_0_phot_couple_inner = block_ph_ph_0_couple_edge =\ + block_ph_ph_0_couple_inner = block_ph_ph_0_couple def block_ph_ph_0_phot(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo if hasattr(hf, "coupling"): diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), - fCC.diagonal())) + fCC.diagonal())) def apply(ampl): return AmplitudeVector(ph=( @@ -162,10 +167,11 @@ def apply(ampl): "but ReferenceState has no coupling attribute") return AdcBlock(apply, diagonal) + def block_ph_ph_0_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), - fCC.diagonal())) + fCC.diagonal())) def apply(ampl): return AmplitudeVector(ph=( @@ -206,11 +212,13 @@ def block_pphh_pphh_0_couple(hf, mp, intermediates): block_pphh_pphh_0_phot_couple = block_pphh_pphh_0_phot_couple_edge =\ -block_pphh_pphh_0_phot_couple_inner = block_pphh_pphh_0_couple_edge =\ -block_pphh_pphh_0_couple_inner = block_pphh_pphh_0_couple + block_pphh_pphh_0_phot_couple_inner = block_pphh_pphh_0_couple_edge =\ + block_pphh_pphh_0_couple_inner = block_pphh_pphh_0_couple + def block_pphh_pphh_0_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return AmplitudeVector(pphh=( + 2 * einsum("ijac,bc->ijab", ampl.pphh1, hf.fvv).antisymmetrise(2, 3) @@ -232,6 +240,7 @@ def apply(ampl): def block_pphh_pphh_0_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return AmplitudeVector(pphh=( + 2 * einsum("ijac,bc->ijab", ampl.pphh2, hf.fvv).antisymmetrise(2, 3) @@ -244,6 +253,7 @@ def apply(ampl): # # 0th order coupling # + def block_ph_pphh_0(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) @@ -256,15 +266,14 @@ def block_pphh_ph_0(hf, mp, intermediates): block_cvs_pphh_ph_0 = block_pphh_ph_0 block_pphh_ph_0_couple = block_pphh_ph_0_phot_couple = block_pphh_ph_0_phot =\ -block_pphh_ph_0_couple_edge = block_pphh_ph_0_couple_inner =\ -block_pphh_ph_0_phot_couple_edge = block_pphh_ph_0_phot_couple_inner =\ -block_pphh_ph_0_phot2 = block_pphh_ph_0 + block_pphh_ph_0_couple_edge = block_pphh_ph_0_couple_inner =\ + block_pphh_ph_0_phot_couple_edge = block_pphh_ph_0_phot_couple_inner =\ + block_pphh_ph_0_phot2 = block_pphh_ph_0 block_ph_pphh_0_couple = block_ph_pphh_0_phot_couple = block_ph_pphh_0_phot =\ -block_ph_pphh_0_couple_edge = block_ph_pphh_0_couple_inner =\ -block_ph_pphh_0_phot_couple_edge = block_ph_pphh_0_phot_couple_inner =\ -block_ph_pphh_0_phot2 = block_ph_pphh_0 - + block_ph_pphh_0_couple_edge = block_ph_pphh_0_couple_inner =\ + block_ph_pphh_0_phot_couple_edge = block_ph_pphh_0_phot_couple_inner =\ + block_ph_pphh_0_phot2 = block_ph_pphh_0 # @@ -274,33 +283,43 @@ def block_pphh_ph_0(hf, mp, intermediates): def block_ph_gs_1(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) + def block_ph_gs_1_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return omega * ampl.gs1 return AdcBlock(apply, omega) + block_ph_gs_1_phot_couple = block_ph_gs_1_phot_couple_edge =\ -block_ph_gs_1_couple_edge = block_ph_gs_1 + block_ph_gs_1_couple_edge = block_ph_gs_1 + def block_ph_gs_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return (-1) * sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph) return AdcBlock(apply, 0) + def block_ph_gs_1_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return 2 * omega * ampl.gs2 return AdcBlock(apply, 2 * omega) + def block_ph_gs_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return (-1) * sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1) return AdcBlock(apply, 0) + def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) @@ -316,8 +335,8 @@ def block_ph_ph_1(hf, mp, intermediates): diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), - einsum("aa->a", mp.qed_t0_df(b.vv))) + + (1 / 2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), + einsum("aa->a", mp.qed_t0_df(b.vv))) )) def apply(ampl): @@ -325,8 +344,8 @@ def apply(ampl): + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 - + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph) - - (1/2) * einsum("ib,ab->ia", ampl.ph, mp.qed_t0_df(b.vv)) + + (1 / 2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph) + - (1 / 2) * einsum("ib,ab->ia", ampl.ph, mp.qed_t0_df(b.vv)) )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): if hasattr(hf, "first_order_coupling"): @@ -348,8 +367,8 @@ def apply(ampl): )) else: diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - - einsum("IaIa->Ia", CvCv) # order 1 + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 + - einsum("IaIa->Ia", CvCv) # 1 )) def apply(ampl): @@ -386,8 +405,8 @@ def block_ph_ph_1_phot(hf, mp, intermediates): diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), - einsum("aa->a", mp.qed_t0_df(b.vv))) + + (1 / 2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), + einsum("aa->a", mp.qed_t0_df(b.vv))) + intermediates.delta_ia_omega )) @@ -396,8 +415,8 @@ def apply(ampl): + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 - + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) - - (1/2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) + + (1 / 2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph1) + - (1 / 2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) + omega * ampl.ph1 )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): @@ -424,8 +443,8 @@ def apply(ampl): )) else: diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - - einsum("IaIa->Ia", CvCv) # order 1 + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 + - einsum("IaIa->Ia", CvCv) # 1 + intermediates.delta_ia_omega )) @@ -447,8 +466,9 @@ def block_ph_ph_1_couple(hf, mp, intermediates): if hasattr(hf, "coupling"): def apply(ampl): return AmplitudeVector(ph=( - sqrt(omega / 2) * (- einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) + sqrt(omega / 2) * ( + - einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) )) return AdcBlock(apply, 0) @@ -458,9 +478,10 @@ def block_ph_ph_1_phot_couple(hf, mp, intermediates): if hasattr(hf, "coupling"): def apply(ampl): return AmplitudeVector(ph=( - sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) - - mp.qed_t1_df(b.ov) * ampl.gs1) + sqrt(omega / 2) * ( + - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) + - mp.qed_t1_df(b.ov) * ampl.gs1) )) return AdcBlock(apply, 0) @@ -468,8 +489,8 @@ def apply(ampl): def block_ph_ph_1_couple_edge(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) -block_ph_ph_1_phot_couple_edge = block_ph_ph_1_couple_edge +block_ph_ph_1_phot_couple_edge = block_ph_ph_1_couple_edge def block_ph_ph_1_couple_inner(hf, mp, intermediates): @@ -478,22 +499,23 @@ def block_ph_ph_1_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) )) return AdcBlock(apply, 0) - + def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) if hasattr(hf, "coupling"): def apply(ampl): return AmplitudeVector(ph=( - sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) - - mp.qed_t1_df(b.ov) * ampl.gs2) + sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) + - mp.qed_t1_df(b.ov) * ampl.gs2) )) return AdcBlock(apply, 0) + def block_ph_ph_1_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov @@ -503,8 +525,8 @@ def block_ph_ph_1_phot2(hf, mp, intermediates): diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 - + (1/2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), - einsum("aa->a", mp.qed_t0_df(b.vv))) + + (1 / 2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), + einsum("aa->a", mp.qed_t0_df(b.vv))) + intermediates.delta_ia_omega * 2 )) @@ -513,8 +535,8 @@ def apply(ampl): + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 - + (1/2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) - - (1/2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) + + (1 / 2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph2) + - (1 / 2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) + 2 * omega * ampl.ph2 )) elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): @@ -541,8 +563,8 @@ def apply(ampl): )) else: diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - - einsum("IaIa->Ia", CvCv) # order 1 + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 + - einsum("IaIa->Ia", CvCv) # 1 + intermediates.delta_ia_omega * 2 )) @@ -556,8 +578,6 @@ def apply(ampl): return AdcBlock(apply, diagonal) - - def diagonal_pphh_pphh_1(hf): # Fock matrix and ovov diagonal term (sometimes called "intermediate diagonal") dinterm_ov = (direct_sum("a-i->ia", hf.fvv.diagonal(), hf.foo.diagonal()) @@ -623,6 +643,7 @@ def apply(ampl): )) return AdcBlock(apply, 0) + def block_ph_pphh_1_phot(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( @@ -631,6 +652,7 @@ def apply(ampl): )) return AdcBlock(apply, 0) + def block_ph_pphh_1_phot2(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( @@ -640,7 +662,6 @@ def apply(ampl): return AdcBlock(apply, 0) - def block_cvs_ph_pphh_1(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( @@ -658,6 +679,7 @@ def apply(ampl): )) return AdcBlock(apply, 0) + def block_pphh_ph_1_phot(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pphh=( @@ -666,6 +688,7 @@ def apply(ampl): )) return AdcBlock(apply, 0) + def block_pphh_ph_1_phot2(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(pphh=( @@ -687,15 +710,17 @@ def apply(ampl): def block_ph_pphh_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return AmplitudeVector(ph=( - 2 * sqrt(omega/2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) + 2 * sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) # noqa: E501 )) return AdcBlock(apply, 0) def block_ph_pphh_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return AmplitudeVector(ph=( 2 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) @@ -704,30 +729,34 @@ def apply(ampl): block_pphh_ph_1_couple = block_pphh_ph_1_couple_inner =\ -block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge =\ -block_pphh_ph_0_couple + block_pphh_ph_1_phot_couple_edge = block_pphh_ph_1_couple_edge =\ + block_pphh_ph_0_couple block_ph_pphh_1_phot_couple = block_ph_pphh_1_phot_couple_inner =\ -block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge =\ -block_ph_pphh_0_phot_couple + block_ph_pphh_1_phot_couple_edge = block_ph_pphh_1_couple_edge =\ + block_ph_pphh_0_phot_couple def block_pphh_ph_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return AmplitudeVector(pphh=( - 2 * sqrt(omega/2) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), - ampl.ph1).antisymmetrise(0,1).antisymmetrise(2,3) + 2 * sqrt(omega / 2) * einsum( + "jb,ia->ijab", mp.qed_t1_df(b.ov), + ampl.ph1).antisymmetrise(0, 1).antisymmetrise(2, 3) )) return AdcBlock(apply, 0) def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): return AmplitudeVector(pphh=( - 2 * sqrt(omega) * einsum("jb,ia->ijab", mp.qed_t1_df(b.ov), - ampl.ph2).antisymmetrise(0,1).antisymmetrise(2,3) + 2 * sqrt(omega) * einsum( + "jb,ia->ijab", mp.qed_t1_df(b.ov), + ampl.ph2).antisymmetrise(0, 1).antisymmetrise(2, 3) )) return AdcBlock(apply, 0) @@ -739,33 +768,35 @@ def apply(ampl): def block_ph_gs_2(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) + def block_ph_gs_2_phot_couple(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) - def block_ph_gs_2_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return ( sqrt(omega / 2) * einsum("jkbc,kc->jb", mp.t2oo, - mp.qed_t1_df(b.ov)).dot(ampl.ph) - - sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph)) # 1. order + return (sqrt(omega / 2) * einsum( + "jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov)).dot(ampl.ph) + - sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph)) # 1. order return AdcBlock(apply, 0) + def block_ph_gs_2_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return omega * ampl.gs1 # 1. order - + return omega * ampl.gs1 # 1. order + return AdcBlock(apply, omega) + def block_ph_gs_2_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return 2 * omega * ampl.gs2 # 1. order + return 2 * omega * ampl.gs2 # 1. order return AdcBlock(apply, 2 * omega) @@ -777,15 +808,16 @@ def block_ph_gs_2_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return ( sqrt(omega) * einsum("jkbc,kc->jb", mp.t2oo, - mp.qed_t1_df(b.ov)).dot(ampl.ph1) - - sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1)) # 1. order + return (sqrt(omega) * einsum( + "jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov)).dot(ampl.ph1) + - sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1)) # 1. order return AdcBlock(apply, 0) def block_ph_gs_2_phot_couple_edge(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) + def block_ph_gs_2_couple_edge(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) @@ -793,6 +825,7 @@ def block_ph_gs_2_couple_edge(hf, mp, intermediates): # # 2nd order main # + def block_ph_ph_2(hf, mp, intermediates): i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 @@ -808,24 +841,31 @@ def block_ph_ph_2(hf, mp, intermediates): + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * ( - - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) - + (1/2) * 2 * einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + + (-omega / 2) * ( + - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + (1 / 2) * 2 * einsum( + "ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov) + ) + ) )) + def apply(ampl): return AmplitudeVector(ph=( + einsum("ib,ab->ia", ampl.ph, i1) - einsum("ij,ja->ia", i2, ampl.ph) - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 - + (-omega/2) * ( - - einsum("ib,ab->ia", ampl.ph, qed_i1) - - einsum("ij,ja->ia", qed_i2, ampl.ph) - + (1/2) * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph) - + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph))) + + (-omega / 2) * ( + - einsum("ib,ab->ia", ampl.ph, qed_i1) + - einsum("ij,ja->ia", qed_i2, ampl.ph) + + (1 / 2) * ( + mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph) + + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph) + ) + ) )) else: - raise NotImplementedError("QED-ADC(2) from non-QED-HF reference" + raise NotImplementedError("QED-ADC(2) from non-QED-HF reference" "is not implemented") else: diagonal = AmplitudeVector(ph=( @@ -867,14 +907,14 @@ def block_ph_ph_2_couple(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph, qed_i1) - + einsum("ij,ja->ia", qed_i2, ampl.ph) - + sqrt(omega / 2) * ( - + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) - + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) - + sqrt(omega / 2) * ( - - einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) # 1. order - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) # 1. order + + einsum("ib,ab->ia", ampl.ph, qed_i1) + + einsum("ij,ja->ia", qed_i2, ampl.ph) + + sqrt(omega / 2) * ( + + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph) + + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph)) + + sqrt(omega / 2) * ( + - einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) # 1. order )) return AdcBlock(apply, 0) @@ -887,16 +927,16 @@ def block_ph_ph_2_phot_couple(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph1, qed_i1) - + einsum("ij,ja->ia", qed_i2, ampl.ph1) - + sqrt(omega / 2) * ( - + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) - + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) - + gs_part * ampl.gs1 - + sqrt(omega / 2) * ( - - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs1) # gs_ph block 1. order + + einsum("ib,ab->ia", ampl.ph1, qed_i1) + + einsum("ij,ja->ia", qed_i2, ampl.ph1) + + sqrt(omega / 2) * ( + + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + + gs_part * ampl.gs1 + + sqrt(omega / 2) * ( + - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs1) # gs_ph block 1. order )) return AdcBlock(apply, 0) @@ -912,29 +952,29 @@ def block_ph_ph_2_phot(hf, mp, intermediates): qed_i2 = intermediates.adc2_qed_i2 diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) - - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * 2 * ( - - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) - + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - + intermediates.delta_ia_omega # 1. order - )) + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + + (-omega / 2) * 2 * ( + - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + + intermediates.delta_ia_omega # 1. order + )) def apply(ampl): return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph1, i1) - - einsum("ij,ja->ia", i2, ampl.ph1) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 - - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 - + (-omega/2) * 2 * ( - - einsum("ib,ab->ia", ampl.ph1, qed_i1) - - einsum("ij,ja->ia", qed_i2, ampl.ph1) - + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) - + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph1))) - + omega * ampl.ph1 # 1. order + + einsum("ib,ab->ia", ampl.ph1, i1) + - einsum("ij,ja->ia", i2, ampl.ph1) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 + + (-omega / 2) * 2 * ( + - einsum("ib,ab->ia", ampl.ph1, qed_i1) + - einsum("ij,ja->ia", qed_i2, ampl.ph1) + + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) + + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph1))) + + omega * ampl.ph1 # 1. order )) - + if not hasattr(hf, "coupling"): raise NotImplementedError("block_ph_ph_2_phot is requested, " "but ReferenceState has no coupling attribute") @@ -952,27 +992,27 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): qed_i2 = intermediates.adc2_qed_i2 diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) - - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + (-omega/2) * 3 * ( - - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) - + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - + intermediates.delta_ia_omega * 2 # 1. order - )) + + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) + - einsum("IaIa->Ia", hf.ovov) + - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + + (-omega / 2) * 3 * ( + - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) + + intermediates.delta_ia_omega * 2 # 1. order + )) def apply(ampl): return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph2, i1) - - einsum("ij,ja->ia", i2, ampl.ph2) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 - - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 - + (-omega/2) * 3 * ( - - einsum("ib,ab->ia", ampl.ph2, qed_i1) - - einsum("ij,ja->ia", qed_i2, ampl.ph2) - + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph2) - + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph2))) - + 2 * omega * ampl.ph2 # 1. order + + einsum("ib,ab->ia", ampl.ph2, i1) + - einsum("ij,ja->ia", i2, ampl.ph2) + - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 + - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 + + (-omega / 2) * 3 * ( + - einsum("ib,ab->ia", ampl.ph2, qed_i1) + - einsum("ij,ja->ia", qed_i2, ampl.ph2) + + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph2) + + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph2))) + + 2 * omega * ampl.ph2 # 1. order )) return AdcBlock(apply, diagonal) @@ -984,14 +1024,14 @@ def block_ph_ph_2_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( - + sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) - + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) - + sqrt(omega) * ( - + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) - + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) - + sqrt(omega) * ( - - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) # 1. order + + sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) + + sqrt(omega) * ( + + einsum("ka,jkib,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + + einsum("ic,jabc,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) + + sqrt(omega) * ( + - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) # 1. order )) return AdcBlock(apply, 0) @@ -1004,24 +1044,25 @@ def block_ph_ph_2_phot_couple_inner(hf, mp, intermediates): def apply(ampl): return AmplitudeVector(ph=( - + sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) - + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) - + sqrt(omega) * ( - + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) - + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) - + gs_part * ampl.gs2 - + sqrt(omega) * ( - - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order - + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs2) # gs_ph block # 1. order + + sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) + + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) + + sqrt(omega) * ( + + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) + + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) + + gs_part * ampl.gs2 + + sqrt(omega) * ( + - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs2) # gs_ph block # 1. order )) return AdcBlock(apply, 0) def block_ph_ph_2_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): - return - (omega/2) * sqrt(2) * ( + return - (omega / 2) * sqrt(2) * ( einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph - einsum("ka,kb,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - einsum("ic,jc,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) @@ -1029,11 +1070,11 @@ def apply(ampl): return AdcBlock(apply, 0) - def block_ph_ph_2_phot_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) + def apply(ampl): - return - (omega/2) * sqrt(2) * ( + return - (omega / 2) * sqrt(2) * ( einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 - einsum("kb,ka,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - einsum("jc,ic,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) @@ -1041,10 +1082,10 @@ def apply(ampl): return AdcBlock(apply, 0) - # # 2nd order coupling # + def block_ph_pphh_2(hf, mp, intermediates): pia_ooov = intermediates.adc3_pia pib_ovvv = intermediates.adc3_pib @@ -1140,15 +1181,15 @@ def adc2_i2(hf, mp, intermediates): # qed intermediates for adc2, without the factor of (omega/2), # which is added in the actual matrix builder @register_as_intermediate -def adc2_qed_i1(hf, mp, intermediates): # maybe do this with symmetrise - return (1/2) * (einsum("kb,ka->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) - + einsum("ka,kb->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) +def adc2_qed_i1(hf, mp, intermediates): # maybe do this with symmetrise + return (1 / 2) * (einsum("kb,ka->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + + einsum("ka,kb->ab", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) @register_as_intermediate -def adc2_qed_i2(hf, mp, intermediates): # maybe do this with symmetrise - return (1/2) * (einsum("jc,ic->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) - + einsum("ic,jc->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) +def adc2_qed_i2(hf, mp, intermediates): # maybe do this with symmetrise + return (1 / 2) * (einsum("jc,ic->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) + + einsum("ic,jc->ij", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) @register_as_intermediate @@ -1163,7 +1204,6 @@ def term_t2_eri(hf, mp, intermediates): + einsum("ijab,jkbc->ikac", hf.oovv, mp.t2oo)) - @register_as_intermediate def adc2_qed_ph_ph_2_phot_couple_gs_part(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -1181,25 +1221,25 @@ def adc2_qed_ph_ph_2_phot_couple_inner_gs_part(hf, mp, intermediates): @register_as_intermediate def adc2_qed_couple_i1(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (einsum("kc,kacb->ab", mp.qed_t1(b.ov), hf.ovvv))) + return (sqrt(omega / 2) * (einsum("kc,kacb->ab", mp.qed_t1(b.ov), hf.ovvv))) @register_as_intermediate def adc2_qed_couple_i2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (einsum("kc,kjic->ij", mp.qed_t1(b.ov), hf.ooov))) + return (sqrt(omega / 2) * (einsum("kc,kjic->ij", mp.qed_t1(b.ov), hf.ooov))) @register_as_intermediate def adc2_qed_phot_couple_i1(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) + return (sqrt(omega / 2) * (einsum("kc,kbca->ab", mp.qed_t1(b.ov), hf.ovvv))) @register_as_intermediate def adc2_qed_phot_couple_i2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - return ( sqrt(omega / 2) * (einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) + return (sqrt(omega / 2) * (einsum("kc,kijc->ij", mp.qed_t1(b.ov), hf.ooov))) @register_as_intermediate diff --git a/adcc/adc_pp/state2state_transition_dm.py b/adcc/adc_pp/state2state_transition_dm.py index cb019f4d..41793d7b 100644 --- a/adcc/adc_pp/state2state_transition_dm.py +++ b/adcc/adc_pp/state2state_transition_dm.py @@ -42,7 +42,7 @@ def s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates): return dm -def s2s_tdm_qed_adc2_diag_part(mp, amplitude_l, amplitude_r, intermediates): +def s2s_tdm_qed_adc2_diag_part(mp, amplitude_l, amplitude_r, intermediates): dm = s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates) ul1 = amplitude_l.ph ur1 = amplitude_r.ph @@ -52,20 +52,21 @@ def s2s_tdm_qed_adc2_diag_part(mp, amplitude_l, amplitude_r, intermediates): dm_new = OneParticleOperator(mp, is_symmetric=False) dm_new.ov = ( - - einsum("kb,ab->ka", mp.qed_t1(b.ov), p0_vv) - + einsum("ji,ic->jc", p0_oo, mp.qed_t1(b.ov)) - + ul1.dot(mp.qed_t1(b.ov)) * ur1 + - einsum("kb,ab->ka", mp.qed_t1(b.ov), p0_vv) + + einsum("ji,ic->jc", p0_oo, mp.qed_t1(b.ov)) + + ul1.dot(mp.qed_t1(b.ov)) * ur1 ) / 2 dm_new.vo = ( - - einsum("kb,ba->ak", mp.qed_t1(b.ov), p0_vv) - + einsum("ij,ic->cj", p0_oo, mp.qed_t1(b.ov)) - + ur1.dot(mp.qed_t1(b.ov)) * ul1.transpose() + - einsum("kb,ba->ak", mp.qed_t1(b.ov), p0_vv) + + einsum("ij,ic->cj", p0_oo, mp.qed_t1(b.ov)) + + ur1.dot(mp.qed_t1(b.ov)) * ul1.transpose() ) / 2 return dm_new -def s2s_tdm_qed_adc2_edge_part_couple(mp, amplitude_l, amplitude_r, intermediates): + +def s2s_tdm_qed_adc2_edge_part_couple(mp, amplitude_l, amplitude_r, intermediates): dm = s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates) ul1 = amplitude_l.ph ur1 = amplitude_r.ph @@ -74,14 +75,16 @@ def s2s_tdm_qed_adc2_edge_part_couple(mp, amplitude_l, amplitude_r, intermediate dm_new = OneParticleOperator(mp, is_symmetric=False) - dm_new.ov = (mp.qed_t1(b.ov) * ul1.dot(ur1) - - einsum("kb,ab->ka", mp.qed_t1(b.ov), p0_vv) - + einsum("ji,ic->jc", p0_oo, mp.qed_t1(b.ov)) + dm_new.ov = ( + mp.qed_t1(b.ov) * ul1.dot(ur1) + - einsum("kb,ab->ka", mp.qed_t1(b.ov), p0_vv) + + einsum("ji,ic->jc", p0_oo, mp.qed_t1(b.ov)) ) return dm_new -def s2s_tdm_qed_adc2_edge_part_phot_couple(mp, amplitude_l, amplitude_r, intermediates): + +def s2s_tdm_qed_adc2_edge_part_phot_couple(mp, amplitude_l, amplitude_r, intermediates): # noqa: E501 dm = s2s_tdm_adc0(mp, amplitude_l, amplitude_r, intermediates) ul1 = amplitude_l.ph ur1 = amplitude_r.ph @@ -90,15 +93,16 @@ def s2s_tdm_qed_adc2_edge_part_phot_couple(mp, amplitude_l, amplitude_r, interme dm_new = OneParticleOperator(mp, is_symmetric=False) - dm_new.vo = (einsum("ia->ai", mp.qed_t1(b.ov)) * ul1.dot(ur1) - - einsum("kb,ba->ak", mp.qed_t1(b.ov), p0_vv) - + einsum("ij,ic->cj", p0_oo, mp.qed_t1(b.ov)) + dm_new.vo = ( + einsum("ia->ai", mp.qed_t1(b.ov)) * ul1.dot(ur1) + - einsum("kb,ba->ak", mp.qed_t1(b.ov), p0_vv) + + einsum("ij,ic->cj", p0_oo, mp.qed_t1(b.ov)) ) return dm_new -def s2s_tdm_qed_adc2_ph_pphh_coupl_part(mp, amplitude_l, amplitude_r, intermediates): +def s2s_tdm_qed_adc2_ph_pphh_coupl_part(mp, amplitude_l, amplitude_r, intermediates): # noqa: E501 ul1 = amplitude_l.ph ur2 = amplitude_r.pphh @@ -109,7 +113,7 @@ def s2s_tdm_qed_adc2_ph_pphh_coupl_part(mp, amplitude_l, amplitude_r, intermedia return dm -def s2s_tdm_qed_adc2_pphh_ph_phot_coupl_part(mp, amplitude_l, amplitude_r, intermediates): +def s2s_tdm_qed_adc2_pphh_ph_phot_coupl_part(mp, amplitude_l, amplitude_r, intermediates): # noqa: E501 ul2 = amplitude_l.pphh ur1 = amplitude_r.ph @@ -229,8 +233,8 @@ def state2state_transition_dm(method, ground_state, amplitude_from, f"for {method.name}.") else: if hasattr(ground_state, "s2s_contribution"): - ret = DISPATCH[ground_state.s2s_contribution](ground_state, amplitude_to, - amplitude_from, intermediates) + ret = DISPATCH[ground_state.s2s_contribution]( + ground_state, amplitude_to, amplitude_from, intermediates) else: # final state is on the bra side/left (complex conjugate) # see ref https://doi.org/10.1080/00268976.2013.859313, appendix A2 diff --git a/adcc/backends/__init__.py b/adcc/backends/__init__.py index d4ad1118..a950e60f 100644 --- a/adcc/backends/__init__.py +++ b/adcc/backends/__init__.py @@ -86,7 +86,7 @@ def import_scf_results(res): # Here we also have to import the .core.Wavefunction object, # since this is the one returned by the hilbert package if isinstance(res, (psi4.core.HF, psi4.core.Wavefunction)): - return backend_psi4.import_scf(res) + return backend_psi4.import_scf(res) from libadcc import HartreeFockSolution_i if isinstance(res, HartreeFockSolution_i): diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index 04037c8d..f898a0cb 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -230,8 +230,8 @@ def fill_occupation_f(self, out): out[:] = np.hstack((occ_array_a, occ_array_b)) else: out[:] = np.hstack(( - np.asarray(self.wfn.occupation_a()), - np.asarray(self.wfn.occupation_b()) + np.asarray(self.wfn.occupation_a()), + np.asarray(self.wfn.occupation_b()) )) def fill_fock_ff(self, slices, out): @@ -265,14 +265,13 @@ def import_scf(wfn): raise InvalidReference("Right now only RHF and UHF references are " "supported for Psi4.") - # TODO This is not fully correct, because the core.Wavefunction object # has an internal, but py-invisible Options structure, which contains # the actual set of options ... theoretically they could differ scf_type = psi4.core.get_global_option('SCF_TYPE') # CD = Choleski, DF = density-fitting unsupported_scf_types = ["CD"] - if not isinstance(wfn, psi4.core.Wavefunction): # hilbert package only uses DF + if not isinstance(wfn, psi4.core.Wavefunction): # hilbert package only uses DF unsupported_scf_types += ["DISK_DF", "MEM_DF"] if scf_type in unsupported_scf_types: raise InvalidReference("Unsupported Psi4 SCF_TYPE, should not be one " diff --git a/adcc/functions.py b/adcc/functions.py index 75fcc3fc..6dc1ce16 100644 --- a/adcc/functions.py +++ b/adcc/functions.py @@ -135,11 +135,12 @@ def lincomb(coefficients, tensors, evaluate=False): phot_part = lincomb(coefficients, phot_list, evaluate=evaluate) phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) if "pphh" in elec_part.blocks_ph: - return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, + return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, gs1_part, phot_part.ph, phot_part.pphh, gs2_part, phot2_part.ph, phot2_part.pphh) else: - return QED_AmplitudeVector(elec_part.ph, None, gs1_part, phot_part.ph, None, + return QED_AmplitudeVector(elec_part.ph, None, + gs1_part, phot_part.ph, None, gs2_part, phot2_part.ph, None) elif not isinstance(tensors[0], libadcc.Tensor): raise TypeError("Tensor type not supported") diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index 40612133..d5ef0d44 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -101,7 +101,7 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, diag = matrix.diagonal().phot2 else: raise KeyError("qed_subblock can only be None, elec, phot or phot2" - "for guesses from diagonal") + "for guesses from diagonal") return guessfunction(matrix, n_guesses, diag, spin_change, spin_block_symmetrisation, degeneracy_tolerance, @@ -233,7 +233,6 @@ def pred_singles(telem): and telem.spin_change == spin_change and abs(telem.value) <= max_diagonal_value) - elements = find_smallest_matching_elements( pred_singles, diag.ph, motrans, n_guesses, degeneracy_tolerance=degeneracy_tolerance diff --git a/adcc/qed_matrix_from_diag_adc.py b/adcc/qed_matrix_from_diag_adc.py index 5983226a..07f17781 100644 --- a/adcc/qed_matrix_from_diag_adc.py +++ b/adcc/qed_matrix_from_diag_adc.py @@ -23,6 +23,7 @@ import numpy as np import scipy.linalg as sp + class qed_matrix_from_diag_adc: def __init__(self, exstates, refstate): self.s2s = exstates.s2s_dipole_moments_qed @@ -33,7 +34,6 @@ def __init__(self, exstates, refstate): self.n_adc = len(exstates.excitation_energy) self.exc_en = exstates.excitation_energy - def first_order_coupling(self): # build the blocks of the matrix @@ -43,28 +43,28 @@ def first_order_coupling(self): for i, tdm in enumerate(self.tdm): tdm_block[i] = self.coupl * np.sqrt(2 * self.freq) * tdm[2] - s2s_block = - np.sqrt(self.freq/2) * self.coupl *\ - np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] - tdm_block = - np.sqrt(self.freq/2) * tdm_block + s2s_block = - np.sqrt(self.freq / 2) * self.coupl *\ + np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] + tdm_block = - np.sqrt(self.freq / 2) * tdm_block elec_block = np.diag(self.exc_en) phot_block = np.diag(self.exc_en + self.freq) # build the matrix - matrix_upper = np.vstack((elec_block, tdm_block.reshape((1, self.n_adc)), + matrix_upper = np.vstack((elec_block, tdm_block.reshape((1, self.n_adc)), s2s_block)) - matrix_middle = np.concatenate((tdm_block, np.array([self.freq]), + matrix_middle = np.concatenate((tdm_block, np.array([self.freq]), np.zeros(self.n_adc))) - matrix_lower = np.vstack((s2s_block, np.zeros((1, self.n_adc)), + matrix_lower = np.vstack((s2s_block, np.zeros((1, self.n_adc)), phot_block)) - matrix = np.hstack((matrix_upper, matrix_middle.reshape((len(matrix_middle), 1)), + matrix = np.hstack((matrix_upper, + matrix_middle.reshape((len(matrix_middle), 1)), matrix_lower)) return sp.eigh(matrix) - def second_order_coupling(self): # tdm part @@ -72,42 +72,42 @@ def second_order_coupling(self): qed_adc2_tdm_vec = np.empty(self.n_adc) for i, tdm in enumerate(self.tdm): - qed_adc2_tdm_vec[i] = - np.sqrt(self.freq/2) * self.coupl *\ - np.sqrt(2 * self.freq) * tdm[2] + qed_adc2_tdm_vec[i] = - np.sqrt(self.freq / 2) * self.coupl *\ + np.sqrt(2 * self.freq) * tdm[2] # s2s_dipole parts of the ph_ph blocks - - qed_adc1_off_diag_block = - np.sqrt(self.freq/2) * self.coupl *\ + + qed_adc1_off_diag_block = - np.sqrt(self.freq / 2) * self.coupl *\ np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] - qed_adc2_diag_block = - np.sqrt(self.freq/2) * self.coupl *\ + qed_adc2_diag_block = - np.sqrt(self.freq / 2) * self.coupl *\ np.sqrt(2 * self.freq) * self.s2s["qed_adc2_diag"] # missing factor from state.s2s_dipole_moments_qed_adc2_diag # TODO: commit to one way of defining these factors within the approx method - qed_adc2_diag_block *= np.sqrt(self.freq/2) + qed_adc2_diag_block *= np.sqrt(self.freq / 2) - qed_adc2_edge_block_couple = - np.sqrt(self.freq/2) * self.coupl *\ + qed_adc2_edge_block_couple = - np.sqrt(self.freq / 2) * self.coupl *\ np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_couple"] # missing factor from state.s2s_dipole_moments_qed_adc2_edge qed_adc2_edge_block_couple *= np.sqrt(self.freq) - qed_adc2_edge_block_phot_couple = - np.sqrt(self.freq/2) * self.coupl *\ + qed_adc2_edge_block_phot_couple = - np.sqrt(self.freq / 2) * self.coupl *\ np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_phot_couple"] # missing factor from state.s2s_dipole_moments_qed_adc2_edge qed_adc2_edge_block_phot_couple *= np.sqrt(self.freq) # s2s_dipole parts of the pphh_ph and ph_pphh blocks - qed_adc2_ph_pphh_couple_block = - np.sqrt(self.freq/2) * self.coupl *\ + qed_adc2_ph_pphh_couple_block = - np.sqrt(self.freq / 2) * self.coupl *\ np.sqrt(2 * self.freq) * self.s2s["qed_adc2_ph_pphh"] - qed_adc2_pphh_ph_phot_couple_block = - np.sqrt(self.freq/2) * self.coupl *\ - np.sqrt(2 * self.freq) * self.s2s["qed_adc2_pphh_ph"] + qed_adc2_pphh_ph_phot_couple_block = - np.sqrt(self.freq / 2) *\ + self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_pphh_ph"] # we still need the H_1 expectation value "as property" - qed_adc2_couple_block = np.sqrt(self.freq/2) * self.h1["couple"] - qed_adc2_phot_couple_block = np.sqrt(self.freq/2) * self.h1["phot_couple"] + qed_adc2_couple_block = np.sqrt(self.freq / 2) * self.h1["couple"] + qed_adc2_phot_couple_block = np.sqrt(self.freq / 2) * self.h1["phot_couple"] # build the blocks of the matrix @@ -124,26 +124,30 @@ def second_order_coupling(self): couple_block = qed_adc1_off_diag_block + qed_adc2_ph_pphh_couple_block +\ qed_adc2_couple_block - phot_couple_block = qed_adc1_off_diag_block + qed_adc2_pphh_ph_phot_couple_block +\ + phot_couple_block = qed_adc1_off_diag_block +\ + qed_adc2_pphh_ph_phot_couple_block +\ qed_adc2_phot_couple_block # build the matrix - matrix_1 = np.vstack((elec_block, qed_adc2_tdm_vec.reshape((1, self.n_adc)), - phot_couple_block, np.zeros((1, self.n_adc)), + matrix_1 = np.vstack((elec_block, qed_adc2_tdm_vec.reshape((1, self.n_adc)), + phot_couple_block, np.zeros((1, self.n_adc)), qed_adc2_edge_block_phot_couple)) matrix_2 = np.concatenate((qed_adc2_tdm_vec, np.array([self.freq]), - np.zeros(self.n_adc), np.array([0]), np.zeros(self.n_adc))) + np.zeros(self.n_adc), np.array([0]), + np.zeros(self.n_adc))) matrix_3 = np.vstack((couple_block, np.zeros((1, self.n_adc)), phot_block, - np.sqrt(2) * qed_adc2_tdm_vec.reshape((1, self.n_adc)), + np.sqrt(2) * qed_adc2_tdm_vec.reshape((1, self.n_adc)), # noqa: E501 np.sqrt(2) * phot_couple_block)) - matrix_4 = np.concatenate((np.zeros(self.n_adc), np.array([0]), + matrix_4 = np.concatenate((np.zeros(self.n_adc), np.array([0]), np.sqrt(2) * qed_adc2_tdm_vec, 2 * np.array([self.freq]), np.zeros(self.n_adc))) - matrix_5 = np.vstack((qed_adc2_edge_block_couple, np.zeros((1, self.n_adc)), - np.sqrt(2) * couple_block, np.zeros((1, self.n_adc)), phot2_block)) + matrix_5 = np.vstack((qed_adc2_edge_block_couple, np.zeros((1, self.n_adc)), + np.sqrt(2) * couple_block, np.zeros((1, self.n_adc)), + phot2_block)) - matrix = np.hstack((matrix_1, matrix_2.reshape((len(matrix_2), 1)), matrix_3, - matrix_4.reshape((len(matrix_4), 1)), matrix_5)) + matrix = np.hstack((matrix_1, matrix_2.reshape((len(matrix_2), 1)), + matrix_3, matrix_4.reshape((len(matrix_4), 1)), + matrix_5)) - return sp.eigh(matrix) \ No newline at end of file + return sp.eigh(matrix) diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index 71821ea1..b9f79eec 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -200,8 +200,8 @@ def form_residual(rval, rvec): if hasattr(matrix.reference_state, "print_eigvec_norms"): # building the eigenvectors here is just for debugging purposes eigenvecs = [lincomb(v, SS, evaluate=True) - for i, v in enumerate(np.transpose(rvecs)) - if i in epair_mask] + for i, v in enumerate(np.transpose(rvecs)) + if i in epair_mask] for eigv in eigenvecs: print("norm of eigenvector", np.sqrt(eigv @ eigv)) # TODO This is misleading ... actually residual_norms contains @@ -273,7 +273,6 @@ def form_residual(rval, rvec): if explicit_symmetrisation: explicit_symmetrisation.symmetrise(preconds) - # Project the components of the preconditioned vectors away # which are already contained in the subspace. # Then add those, which have a significant norm to the subspace. diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index 12780c49..8af52abf 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -58,8 +58,8 @@ def symmetrise(self, new_vectors): def symm_subroutine(vec): if not isinstance(vec, AmplitudeVector): - raise TypeError("new_vectors has to be an " - "iterable of AmplitudeVector") + raise TypeError("new_vectors has to be an " + "iterable of AmplitudeVector") for b in vec.blocks_ph: if b not in self.symmetrisation_functions: continue diff --git a/adcc/solver/lanczos.py b/adcc/solver/lanczos.py index 4fcbfca2..61f29f6c 100644 --- a/adcc/solver/lanczos.py +++ b/adcc/solver/lanczos.py @@ -211,7 +211,6 @@ def callback(state, identifier): state.residual_norms = eigenpair_error[epair_mask] converged = np.all(is_rval_converged[epair_mask]) - # TODO For consistency with the Davidson the residual norms are squared # again to give output in the same order of magnitude. state.residual_norms = state.residual_norms**2 diff --git a/adcc/solver/preconditioner.py b/adcc/solver/preconditioner.py index 258fea39..47c49936 100644 --- a/adcc/solver/preconditioner.py +++ b/adcc/solver/preconditioner.py @@ -75,7 +75,7 @@ def apply(self, invecs): "to a single vector if shifts is " "only a single number.") return invecs / (self.diagonal - self.shifts) - elif isinstance(invecs, list): # either list of AmplitudeVectors or QED_AmplitudeVectors + elif isinstance(invecs, list): if len(self.shifts) != len(invecs): raise ValueError("Number of vectors passed does not agree " "with number of shifts stored inside " diff --git a/adcc/test_qed.py b/adcc/test_qed.py index 3dc37aee..cc8b86b5 100644 --- a/adcc/test_qed.py +++ b/adcc/test_qed.py @@ -31,7 +31,6 @@ from adcc.testdata.cache import qed_data import itertools -import pytest # In principle one could also test the approx method against the full # method, by expanding them to the full matrix dimension. The smallest @@ -45,6 +44,7 @@ testcases = ["methox_sto3g", "h2o_sto3g"] methods = ["adc2"] + @expand_test_templates(list(itertools.product(testcases, methods))) class qed_test(unittest.TestCase): def set_refstate(self, case): @@ -57,7 +57,7 @@ def template_approx(self, case, method): self.set_refstate(case) self.refstate.approx = True - approx = adcc.adc2(self.refstate, n_singlets = 5, conv_tol = 1e-7) + approx = adcc.adc2(self.refstate, n_singlets=5, conv_tol=1e-7) ref_name = f"{case}_{method}_approx" approx_ref = qed_data[ref_name]["excitation_energy"] @@ -68,10 +68,10 @@ def template_approx(self, case, method): def template_full(self, case, method): self.set_refstate(case) - full = adcc.adc2(self.refstate, n_singlets = 3, conv_tol = 1e-7) + full = adcc.adc2(self.refstate, n_singlets=3, conv_tol=1e-7) ref_name = f"{case}_{method}_full" full_ref = qed_data[ref_name]["excitation_energy"] assert_allclose(full.excitation_energy, - full_ref, atol=1e-6) \ No newline at end of file + full_ref, atol=1e-6) diff --git a/adcc/workflow.py b/adcc/workflow.py index a889a954..fe9ad5dc 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -157,7 +157,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, If the full solution to the qed method is required, the performance of the standard qed guess is very poor. In that case, this guess performs much better. - + Other parameters ---------------- @@ -205,13 +205,13 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, matrix = construct_adcmatrix( data_or_matrix, core_orbitals=core_orbitals, frozen_core=frozen_core, frozen_virtual=frozen_virtual, method=method, coupl=coupl, - freq=freq, qed_hf=qed_hf, qed_approx=qed_approx, + freq=freq, qed_hf=qed_hf, qed_approx=qed_approx, qed_full_diag=qed_full_diag) n_states, kind = validate_state_parameters( matrix.reference_state, n_states=n_states, n_singlets=n_singlets, n_triplets=n_triplets, n_spin_flip=n_spin_flip, kind=kind, coupl=coupl, - freq=freq, qed_hf=qed_hf, qed_approx=qed_approx, + freq=freq, qed_hf=qed_hf, qed_approx=qed_approx, qed_full_diag=qed_full_diag) # Determine spin change during excitation. If guesses is not None, @@ -245,22 +245,22 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, # Build QED (approximated) matrix from "standard" ADC matrix # and expectation values, if requested. - + if hasattr(matrix.reference_state, "approx"): qed_matrix = qed_matrix_from_diag_adc(exstates, matrix.reference_state) - if method == "adc2" and not hasattr(matrix.reference_state, "first_order_coupling"): + if method == "adc2" and not hasattr(matrix.reference_state, "first_order_coupling"): # noqa: E501 qed_eigvals, qed_eigvecs = qed_matrix.second_order_coupling() else: qed_eigvals, qed_eigvecs = qed_matrix.first_order_coupling() # TODO: This is a bad solution, but filtering out the final excitation - # vectors and properly feeding them back into the corresponding libtensor is + # vectors and properly feeding them back into the corresponding libtensor is # usually just unnecessary, especially since consistent qed properties # are not implemented yet. This way the user is at least provided with # the ADC result without polaritonic coupling between the states and can # then request the energies and vectors from the exstates object. exstates.qed_excitation_energy = qed_eigvals exstates.qed_excitation_vector = qed_eigvecs - + return exstates @@ -268,8 +268,8 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, # Individual steps # def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, - frozen_virtual=None, method=None, coupl=None, - freq=None, qed_hf=True, qed_approx=False, + frozen_virtual=None, method=None, coupl=None, + freq=None, qed_hf=True, qed_approx=False, qed_full_diag=False): """ Use the provided data or AdcMatrix object to check consistency of the @@ -302,7 +302,7 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, # TODO: for now qed keywords are requested as refstate attributes, which # should be forwarded to the adcc_ReferenceState via the above call, and # maybe stored in a dict as e.g. refstate.qed_keys["approx"] = True - if coupl != None: + if coupl is not None: refstate.coupling = coupl refstate.frequency = freq if qed_hf: @@ -408,17 +408,17 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, "ADC calculations in combination with an unrestricted " "ground state.") - # qed sanity checks - if coupl != None or freq != None: - if not (coupl != None and freq != None): + # qed sanity checks + if coupl is not None or freq is not None: + if not (coupl is not None and freq is not None): raise InputError("qed calculation requires coupl and freq") if len(coupl) != 3 or len(freq) != 3: raise InputError("freq and coupl must contain 3 elements," "i.e. x, y, z") - if qed_hf == False: + if not qed_hf: raise InputError("QED-ADC of zeroth and first level are not yet," - "properly tested and second order is not implemented") - + "properly tested and second order is not implemented") + return n_states, kind @@ -469,11 +469,11 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", if guesses is None: if n_guesses is None: n_guesses = estimate_n_guesses(matrix, n_states, n_guesses_per_state) - if hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): # noqa: E501 - guesses = obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, - n_guesses_doubles) + if hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): # noqa: E501 + guesses = obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, + n_guesses_doubles) else: - guesses = obtain_guesses_by_inspection(matrix, n_guesses, kind, + guesses = obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles) else: if len(guesses) < n_states: @@ -529,7 +529,7 @@ def estimate_n_guesses(matrix, n_states, singles_only=True, return max(n_states, n_guesses) -def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None, qed_subblock=None): # noqa: E501 +def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None, qed_subblock=None): # noqa: E501 """ Obtain guesses by inspecting the diagonal matrix elements. If n_guesses_doubles is not None, this number is always adhered to. @@ -547,14 +547,13 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None guess_function = {"any": guesses_any, "singlet": guesses_singlet, "triplet": guesses_triplet, "spin_flip": guesses_spin_flip}[kind] - # Determine number of singles guesses to request n_guess_singles = n_guesses if n_guesses_doubles is not None: n_guess_singles = n_guesses - n_guesses_doubles - singles_guesses = guess_function(matrix, n_guess_singles, block="ph", - qed_subblock=qed_subblock) + singles_guesses = guess_function(matrix, n_guess_singles, block="ph", + qed_subblock=qed_subblock) doubles_guesses = [] if "pphh" in matrix.axis_blocks: @@ -564,7 +563,8 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None n_guesses_doubles = n_guesses - len(singles_guesses) if n_guesses_doubles > 0: doubles_guesses = guess_function(matrix, n_guesses_doubles, - block="pphh", qed_subblock=qed_subblock) + block="pphh", + qed_subblock=qed_subblock) total_guesses = singles_guesses + doubles_guesses if len(total_guesses) < n_guesses: @@ -573,18 +573,18 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None return total_guesses -def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles=None): +def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles=None): # noqa: E501 """ Obtain guesses for QED_AmplitudeVectors, by pushing the subblocks into obtain_guesses_by_inspection. Internal function called from run_adc. """ - guesses_elec = obtain_guesses_by_inspection(matrix, n_guesses, kind, - n_guesses_doubles, qed_subblock="elec") - guesses_phot = obtain_guesses_by_inspection(matrix, n_guesses, kind, - n_guesses_doubles, qed_subblock="phot") - guesses_phot2 = obtain_guesses_by_inspection(matrix, n_guesses, kind, - n_guesses_doubles, qed_subblock="phot2") + guesses_elec = obtain_guesses_by_inspection( + matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="elec") + guesses_phot = obtain_guesses_by_inspection( + matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot") + guesses_phot2 = obtain_guesses_by_inspection( + matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot2") n_guess = len(guesses_elec) # Usually only few states are requested and most of them are close @@ -606,15 +606,17 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= for guess_index in np.arange(n_guess): if "pphh" in matrix.axis_blocks: - guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, - guesses_elec[guess_index].pphh, + guesses_tmp.append(QED_AmplitudeVector( + guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, 0, guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, - 0, guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh)) + 0, guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh + )) else: - guesses_tmp.append(QED_AmplitudeVector(guesses_elec[guess_index].ph, None, + guesses_tmp.append(QED_AmplitudeVector( + guesses_elec[guess_index].ph, None, 0, guesses_phot[guess_index].ph, None, - 0, guesses_phot2[guess_index].ph, None)) - + 0, guesses_phot2[guess_index].ph, None + )) if hasattr(matrix.reference_state, "full_diagonalization"): full_guess = [] @@ -627,22 +629,31 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= tmp_zero_vec = qed_vec.elec.zeros_like() if "pphh" in matrix.axis_blocks: - full_guess.append(QED_AmplitudeVector(tmp_elec_vec.ph, tmp_elec_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) - full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) - full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh)) + full_guess.append(QED_AmplitudeVector( + tmp_elec_vec.ph, tmp_elec_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) + full_guess.append(QED_AmplitudeVector( + tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) + full_guess.append(QED_AmplitudeVector( + tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, + 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh)) else: - full_guess.append(QED_AmplitudeVector(tmp_elec_vec.ph, None, - 0, tmp_zero_vec.ph, None, 0, tmp_zero_vec.ph, None)) - full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, None, - 0, tmp_phot_vec.ph, None, 0, tmp_zero_vec.ph, None)) - full_guess.append(QED_AmplitudeVector(tmp_zero_vec.ph, None, - 0, tmp_zero_vec.ph, None, 0, tmp_phot2_vec.ph, None)) + full_guess.append(QED_AmplitudeVector( + tmp_elec_vec.ph, None, + 0, tmp_zero_vec.ph, None, + 0, tmp_zero_vec.ph, None)) + full_guess.append(QED_AmplitudeVector( + tmp_zero_vec.ph, None, + 0, tmp_phot_vec.ph, None, + 0, tmp_zero_vec.ph, None)) + full_guess.append(QED_AmplitudeVector( + tmp_zero_vec.ph, None, + 0, tmp_zero_vec.ph, None, + 0, tmp_phot2_vec.ph, None)) full_guess.append(guesses_tmp[0].zeros_like()) full_guess.append(guesses_tmp[0].zeros_like()) @@ -657,11 +668,12 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= # to the single photon dispersion mode and how many states one # requests, adjusting these values can significantly increase the # convergence rate - final_guesses[len(final_guesses) - 2].gs1 += 5 # for stronger coupling e.g. 2 - final_guesses[len(final_guesses) - 1].gs2 += 20 # for stronger coupling e.g. 5 + final_guesses[len(final_guesses) - 2].gs1 += 5 # for stronger coupling e.g. 2 + final_guesses[len(final_guesses) - 1].gs2 += 20 # for stronger coupling e.g. 5 return [vec / np.sqrt(vec @ vec) for vec in final_guesses] + def setup_solver_printing(solmethod_name, matrix, kind, default_print, output=None): """ From 9d5f74a683ea33ea7223880cfbfb49d7417dafa8 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Mon, 22 Aug 2022 14:05:30 +0200 Subject: [PATCH 55/64] removed numerical inconsistency of approx method, by reintroducing the adjusted dispatch routine in tdm --- adcc/adc_pp/transition_dm.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index 66bdbb0d..241bb38d 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -145,5 +145,10 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): raise NotImplementedError("transition_dm is not implemented " f"for {method.name}.") else: - ret = DISPATCH[method.name](ground_state, amplitude, intermediates) + if hasattr(ground_state, "tdm_contribution"): + ret = DISPATCH[ground_state.tdm_contribution]( + ground_state, amplitude, intermediates + ) + else: + ret = DISPATCH[method.name](ground_state, amplitude, intermediates) return ret.evaluate() From 1fabe94f66cf35dbaf4f24749352103a8af3b05e Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 9 Sep 2022 10:46:21 +0200 Subject: [PATCH 56/64] adapted the test cases, so they also don't fail, if all tests are run. Furthermore, qed parameters are now passed directly into the ReferenceState, easing up conditions. --- adcc/AdcMatrix.py | 12 ++++++------ adcc/ElectronicTransition.py | 6 +++--- adcc/LazyMp.py | 6 +++--- adcc/ReferenceState.py | 18 +++++++++++++----- adcc/adc_pp/matrix.py | 28 ++++++++++++++-------------- adcc/backends/psi4.py | 4 +++- adcc/solver/davidson.py | 9 +-------- adcc/solver/lanczos.py | 8 -------- adcc/test_ExcitedStates.py | 5 ++++- adcc/test_qed.py | 5 ++++- adcc/testdata/qed_dump.yml | 6 +++--- adcc/workflow.py | 31 +++++++++++++------------------ 12 files changed, 67 insertions(+), 71 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 7d384f87..fb20acb9 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -134,8 +134,8 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, self.ndim = 2 self.extra_terms = [] - if hasattr(self.reference_state, "coupling"): - if method.base_method.name == "adc2x" or method.base_method.name == "adc3": # noqa: E501 + if self.reference_state.qed: + if method.base_method.name in ["adc2x", "adc3"]: raise NotImplementedError("Neither adc2x nor adc3 " "are implemented for QED-ADC") @@ -151,7 +151,7 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, # Determine orders of PT in the blocks if block_orders is None: - if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 + if self.reference_state.qed and not self.reference_state.approx: block_orders = self.qed_default_block_orders[method.base_method.name] # noqa: E501 else: block_orders = self.default_block_orders[method.base_method.name] @@ -205,10 +205,10 @@ def get_pp_blocks(disp_str): if method.is_core_valence_separated: variant = "cvs" # Build full QED-matrix - if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 + if self.reference_state.qed and not self.reference_state.approx: blocks = {} - if hasattr(hf_or_mp.reference_state, "coupling"): + if hf_or_mp.reference_state.qed: for key in self.qed_dispatch_dict: for bl, order in block_orders.items(): if order is not None: @@ -332,7 +332,7 @@ def __init_space_data(self, diagonal): ]) self.shape = (sum(self.axis_lengths.values()), sum(self.axis_lengths.values())) - if hasattr(self.reference_state, "coupling") and not hasattr(self.reference_state, "approx"): # noqa: E501 + if self.reference_state.qed and not self.reference_state.approx: # We leave out the the dimension with the # purely electronic ground state, # since by construction the coupling to it is always zero diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py index 541c5b08..ea92ea66 100644 --- a/adcc/ElectronicTransition.py +++ b/adcc/ElectronicTransition.py @@ -178,7 +178,7 @@ def transition_dipole_moments_qed(self): to build the QED-matrix in the basis of the diagonal purely electric subblock """ - if hasattr(self.reference_state, "approx"): + if self.reference_state.approx: dipole_integrals = self.operators.electric_dipole @@ -214,7 +214,7 @@ def s2s_dipole_moments_qed(self): to build the QED-matrix in the basis of the diagonal purely electric subblock """ - if hasattr(self.reference_state, "approx"): + if self.reference_state.approx: dipole_integrals = self.operators.electric_dipole print("note, that only the z coordinate of the " "dipole integrals is calculated") @@ -256,7 +256,7 @@ def qed_second_order_ph_ph_couplings(self): to build the QED-matrix in the basis of the diagonal purely electric subblock """ - if hasattr(self.reference_state, "approx"): + if self.reference_state.approx: qed_t1 = self.ground_state.qed_t1(b.ov) def couple(qed_t1, ul, ur): diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index f3cbcb21..eec7e0a4 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -181,7 +181,7 @@ def density(self, level=2): up to the specified order of perturbation theory """ if level == 1: - if hasattr(self.reference_state, "coupling"): + if self.reference_state.qed: return self.reference_state.density else: return self.reference_state.density @@ -278,7 +278,7 @@ def energy_correction(self, level=2): qed_mp2_correction = 0 if level == 2 and not is_cvs: terms = [(1.0, hf.oovv, self.t2oo)] - if hasattr(hf, "coupling"): + if hf.qed: total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) omega = ReferenceState.get_qed_omega(hf) total_dip.ov = self.get_qed_total_dip.ov @@ -287,7 +287,7 @@ def energy_correction(self, level=2): -pref * lambda_dip.dot(qed_t) for pref, lambda_dip, qed_t in qed_terms ) - if hasattr(hf, "qed_hf"): + if hf.qed_hf: qed_mp2_correction = qed_mp2_correction_1 else: qed_terms_0 = [(1.0, self.qed_t0(b.ov), self.qed_t0_df(b.ov))] diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index 7cbed3cd..7a7d6dca 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -38,7 +38,8 @@ class ReferenceState(libadcc.ReferenceState): def __init__(self, hfdata, core_orbitals=None, frozen_core=None, frozen_virtual=None, symmetry_check_on_import=False, - import_all_below_n_orbs=10): + import_all_below_n_orbs=10, qed=False, coupl=None, + freq=None, qed_hf=True, qed_approx=False, qed_full_diag=False): """Construct a ReferenceState holding information about the employed SCF reference. @@ -139,6 +140,13 @@ def __init__(self, hfdata, core_orbitals=None, frozen_core=None, which would place the 2nd and 3rd alpha and the 1st and second beta orbital into the core space. """ + self.qed = qed + self.coupling = coupl + self.frequency = freq + self.qed_hf = qed_hf + self.approx = qed_approx + self.full_diagonalization = qed_full_diag + if not isinstance(hfdata, libadcc.HartreeFockSolution_i): hfdata = import_scf_results(hfdata) @@ -185,7 +193,7 @@ def get_qed_total_dip(self, block): # factor needs to be adjusted depending on the input, but since the # hilbert package is currently the best in terms of performance, at # least to my knowledge, the factor should be included here. - if hasattr(self, "coupling"): + if self.qed: dips = self.operators.electric_dipole couplings = self.coupling freqs = self.frequency @@ -200,7 +208,7 @@ def get_qed_omega(self): """ Return the cavity frequency """ - if hasattr(self, "coupling"): + if self.qed: freqs = self.frequency return np.linalg.norm(freqs) @@ -209,7 +217,7 @@ def qed_D_object(self, block): """ Return the object, which is added to the ERIs in a PT QED calculation """ - if hasattr(self, "coupling"): + if self.qed: from . import block as b from .functions import einsum total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) @@ -238,7 +246,7 @@ def qed_D_object(self, block): return ds[block] def eri(self, block): - if hasattr(self, "coupling"): + if self.qed: from . import block as b from .functions import einsum # Since there is no TwoParticleOperator object, diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index f6484777..590ce6be 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -118,7 +118,7 @@ def block_ph_gs_0(hf, mp, intermediates): def block_ph_ph_0(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - if hasattr(hf, "coupling"): + if hf.qed: diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) @@ -153,7 +153,7 @@ def block_ph_ph_0_couple(hf, mp, intermediates): def block_ph_ph_0_phot(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - if hasattr(hf, "coupling"): + if hf.qed: diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) @@ -331,7 +331,7 @@ def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): def block_ph_ph_1(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): + if hf.qed and not hf.qed_hf: diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 @@ -347,7 +347,7 @@ def apply(ampl): + (1 / 2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph) - (1 / 2) * einsum("ib,ab->ia", ampl.ph, mp.qed_t0_df(b.vv)) )) - elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): + elif hf.qed and hf.qed_hf: if hasattr(hf, "first_order_coupling"): i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 @@ -399,7 +399,7 @@ def apply(ampl): def block_ph_ph_1_phot(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): + if hf.qed and not hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) diagonal = AmplitudeVector(ph=( @@ -419,7 +419,7 @@ def apply(ampl): - (1 / 2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) + omega * ampl.ph1 )) - elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): + elif hf.qed and hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) if hasattr(hf, "first_order_coupling"): @@ -463,7 +463,7 @@ def apply(ampl): def block_ph_ph_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - if hasattr(hf, "coupling"): + if hf.qed: def apply(ampl): return AmplitudeVector(ph=( sqrt(omega / 2) * ( @@ -475,7 +475,7 @@ def apply(ampl): def block_ph_ph_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - if hasattr(hf, "coupling"): + if hf.qed: def apply(ampl): return AmplitudeVector(ph=( sqrt(omega / 2) * ( @@ -495,7 +495,7 @@ def block_ph_ph_1_couple_edge(hf, mp, intermediates): def block_ph_ph_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - if hasattr(hf, "coupling"): + if hf.qed: def apply(ampl): return AmplitudeVector(ph=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) @@ -506,7 +506,7 @@ def apply(ampl): def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - if hasattr(hf, "coupling"): + if hf.qed: def apply(ampl): return AmplitudeVector(ph=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) @@ -519,7 +519,7 @@ def apply(ampl): def block_ph_ph_1_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - if hasattr(hf, "coupling") and not hasattr(hf, "qed_hf"): + if hf.qed and not hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) diagonal = AmplitudeVector(ph=( @@ -539,7 +539,7 @@ def apply(ampl): - (1 / 2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) + 2 * omega * ampl.ph2 )) - elif hasattr(hf, "coupling") and hasattr(hf, "qed_hf"): + elif hf.qed and hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) if hasattr(hf, "first_order_coupling"): @@ -832,9 +832,9 @@ def block_ph_ph_2(hf, mp, intermediates): term_t2_eri = intermediates.term_t2_eri - if hasattr(hf, "coupling") and not hasattr(hf, "approx"): + if hf.qed and not hf.approx: omega = float(ReferenceState.get_qed_omega(hf)) - if hasattr(hf, "qed_hf"): + if hf.qed_hf: qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 diagonal = AmplitudeVector(ph=( diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index f898a0cb..2911581b 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -181,6 +181,8 @@ def get_restricted(self): else: print("This is an unrestricted calculation") return False + else: + return False def get_energy_scf(self): return self.wfn.energy() @@ -220,7 +222,7 @@ def fill_orben_f(self, out): out[:] = np.hstack((orben_a, orben_b)) def fill_occupation_f(self, out): - if isinstance(self.wfn, psi4.core.Wavefunction): + if not hasattr(self.wfn, "occupation_a"): # hilbert package num_of_orbs = self.wfn.nmo() nalpha_elec = self.wfn.nalpha() nbeta_elec = self.wfn.nbeta() diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index b9f79eec..b1158859 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -197,13 +197,6 @@ def form_residual(rval, rvec): state.eigenvalues = rvals[epair_mask] state.residuals = [residuals[i] for i in epair_mask] state.residual_norms = np.array([r @ r for r in state.residuals]) - if hasattr(matrix.reference_state, "print_eigvec_norms"): - # building the eigenvectors here is just for debugging purposes - eigenvecs = [lincomb(v, SS, evaluate=True) - for i, v in enumerate(np.transpose(rvecs)) - if i in epair_mask] - for eigv in eigenvecs: - print("norm of eigenvector", np.sqrt(eigv @ eigv)) # TODO This is misleading ... actually residual_norms contains # the norms squared. That's also the used e.g. in adcman to # check for convergence, so using the norm squared is fine, @@ -382,7 +375,7 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, if not max_subspace: # TODO Arnoldi uses this: # max_subspace = max(2 * n_ep + 1, 20) - if hasattr(matrix.reference_state, "full_diagonalization"): + if matrix.reference_state.full_diagonalization: max_subspace = len(guesses) else: max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) diff --git a/adcc/solver/lanczos.py b/adcc/solver/lanczos.py index 61f29f6c..14d2c5ce 100644 --- a/adcc/solver/lanczos.py +++ b/adcc/solver/lanczos.py @@ -215,14 +215,6 @@ def callback(state, identifier): # again to give output in the same order of magnitude. state.residual_norms = state.residual_norms**2 - if hasattr(iterator.matrix.reference_state, "print_eigvec_norms"): - tmp_state = amend_true_residuals(state, subspace, rvals, - rvecs, epair_mask) - - print("norms of eigenvectors") - for vec in tmp_state.eigenvectors: - print(np.sqrt(vec @ vec)) - callback(state, "next_iter") state.timer.restart("iteration") diff --git a/adcc/test_ExcitedStates.py b/adcc/test_ExcitedStates.py index b4d1e0b3..9c13ef8e 100644 --- a/adcc/test_ExcitedStates.py +++ b/adcc/test_ExcitedStates.py @@ -41,8 +41,11 @@ def base_test(self, system, method, kind): for key in dir(exci): if key.startswith("_"): continue + # TODO: Remove qed from blacklist, if the function + # qed_second_order_ph_ph_couplings, is removed from + # @mark_excitation_property() blacklist = ["__", "index", "_ao", "excitation_vector", - "method", "parent_state"] + "method", "parent_state", "qed"] if any(b in key for b in blacklist): continue try: diff --git a/adcc/test_qed.py b/adcc/test_qed.py index cc8b86b5..200a7dfc 100644 --- a/adcc/test_qed.py +++ b/adcc/test_qed.py @@ -48,10 +48,13 @@ @expand_test_templates(list(itertools.product(testcases, methods))) class qed_test(unittest.TestCase): def set_refstate(self, case): - self.refstate = cache.refstate[case] + # Here we have to build the new refstate, instead of using the cached + # refstate, because otherwise the qed objects are not build + self.refstate = adcc.ReferenceState(cache.hfdata[case]) self.refstate.coupling = [0.0, 0.0, 0.05] self.refstate.frequency = [0.0, 0.0, 0.5] self.refstate.qed_hf = True + self.refstate.qed = True def template_approx(self, case, method): self.set_refstate(case) diff --git a/adcc/testdata/qed_dump.yml b/adcc/testdata/qed_dump.yml index c2bb9f25..e19b0812 100644 --- a/adcc/testdata/qed_dump.yml +++ b/adcc/testdata/qed_dump.yml @@ -3,7 +3,7 @@ h2o_sto3g_adc2_full: method: adc2 molecule: h2o approx: False - excitation_energy: [0.470369, 0.572391, 0.59388 ] + excitation_energy: [0.469789, 0.496679, 0.571507] h2o_sto3g_adc2_approx: basis: sto3g method: adc2 @@ -17,7 +17,7 @@ methox_sto3g_adc2_full: method: adc2 molecule: cn approx: False - excitation_energy: [0.34166625, 0.41536805, 0.49308736] + excitation_energy: [0.341541, 0.415104, 0.486678] methox_sto3g_adc2_approx: basis: sto3g method: adc2 @@ -25,4 +25,4 @@ methox_sto3g_adc2_approx: approx: True excitation_energy: [0.341774, 0.41538 , 0.491732, 0.501357, 0.511708, 0.546447, 0.842004, 0.915563, 0.990758, 1.002418, 1.011967, 1.046802, - 1.342381, 1.416067, 1.493567, 1.512128, 1.54733 ] \ No newline at end of file + 1.342381, 1.416067, 1.493567, 1.512128, 1.54733] \ No newline at end of file diff --git a/adcc/workflow.py b/adcc/workflow.py index fe9ad5dc..702ea37e 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -246,7 +246,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, # Build QED (approximated) matrix from "standard" ADC matrix # and expectation values, if requested. - if hasattr(matrix.reference_state, "approx"): + if matrix.reference_state.approx: qed_matrix = qed_matrix_from_diag_adc(exstates, matrix.reference_state) if method == "adc2" and not hasattr(matrix.reference_state, "first_order_coupling"): # noqa: E501 qed_eigvals, qed_eigvecs = qed_matrix.second_order_coupling() @@ -293,24 +293,19 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, "to be specified via the parameter " "core_orbitals.") try: + if coupl is not None: + qed = True + else: + qed = False refstate = adcc_ReferenceState(data_or_matrix, core_orbitals=core_orbitals, frozen_core=frozen_core, - frozen_virtual=frozen_virtual) + frozen_virtual=frozen_virtual, + qed=qed, coupl=coupl, qed_hf=qed_hf, + freq=freq, qed_approx=qed_approx, + qed_full_diag=qed_full_diag) except ValueError as e: raise InputError(str(e)) # In case of an issue with the spaces - # TODO: for now qed keywords are requested as refstate attributes, which - # should be forwarded to the adcc_ReferenceState via the above call, and - # maybe stored in a dict as e.g. refstate.qed_keys["approx"] = True - if coupl is not None: - refstate.coupling = coupl - refstate.frequency = freq - if qed_hf: - refstate.qed_hf = True - if qed_approx: - refstate.approx = True - if qed_full_diag: - refstate.full_diagonalization = True data_or_matrix = refstate elif core_orbitals is not None: mospaces = data_or_matrix.mospaces @@ -416,7 +411,7 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, raise InputError("freq and coupl must contain 3 elements," "i.e. x, y, z") if not qed_hf: - raise InputError("QED-ADC of zeroth and first level are not yet," + raise InputError("QED-ADC of zeroth and first level are not yet" "properly tested and second order is not implemented") return n_states, kind @@ -469,7 +464,7 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", if guesses is None: if n_guesses is None: n_guesses = estimate_n_guesses(matrix, n_states, n_guesses_per_state) - if hasattr(matrix.reference_state, "coupling") and not hasattr(matrix.reference_state, "approx"): # noqa: E501 + if matrix.reference_state.qed and not matrix.reference_state.approx: guesses = obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles) else: @@ -590,7 +585,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= # Usually only few states are requested and most of them are close # to pure electronic states, so we initialize the guess vectors # as almost purely electric guesses. - if not hasattr(matrix.reference_state, "full_diagonalization"): + if not matrix.reference_state.full_diagonalization: for i in np.arange(n_guess): # TODO: maybe make these values accessible by a keyword, since # they can tune the performance. From my experience these work @@ -618,7 +613,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= 0, guesses_phot2[guess_index].ph, None )) - if hasattr(matrix.reference_state, "full_diagonalization"): + if matrix.reference_state.full_diagonalization: full_guess = [] for qed_vec in guesses_tmp: From 9cec484a8259fc2371ab8f56084a30ca8c3c3f08 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 4 Oct 2022 16:14:54 +0200 Subject: [PATCH 57/64] apparently mp1 contribution got added twice in qed-mp2 energy from non-polaritonic hf reference. Also missing indentation, leading to unbounded errors in backends init has been inserted. --- adcc/LazyMp.py | 1 - adcc/backends/__init__.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index eec7e0a4..af6c487d 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -303,7 +303,6 @@ def energy_correction(self, level=2): ) qed_mp2_correction = qed_mp2_correction_1 +\ qed_mp2_correction_0 +\ - qed_mp1_correction +\ qed_mp1_correction elif level == 2 and is_cvs: terms = [(1.0, hf.oovv, self.t2oo), diff --git a/adcc/backends/__init__.py b/adcc/backends/__init__.py index a950e60f..65efea51 100644 --- a/adcc/backends/__init__.py +++ b/adcc/backends/__init__.py @@ -83,10 +83,10 @@ def import_scf_results(res): import psi4 from . import psi4 as backend_psi4 - # Here we also have to import the .core.Wavefunction object, - # since this is the one returned by the hilbert package - if isinstance(res, (psi4.core.HF, psi4.core.Wavefunction)): - return backend_psi4.import_scf(res) + # Here we also have to import the .core.Wavefunction object, + # since this is the one returned by the hilbert package + if isinstance(res, (psi4.core.HF, psi4.core.Wavefunction)): + return backend_psi4.import_scf(res) from libadcc import HartreeFockSolution_i if isinstance(res, HartreeFockSolution_i): From e8a032de447675c0a5963a70236ae65a3393691b Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 13 Oct 2022 18:04:44 +0200 Subject: [PATCH 58/64] qed approx method is now able to account for photon losses in the cavity, due to an imaginary contribution on the diagonal of the final qed-matrix, which is requested as imaginary part of the cavity photon energy --- adcc/ReferenceState.py | 3 ++- adcc/qed_matrix_from_diag_adc.py | 17 +++++++++++++++-- adcc/test_qed.py | 1 + adcc/workflow.py | 17 +++++++++++++++-- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index 7a7d6dca..dd04440f 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -142,7 +142,8 @@ def __init__(self, hfdata, core_orbitals=None, frozen_core=None, """ self.qed = qed self.coupling = coupl - self.frequency = freq + self.frequency = np.real(freq) + self.freq_with_loss = freq self.qed_hf = qed_hf self.approx = qed_approx self.full_diagonalization = qed_full_diag diff --git a/adcc/qed_matrix_from_diag_adc.py b/adcc/qed_matrix_from_diag_adc.py index 07f17781..037f623a 100644 --- a/adcc/qed_matrix_from_diag_adc.py +++ b/adcc/qed_matrix_from_diag_adc.py @@ -31,6 +31,7 @@ def __init__(self, exstates, refstate): self.h1 = exstates.qed_second_order_ph_ph_couplings self.coupl = refstate.coupling[2] self.freq = refstate.frequency[2] + self.full_freq = refstate.freq_with_loss[2] self.n_adc = len(exstates.excitation_energy) self.exc_en = exstates.excitation_energy @@ -47,6 +48,9 @@ def first_order_coupling(self): np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] tdm_block = - np.sqrt(self.freq / 2) * tdm_block + if np.iscomplex(self.full_freq): + self.freq = self.full_freq + elec_block = np.diag(self.exc_en) phot_block = np.diag(self.exc_en + self.freq) @@ -63,7 +67,10 @@ def first_order_coupling(self): matrix_middle.reshape((len(matrix_middle), 1)), matrix_lower)) - return sp.eigh(matrix) + if np.iscomplex(self.full_freq): + return sp.eig(matrix) + else: + return sp.eigh(matrix) def second_order_coupling(self): @@ -111,6 +118,9 @@ def second_order_coupling(self): # build the blocks of the matrix + if np.iscomplex(self.full_freq): + self.freq = self.full_freq + single_excitation_states = np.ones(self.n_adc) elec_block = np.diag(self.exc_en) + qed_adc2_diag_block @@ -150,4 +160,7 @@ def second_order_coupling(self): matrix_3, matrix_4.reshape((len(matrix_4), 1)), matrix_5)) - return sp.eigh(matrix) + if np.iscomplex(self.full_freq): + return sp.eig(matrix) + else: + return sp.eigh(matrix) diff --git a/adcc/test_qed.py b/adcc/test_qed.py index 200a7dfc..52d0b138 100644 --- a/adcc/test_qed.py +++ b/adcc/test_qed.py @@ -53,6 +53,7 @@ def set_refstate(self, case): self.refstate = adcc.ReferenceState(cache.hfdata[case]) self.refstate.coupling = [0.0, 0.0, 0.05] self.refstate.frequency = [0.0, 0.0, 0.5] + self.refstate.freq_with_loss = self.refstate.frequency self.refstate.qed_hf = True self.refstate.qed = True diff --git a/adcc/workflow.py b/adcc/workflow.py index 702ea37e..ef15f866 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -408,11 +408,24 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, if not (coupl is not None and freq is not None): raise InputError("qed calculation requires coupl and freq") if len(coupl) != 3 or len(freq) != 3: - raise InputError("freq and coupl must contain 3 elements," + raise InputError("freq and coupl must contain 3 elements, " "i.e. x, y, z") + if any(np.iscomplex(freq)) and not qed_approx: + raise InputError("imaginary contribution to freq will only be " + "processed, if qed_approx=True") + if freq[0] != 0 or freq[1] != 0: + if qed_approx: + raise InputError("only request qed_approx=True with the cavity " + "photon polarized in z direction") + else: + raise Warning("polarizations of the cavity photon different from " + "z polarization have not been thoroughly tested yet") if not qed_hf: - raise InputError("QED-ADC of zeroth and first level are not yet" + raise InputError("QED-ADC of zeroth and first level are not yet " "properly tested and second order is not implemented") + if qed_full_diag: + raise Warning("Care to only request qed_full_diag=True, if you ask for " + "the maximum number of states possible") return n_states, kind From af4b380f869a39ea5810ff556b30dac2ae6fba03 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 27 Oct 2022 14:41:54 +0200 Subject: [PATCH 59/64] qed adc runs with AmplitudeVector object, instead of QED_AmplitudeVector --- adcc/AdcMatrix.py | 159 +++++------------------ adcc/AmplitudeVector.py | 20 +++ adcc/adc_pp/matrix.py | 188 +++++++++++++++------------- adcc/guess/guesses_from_diagonal.py | 22 ++-- adcc/workflow.py | 56 +++++---- libadcc/pyiface/export_Tensor.cc | 17 +++ 6 files changed, 215 insertions(+), 247 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index fb20acb9..1a909224 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -206,39 +206,18 @@ def get_pp_blocks(disp_str): variant = "cvs" # Build full QED-matrix if self.reference_state.qed and not self.reference_state.approx: - blocks = {} - - if hf_or_mp.reference_state.qed: - for key in self.qed_dispatch_dict: - for bl, order in block_orders.items(): - if order is not None: - blocks[bl + "_" + key] = get_pp_blocks(key)[bl] - - self.__diagonal_gs1 = sum(blocks[block].diagonal for block in blocks - if "ph_gs" in block - and block.endswith("phot")) - self.__diagonal_gs2 = sum(blocks[block].diagonal for block in blocks - if "ph_gs" in block - and block.endswith("phot2")) - - if "pphh_pphh_elec" in blocks: - self.__diagonal = QED_AmplitudeVector( - blocks["ph_ph_elec"].diagonal.evaluate().ph, - blocks["pphh_pphh_elec"].diagonal.evaluate().pphh, - self.__diagonal_gs1, - blocks["ph_ph_phot"].diagonal.evaluate().ph, - blocks["pphh_pphh_phot"].diagonal.evaluate().pphh, - self.__diagonal_gs2, - blocks["ph_ph_phot2"].diagonal.evaluate().ph, - blocks["pphh_pphh_phot2"].diagonal.evaluate().pphh) - else: - self.__diagonal = QED_AmplitudeVector( - blocks["ph_ph_elec"].diagonal.evaluate().ph, None, - self.__diagonal_gs1, - blocks["ph_ph_phot"].diagonal.evaluate().ph, None, - self.__diagonal_gs2, - blocks["ph_ph_phot2"].diagonal.evaluate().ph, None) - self.__init_space_data(self.__diagonal.elec) + blocks = { + bl + "_" + key: get_pp_blocks(key)[bl] + for bl, order in block_orders.items() + if order is not None + for key in self.qed_dispatch_dict + } + + #if hf_or_mp.reference_state.qed: + #for key in self.qed_dispatch_dict: + # for bl, order in block_orders.items(): + # if order is not None: + # blocks[bl + "_" + key] = get_pp_blocks(key)[bl] else: # Build "standard" ADC-matrix blocks = { bl: get_pp_blocks("elec")[bl] @@ -246,31 +225,17 @@ def get_pp_blocks(disp_str): if order is not None } - if diagonal_precomputed: - self.__diagonal = diagonal_precomputed - else: - self.__diagonal = sum(bl.diagonal for bl in blocks.values() - if bl.diagonal) - self.__diagonal.evaluate() - self.__init_space_data(self.__diagonal) + if diagonal_precomputed: + self.__diagonal = diagonal_precomputed + else: + self.__diagonal = sum(bl.diagonal for bl in blocks.values() + if bl.diagonal) + self.__diagonal.evaluate() + self.__init_space_data(self.__diagonal) # TODO Rename to self.block in 0.16.0 self.blocks_ph = {bl: blocks[bl].apply for bl in blocks} - def qed_subblock(self, qed_disp_key): - # These subblocks are "standard ADC matrices", - # so we can use the implemented functions. - # This is useful e.g. in the matvec function - """ - Extraction of subblocks from the full QED-ADC Matrix, - without the ph_gs blocks - ---------- - qed_disp_key : string - key specifying which block to return - """ - return [bl for key, bl in self.blocks_ph.items() - if not key.startswith("ph_gs") and key.endswith(qed_disp_key)] - def __iadd__(self, other): """In-place addition of an :py:class:`AdcExtraTerm` Parameters @@ -326,17 +291,19 @@ def __init_space_data(self, diagonal): self.axis_spaces = {} self.axis_lengths = {} for block in diagonal.blocks_ph: - self.axis_spaces[block] = getattr(diagonal, block).subspaces - self.axis_lengths[block] = np.prod([ - self.mospaces.n_orbs(sp) for sp in self.axis_spaces[block] - ]) + if "gs" in block: + # Either include g1 in whole libadcc backend, or use this + # approach for now, which is only required for functionalities, + # which should not be used with the full qed matrix yet anyway + self.axis_spaces[block] = ['g1'] + self.axis_lengths[block] = 1 + else: + self.axis_spaces[block] = getattr(diagonal, block).subspaces + self.axis_lengths[block] = np.prod([ + self.mospaces.n_orbs(sp) for sp in self.axis_spaces[block] + ]) self.shape = (sum(self.axis_lengths.values()), sum(self.axis_lengths.values())) - if self.reference_state.qed and not self.reference_state.approx: - # We leave out the the dimension with the - # purely electronic ground state, - # since by construction the coupling to it is always zero - self.shape = ((self.shape[0] + 1) * 3 - 1, (self.shape[0] + 1) * 3 - 1) def __repr__(self): ret = f"AdcMatrix({self.method.name}, " @@ -363,7 +330,6 @@ def block_spaces(self, block): "will be removed in 0.16.0. " "Use `matrix.axis_spaces[block]` in the future.") return { - "g": self.axis_spaces.get("gs", None), "s": self.axis_spaces.get("ph", None), "d": self.axis_spaces.get("pphh", None), "t": self.axis_spaces.get("ppphhh", None), @@ -382,8 +348,6 @@ def diagonal(self, block=None): if block is not None: warnings.warn("Support for the block argument will be dropped " "in 0.16.0.") - if block == "g": - return self.__diagonal.gs if block == "s": return self.__diagonal.ph if block == "d": @@ -421,64 +385,7 @@ def matvec(self, v): Compute the matrix-vector product of the ADC matrix with an excitation amplitude and return the result. """ - if isinstance(v, AmplitudeVector): - return sum(block(v) for block in self.blocks_ph.values()) - elif isinstance(v, QED_AmplitudeVector): - - def mv(qed_disp_key): - return sum(block(v) for block in self.qed_subblock(qed_disp_key)) - - phot_part = mv("elec_couple") + mv("phot") + mv("phot_couple_inner") - - if "pphh_pphh_elec" in self.blocks_ph.keys() and not hasattr(self.reference_state, "first_order_coupling"): # noqa: E501 - phot_couple_edge_with_doubles = AmplitudeVector( - ph=mv("phot_couple_edge"), pphh=v.pphh.zeros_like()) - elec_couple_edge_with_doubles = AmplitudeVector( - ph=mv("elec_couple_edge"), pphh=v.pphh.zeros_like()) - elec_part = mv("elec") + mv("phot_couple") +\ - phot_couple_edge_with_doubles - phot2_part = elec_couple_edge_with_doubles +\ - mv("elec_couple_inner") + mv("phot2") - else: - elec_part = mv("elec") + mv("phot_couple") - phot2_part = mv("elec_couple_inner") + mv("phot2") - - gs1_part = 0 - gs2_part = 0 - - for block in self.blocks_ph: - if "gs" in block and not block.startswith("gs"): - if block.endswith("phot2"): - gs2_part += self.blocks_ph[block](v) - elif block.endswith("phot_couple_edge"): - continue - elif block.endswith("phot_couple_inner"): - continue - elif block.endswith("couple_edge"): - continue - elif block.endswith("couple_inner"): - gs2_part += self.blocks_ph[block](v) - elif block.endswith("phot_couple"): - continue - elif block.endswith("phot"): - gs1_part += self.blocks_ph[block](v) - elif block.endswith("couple"): - gs1_part += self.blocks_ph[block](v) - else: # elec - continue - - if "pphh_pphh_elec" in self.blocks_ph.keys(): - return QED_AmplitudeVector( - elec_part.ph, elec_part.pphh, - gs1_part, phot_part.ph, phot_part.pphh, - gs2_part, phot2_part.ph, phot2_part.pphh) - else: - return QED_AmplitudeVector( - elec_part.ph, None, gs1_part, phot_part.ph, None, - gs2_part, phot2_part.ph, None) - else: - raise TypeError("matvec needs to be invoked with " - "AmplitudeVector or QED_AmplitudeVector") + return sum(block(v) for block in self.blocks_ph.values()) def rmatvec(self, v): # ADC matrix is symmetric @@ -494,10 +401,10 @@ def compute_matvec(self, ampl): return self.matvec(ampl) def __matmul__(self, other): - if isinstance(other, (AmplitudeVector, QED_AmplitudeVector)): + if isinstance(other, AmplitudeVector): return self.matvec(other) if isinstance(other, list): - if all(isinstance(elem, (AmplitudeVector, QED_AmplitudeVector)) for elem in other): # noqa: E501 + if all(isinstance(elem, AmplitudeVector) for elem in other): # noqa: E501 return [self.matvec(ov) for ov in other] return NotImplemented diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index 24003399..fcefea10 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -143,12 +143,20 @@ def dot(self, other): or the dot products with a list of AmplitudeVectors. In the latter case a np.ndarray is returned. """ + # __forward_to_blocks cannot handle int and float + #gs_keys = ("gs1", "gs2") if isinstance(other, list): # Make a list where the first index is all singles parts, # the second is all doubles parts and so on return sum(self[b].dot([av[b] for av in other]) for b in self.keys()) + # if b not in gs_keys) + # + sum(self[b] * [av[b] for av in other] + # for b in self.keys() if b in gs_keys)) else: return sum(self[b].dot(other[b]) for b in self.keys()) + # if b not in gs_keys) + # + sum(self[b] * other[b] for b in self.keys() + # if b in gs_keys)) def __matmul__(self, other): if isinstance(other, AmplitudeVector): @@ -158,6 +166,16 @@ def __matmul__(self, other): return self.dot(other) return NotImplemented + #def __forward_to_blocks_scalar(self, fname, other, scalar_keys): + # if isinstance(other, AmplitudeVector): + # if sorted(other.blocks_ph) != sorted(self.blocks_ph): + # raise ValueError("Blocks of both AmplitudeVector objects " + # f"need to agree to perform {fname}") + # ret = {k: getattr(tensor, fname)(other[k]) + # for k, tensor in self.items()} + # else: + # ret = {k: getattr(tensor, fname)(other) for k, tensor in self.items()} + def __forward_to_blocks(self, fname, other): if isinstance(other, AmplitudeVector): if sorted(other.blocks_ph) != sorted(self.blocks_ph): @@ -168,6 +186,8 @@ def __forward_to_blocks(self, fname, other): else: ret = {k: getattr(tensor, fname)(other) for k, tensor in self.items()} if any(r == NotImplemented for r in ret.values()): + #if "gs1" in self.blocks_ph: + # scalar_keys = [k for k in ret.keys() if ret[k] == NotImplemented] return NotImplemented else: return AmplitudeVector(**ret) diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 590ce6be..1a4b97be 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -28,6 +28,7 @@ from adcc.Intermediates import Intermediates, register_as_intermediate from adcc.AmplitudeVector import AmplitudeVector from adcc.ReferenceState import ReferenceState +from libadcc import set_lt_scalar __all__ = ["block"] @@ -87,7 +88,7 @@ def block(ground_state, spaces, order, variant=None, intermediates=None): # For QED-ADC (up to double photon dispersion) we build the matrix as follows: -# elec phot_couple phot_couple_outer +# elec phot_couple phot_couple_edge # elec_couple phot phot_couple_inner # elec_couple_edge elec_couple_inner phot2 # where each block is a "standard" ADC matrix itself, including the groundstate @@ -105,10 +106,25 @@ def block_ph_gs_0(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) -block_ph_gs_0_couple = block_ph_gs_0_phot_couple = block_ph_gs_0_phot =\ +def block_ph_gs_0_phot(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + def apply(ampl): + return AmplitudeVector(gs1=omega * ampl.gs1) + return AdcBlock(apply, AmplitudeVector(gs1=set_lt_scalar(omega))) + + +def block_ph_gs_0_phot2(hf, mp, intermediates): + omega = float(ReferenceState.get_qed_omega(hf)) + + def apply(ampl): + return AmplitudeVector(gs2=2 * omega * ampl.gs2) + return AdcBlock(apply, AmplitudeVector(gs2=set_lt_scalar(2 * omega))) + + +block_ph_gs_0_couple = block_ph_gs_0_phot_couple =\ block_ph_gs_0_couple_edge = block_ph_gs_0_phot_couple_edge =\ - block_ph_gs_0_phot2 = block_ph_gs_0_couple_inner =\ - block_ph_gs_0_phot_couple_inner =\ + block_ph_gs_0_couple_inner = block_ph_gs_0_phot_couple_inner =\ block_ph_gs_0 @@ -154,11 +170,11 @@ def block_ph_ph_0_couple(hf, mp, intermediates): def block_ph_ph_0_phot(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo if hf.qed: - diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), + diagonal = AmplitudeVector(ph1=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( + einsum("ib,ab->ia", ampl.ph1, hf.fvv) - einsum("IJ,Ja->Ia", fCC, ampl.ph1) )) @@ -170,11 +186,11 @@ def apply(ampl): def block_ph_ph_0_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), + diagonal = AmplitudeVector(ph2=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph2=( + einsum("ib,ab->ia", ampl.ph2, hf.fvv) - einsum("IJ,Ja->Ia", fCC, ampl.ph2) )) @@ -195,7 +211,7 @@ def diagonal_pphh_pphh_0(hf, n_omega=None): res = direct_sum("-i-J+a+b->iJab", hf.foo.diagonal(), fCC.diagonal(), hf.fvv.diagonal(), hf.fvv.diagonal()) - return AmplitudeVector(pphh=res.symmetrise(2, 3)) + return res.symmetrise(2, 3) def block_pphh_pphh_0(hf, mp, intermediates): @@ -204,7 +220,7 @@ def apply(ampl): + 2 * einsum("ijac,bc->ijab", ampl.pphh, hf.fvv).antisymmetrise(2, 3) - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh).antisymmetrise(0, 1) )) - return AdcBlock(apply, diagonal_pphh_pphh_0(hf)) + return AdcBlock(apply, AmplitudeVector(pphh=diagonal_pphh_pphh_0(hf))) def block_pphh_pphh_0_couple(hf, mp, intermediates): @@ -220,12 +236,12 @@ def block_pphh_pphh_0_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return AmplitudeVector(pphh=( + return AmplitudeVector(pphh1=( + 2 * einsum("ijac,bc->ijab", ampl.pphh1, hf.fvv).antisymmetrise(2, 3) - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh1).antisymmetrise(0, 1) + omega * ampl.pphh1 )) - return AdcBlock(apply, diagonal_pphh_pphh_0(hf, 1)) + return AdcBlock(apply, AmplitudeVector(pphh1=diagonal_pphh_pphh_0(hf, 1))) def block_cvs_pphh_pphh_0(hf, mp, intermediates): @@ -235,19 +251,19 @@ def apply(ampl): - einsum("ik,kJab->iJab", hf.foo, ampl.pphh) - einsum("JK,iKab->iJab", hf.fcc, ampl.pphh) )) - return AdcBlock(apply, diagonal_pphh_pphh_0(hf)) + return AdcBlock(apply, AmplitudeVector(pphh=diagonal_pphh_pphh_0(hf))) def block_pphh_pphh_0_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return AmplitudeVector(pphh=( + return AmplitudeVector(pphh2=( + 2 * einsum("ijac,bc->ijab", ampl.pphh2, hf.fvv).antisymmetrise(2, 3) - 2 * einsum("ik,kjab->ijab", hf.foo, ampl.pphh2).antisymmetrise(0, 1) + 2 * omega * ampl.pphh2 )) - return AdcBlock(apply, diagonal_pphh_pphh_0(hf, 2)) + return AdcBlock(apply, AmplitudeVector(pphh2=diagonal_pphh_pphh_0(hf, 2))) # @@ -288,19 +304,22 @@ def block_ph_gs_1_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return omega * ampl.gs1 - return AdcBlock(apply, omega) + return AmplitudeVector(gs1=omega * ampl.gs1) + return AdcBlock(apply, AmplitudeVector(gs1=set_lt_scalar(omega))) block_ph_gs_1_phot_couple = block_ph_gs_1_phot_couple_edge =\ - block_ph_gs_1_couple_edge = block_ph_gs_1 + block_ph_gs_1_couple_edge = block_ph_gs_1_phot_couple_inner =\ + block_ph_gs_1 def block_ph_gs_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return (-1) * sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph) + return AmplitudeVector(gs1=( + (-1) * sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph) + )) return AdcBlock(apply, 0) @@ -308,20 +327,22 @@ def block_ph_gs_1_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return 2 * omega * ampl.gs2 - return AdcBlock(apply, 2 * omega) + return AmplitudeVector(gs2=2 * omega * ampl.gs2) + return AdcBlock(apply, AmplitudeVector(gs2=set_lt_scalar(2 * omega))) def block_ph_gs_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return (-1) * sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1) + return AmplitudeVector(gs2=( + (-1) * sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1) + )) return AdcBlock(apply, 0) -def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) +#def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): +# return AdcBlock(lambda ampl: 0, 0) # @@ -402,7 +423,7 @@ def block_ph_ph_1_phot(hf, mp, intermediates): if hf.qed and not hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = AmplitudeVector(ph=( + diagonal = AmplitudeVector(ph1=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 + (1 / 2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), @@ -411,7 +432,7 @@ def block_ph_ph_1_phot(hf, mp, intermediates): )) def apply(ampl): - return AmplitudeVector(ph=( # PT order + return AmplitudeVector(ph1=( # PT order + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 @@ -426,7 +447,7 @@ def apply(ampl): i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 term_t2_eri = intermediates.term_t2_eri - diagonal = AmplitudeVector(ph=( + diagonal = AmplitudeVector(ph1=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) @@ -434,7 +455,7 @@ def apply(ampl): )) def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( + einsum("ib,ab->ia", ampl.ph1, i1) - einsum("ij,ja->ia", i2, ampl.ph1) - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 @@ -442,14 +463,14 @@ def apply(ampl): + omega * ampl.ph1 )) else: - diagonal = AmplitudeVector(ph=( + diagonal = AmplitudeVector(ph1=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 - einsum("IaIa->Ia", CvCv) # 1 + intermediates.delta_ia_omega )) def apply(ampl): - return AmplitudeVector(ph=( # PT order + return AmplitudeVector(ph1=( # PT order + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 @@ -465,7 +486,7 @@ def block_ph_ph_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) if hf.qed: def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph)) @@ -481,7 +502,7 @@ def apply(ampl): sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) - - mp.qed_t1_df(b.ov) * ampl.gs1) + - mp.qed_t1_df(b.ov) * ampl.gs1.to_ndarray()) )) return AdcBlock(apply, 0) @@ -497,7 +518,7 @@ def block_ph_ph_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) if hf.qed: def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph2=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1)) )) @@ -508,10 +529,10 @@ def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) if hf.qed: def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) - - mp.qed_t1_df(b.ov) * ampl.gs2) + - mp.qed_t1_df(b.ov) * ampl.gs2.to_ndarray()) )) return AdcBlock(apply, 0) @@ -522,7 +543,7 @@ def block_ph_ph_1_phot2(hf, mp, intermediates): if hf.qed and not hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) - diagonal = AmplitudeVector(ph=( + diagonal = AmplitudeVector(ph2=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 + (1 / 2) * direct_sum("i-a->ia", einsum("ii->i", mp.qed_t0_df(b.oo)), @@ -531,7 +552,7 @@ def block_ph_ph_1_phot2(hf, mp, intermediates): )) def apply(ampl): - return AmplitudeVector(ph=( # PT order + return AmplitudeVector(ph2=( # PT order + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 @@ -546,7 +567,7 @@ def apply(ampl): i1 = intermediates.adc2_i1 i2 = intermediates.adc2_i2 term_t2_eri = intermediates.term_t2_eri - diagonal = AmplitudeVector(ph=( + diagonal = AmplitudeVector(ph2=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) @@ -554,7 +575,7 @@ def apply(ampl): )) def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph2=( + einsum("ib,ab->ia", ampl.ph2, i1) - einsum("ij,ja->ia", i2, ampl.ph2) - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 @@ -562,14 +583,14 @@ def apply(ampl): + 2 * omega * ampl.ph2 )) else: - diagonal = AmplitudeVector(ph=( + diagonal = AmplitudeVector(ph2=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 - einsum("IaIa->Ia", CvCv) # 1 + intermediates.delta_ia_omega * 2 )) def apply(ampl): - return AmplitudeVector(ph=( # PT order + return AmplitudeVector(ph2=( # PT order + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 @@ -646,7 +667,7 @@ def apply(ampl): def block_ph_pphh_1_phot(hf, mp, intermediates): def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( + einsum("jkib,jkab->ia", hf.ooov, ampl.pphh1) + einsum("ijbc,jabc->ia", ampl.pphh1, hf.ovvv) )) @@ -655,7 +676,7 @@ def apply(ampl): def block_ph_pphh_1_phot2(hf, mp, intermediates): def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph2=( + einsum("jkib,jkab->ia", hf.ooov, ampl.pphh2) + einsum("ijbc,jabc->ia", ampl.pphh2, hf.ovvv) )) @@ -682,7 +703,7 @@ def apply(ampl): def block_pphh_ph_1_phot(hf, mp, intermediates): def apply(ampl): - return AmplitudeVector(pphh=( + return AmplitudeVector(pphh1=( + einsum("ic,jcab->ijab", ampl.ph1, hf.ovvv).antisymmetrise(0, 1) - einsum("ijka,kb->ijab", hf.ooov, ampl.ph1).antisymmetrise(2, 3) )) @@ -691,7 +712,7 @@ def apply(ampl): def block_pphh_ph_1_phot2(hf, mp, intermediates): def apply(ampl): - return AmplitudeVector(pphh=( + return AmplitudeVector(pphh2=( + einsum("ic,jcab->ijab", ampl.ph2, hf.ovvv).antisymmetrise(0, 1) - einsum("ijka,kb->ijab", hf.ooov, ampl.ph2).antisymmetrise(2, 3) )) @@ -712,7 +733,7 @@ def block_ph_pphh_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( 2 * sqrt(omega / 2) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh) # noqa: E501 )) return AdcBlock(apply, 0) @@ -722,7 +743,7 @@ def block_ph_pphh_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph2=( 2 * sqrt(omega) * einsum("kc,ikac->ia", mp.qed_t1_df(b.ov), ampl.pphh1) )) return AdcBlock(apply, 0) @@ -753,7 +774,7 @@ def block_pphh_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return AmplitudeVector(pphh=( + return AmplitudeVector(pphh1=( 2 * sqrt(omega) * einsum( "jb,ia->ijab", mp.qed_t1_df(b.ov), ampl.ph2).antisymmetrise(0, 1).antisymmetrise(2, 3) @@ -769,17 +790,18 @@ def block_ph_gs_2(hf, mp, intermediates): return AdcBlock(lambda ampl: 0, 0) -def block_ph_gs_2_phot_couple(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) +block_ph_gs_2_phot_couple = block_ph_gs_2_phot_couple_edge =\ + block_ph_gs_2_couple_edge = block_ph_gs_2_phot_couple_inner =\ + block_ph_gs_2 def block_ph_gs_2_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return (sqrt(omega / 2) * einsum( + return AmplitudeVector(gs1=(sqrt(omega / 2) * einsum( "jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov)).dot(ampl.ph) - - sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph)) # 1. order + - sqrt(0.5 * omega) * mp.qed_t1_df(b.ov).dot(ampl.ph))) # 1. order return AdcBlock(apply, 0) @@ -787,41 +809,29 @@ def block_ph_gs_2_phot(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return omega * ampl.gs1 # 1. order + return AmplitudeVector(gs1=(omega * ampl.gs1)) # 0. order - return AdcBlock(apply, omega) + return AdcBlock(apply, AmplitudeVector(gs1=set_lt_scalar(omega))) def block_ph_gs_2_phot2(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return 2 * omega * ampl.gs2 # 1. order - return AdcBlock(apply, 2 * omega) - - -def block_ph_gs_2_phot_couple_inner(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) + return AmplitudeVector(gs2=(2 * omega * ampl.gs2)) # 0. order + return AdcBlock(apply, AmplitudeVector(gs2=set_lt_scalar(2 * omega))) def block_ph_gs_2_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return (sqrt(omega) * einsum( + return AmplitudeVector(gs2=(sqrt(omega) * einsum( "jkbc,kc->jb", mp.t2oo, mp.qed_t1_df(b.ov)).dot(ampl.ph1) - - sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1)) # 1. order + - sqrt(omega) * mp.qed_t1_df(b.ov).dot(ampl.ph1))) # 1. order return AdcBlock(apply, 0) -def block_ph_gs_2_phot_couple_edge(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - - -def block_ph_gs_2_couple_edge(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - - # # 2nd order main # @@ -906,7 +916,7 @@ def block_ph_ph_2_couple(hf, mp, intermediates): qed_i2 = intermediates.adc2_qed_couple_i2 def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( + einsum("ib,ab->ia", ampl.ph, qed_i1) + einsum("ij,ja->ia", qed_i2, ampl.ph) + sqrt(omega / 2) * ( @@ -932,11 +942,11 @@ def apply(ampl): + sqrt(omega / 2) * ( + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph1) + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph1)) - + gs_part * ampl.gs1 + + gs_part * ampl.gs1.to_ndarray() + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs1) # gs_ph block 1. order + - mp.qed_t1_df(b.ov) * ampl.gs1.to_ndarray()) # gs_ph block 1. order )) return AdcBlock(apply, 0) @@ -951,18 +961,18 @@ def block_ph_ph_2_phot(hf, mp, intermediates): qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 - diagonal = AmplitudeVector(ph=( + diagonal = AmplitudeVector(ph1=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + (-omega / 2) * 2 * ( - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - + intermediates.delta_ia_omega # 1. order + + intermediates.delta_ia_omega # 0. order )) def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( + einsum("ib,ab->ia", ampl.ph1, i1) - einsum("ij,ja->ia", i2, ampl.ph1) - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 @@ -972,7 +982,7 @@ def apply(ampl): - einsum("ij,ja->ia", qed_i2, ampl.ph1) + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph1) + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph1))) - + omega * ampl.ph1 # 1. order + + omega * ampl.ph1 # 0. order )) if not hasattr(hf, "coupling"): @@ -991,18 +1001,18 @@ def block_ph_ph_2_phot2(hf, mp, intermediates): qed_i1 = intermediates.adc2_qed_i1 qed_i2 = intermediates.adc2_qed_i2 - diagonal = AmplitudeVector(ph=( + diagonal = AmplitudeVector(ph2=( + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - einsum("IaIa->Ia", hf.ovov) - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) + (-omega / 2) * 3 * ( - direct_sum("a+i->ia", qed_i1.diagonal(), qed_i2.diagonal()) + einsum("ia,ia->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov))) - + intermediates.delta_ia_omega * 2 # 1. order + + intermediates.delta_ia_omega * 2 # 0. order )) def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph2=( + einsum("ib,ab->ia", ampl.ph2, i1) - einsum("ij,ja->ia", i2, ampl.ph2) - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 @@ -1012,7 +1022,7 @@ def apply(ampl): - einsum("ij,ja->ia", qed_i2, ampl.ph2) + 0.5 * (mp.qed_t1(b.ov) * mp.qed_t1_df(b.ov).dot(ampl.ph2) + mp.qed_t1_df(b.ov) * mp.qed_t1(b.ov).dot(ampl.ph2))) - + 2 * omega * ampl.ph2 # 1. order + + 2 * omega * ampl.ph2 # 0. order )) return AdcBlock(apply, diagonal) @@ -1023,7 +1033,7 @@ def block_ph_ph_2_couple_inner(hf, mp, intermediates): qed_i2 = intermediates.adc2_qed_couple_i2 def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph2=( + sqrt(2) * einsum("ib,ab->ia", ampl.ph1, qed_i1) + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph1) + sqrt(omega) * ( @@ -1043,17 +1053,17 @@ def block_ph_ph_2_phot_couple_inner(hf, mp, intermediates): qed_i2 = intermediates.adc2_qed_phot_couple_i2 def apply(ampl): - return AmplitudeVector(ph=( + return AmplitudeVector(ph1=( + sqrt(2) * einsum("ib,ab->ia", ampl.ph2, qed_i1) + sqrt(2) * einsum("ij,ja->ia", qed_i2, ampl.ph2) + sqrt(omega) * ( + einsum("kb,ikja,jb->ia", mp.qed_t1(b.ov), hf.ooov, ampl.ph2) + einsum("jc,ibac,jb->ia", mp.qed_t1(b.ov), hf.ovvv, ampl.ph2)) - + gs_part * ampl.gs2 + + gs_part * ampl.gs2.to_ndarray() + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs2) # gs_ph block # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs2.to_ndarray()) # gs_ph block # 1. order )) return AdcBlock(apply, 0) @@ -1062,11 +1072,11 @@ def block_ph_ph_2_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return - (omega / 2) * sqrt(2) * ( + return AmplitudeVector(ph2=(- (omega / 2) * sqrt(2) * ( einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph - einsum("ka,kb,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - einsum("ic,jc,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph) - ) + ))) return AdcBlock(apply, 0) @@ -1074,11 +1084,11 @@ def block_ph_ph_2_phot_couple_edge(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) def apply(ampl): - return - (omega / 2) * sqrt(2) * ( + return AmplitudeVector(ph=(- (omega / 2) * sqrt(2) * ( einsum("kc,kc->", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov)) * ampl.ph2 - einsum("kb,ka,ib->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - einsum("jc,ic,ja->ia", mp.qed_t1(b.ov), mp.qed_t1_df(b.ov), ampl.ph2) - ) + ))) return AdcBlock(apply, 0) diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index d5ef0d44..c8cd31f1 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -91,17 +91,19 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, else: raise ValueError(f"Don't know how to generate guesses for block {block}") - if qed_subblock is None: - diag = matrix.diagonal() - elif qed_subblock == "elec": - diag = matrix.diagonal().elec - elif qed_subblock == "phot": - diag = matrix.diagonal().phot + diag = matrix.diagonal().copy() + + if qed_subblock == "phot": + diag.ph = diag.ph1 + if block == "pphh": + diag.pphh = diag.pphh1 elif qed_subblock == "phot2": - diag = matrix.diagonal().phot2 - else: - raise KeyError("qed_subblock can only be None, elec, phot or phot2" - "for guesses from diagonal") + diag.ph = diag.ph2 + if block == "pphh": + diag.pphh = diag.pphh2 + #else: + # raise KeyError("qed_subblock can only be None, elec, phot or phot2" + # "for guesses from diagonal") return guessfunction(matrix, n_guesses, diag, spin_change, spin_block_symmetrisation, degeneracy_tolerance, diff --git a/adcc/workflow.py b/adcc/workflow.py index ef15f866..20ac000e 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -24,7 +24,7 @@ import warnings import numpy as np -from libadcc import ReferenceState +from libadcc import ReferenceState, set_lt_scalar from . import solver from .guess import (guesses_any, guesses_singlet, guesses_spin_flip, @@ -39,7 +39,7 @@ from .solver.davidson import jacobi_davidson from .solver.explicit_symmetrisation import (IndexSpinSymmetrisation, IndexSymmetrisation) -from .AmplitudeVector import QED_AmplitudeVector +from .AmplitudeVector import AmplitudeVector from .qed_matrix_from_diag_adc import qed_matrix_from_diag_adc __all__ = ["run_adc"] @@ -588,12 +588,16 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= Internal function called from run_adc. """ guesses_elec = obtain_guesses_by_inspection( - matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="elec") + matrix, n_guesses, kind, n_guesses_doubles, qed_subblock=None) guesses_phot = obtain_guesses_by_inspection( matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot") guesses_phot2 = obtain_guesses_by_inspection( matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot2") n_guess = len(guesses_elec) + #omega = matrix.reference_state.get_qed_omega() + #guesses_phot = guesses_elec.ph.copy() + omega + #guesses_phot2 = guesses_elec.ph.copy() + 2 * omega + # Usually only few states are requested and most of them are close # to pure electronic states, so we initialize the guess vectors @@ -610,22 +614,29 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= "equal, but are {} electronic and {} photonic " "guesses".format(len(guesses_elec), len(guesses_phot))) - guesses_tmp = [] - - for guess_index in np.arange(n_guess): - if "pphh" in matrix.axis_blocks: - guesses_tmp.append(QED_AmplitudeVector( - guesses_elec[guess_index].ph, guesses_elec[guess_index].pphh, - 0, guesses_phot[guess_index].ph, guesses_phot[guess_index].pphh, - 0, guesses_phot2[guess_index].ph, guesses_phot2[guess_index].pphh - )) - else: - guesses_tmp.append(QED_AmplitudeVector( - guesses_elec[guess_index].ph, None, - 0, guesses_phot[guess_index].ph, None, - 0, guesses_phot2[guess_index].ph, None - )) - + #final_guesses = [] + zero = set_lt_scalar(0.0) + + if hasattr(guesses_elec[0], "pphh"): + final_guesses = [AmplitudeVector(**{ + "ph": guesses_elec[guess_index].ph, + "pphh": guesses_elec[guess_index].pphh, + "gs1": zero.copy(), "ph1": guesses_phot[guess_index].ph, + "pphh1": guesses_phot[guess_index].pphh, + "gs2": zero.copy(), "ph2": guesses_phot2[guess_index].ph, + "pphh2": guesses_phot2[guess_index].pphh + }) for guess_index in np.arange(n_guess)] + else: + final_guesses = [AmplitudeVector(**{ + "ph": guesses_elec[guess_index].ph, + "gs1": zero.copy(), "ph1": guesses_phot[guess_index].ph, + "gs2": zero.copy(), "ph2": guesses_phot2[guess_index].ph + }) for guess_index in np.arange(n_guess)] + + #for vec in final_guesses: + # vec.gs1.set_from_ndarray(np.array([0])) + # vec.gs2.set_from_ndarray(np.array([0])) + """ if matrix.reference_state.full_diagonalization: full_guess = [] @@ -669,6 +680,7 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= final_guesses = full_guess else: final_guesses = guesses_tmp + """ # TODO: maybe make these values accessible by a keyword, since # they can tune the performance. From my experience these work @@ -676,10 +688,10 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= # to the single photon dispersion mode and how many states one # requests, adjusting these values can significantly increase the # convergence rate - final_guesses[len(final_guesses) - 2].gs1 += 5 # for stronger coupling e.g. 2 - final_guesses[len(final_guesses) - 1].gs2 += 20 # for stronger coupling e.g. 5 + final_guesses[len(final_guesses) - 2].gs1.set_from_ndarray(np.array([5])) # for stronger coupling e.g. 2 + final_guesses[len(final_guesses) - 1].gs2.set_from_ndarray(np.array([20])) # for stronger coupling e.g. 5 - return [vec / np.sqrt(vec @ vec) for vec in final_guesses] + return [vec / (np.sqrt(vec @ vec)) for vec in final_guesses] def setup_solver_printing(solmethod_name, matrix, kind, default_print, diff --git a/libadcc/pyiface/export_Tensor.cc b/libadcc/pyiface/export_Tensor.cc index ecff8ac7..d8ef1bf0 100644 --- a/libadcc/pyiface/export_Tensor.cc +++ b/libadcc/pyiface/export_Tensor.cc @@ -23,6 +23,9 @@ #include #include #include +#include +#include "../AdcMemory.hh" +#include "../tests/wrap_libtensor.hh" namespace libadcc { @@ -346,6 +349,17 @@ static py::object Tensor___repr__(const Tensor& self) { return Tensor___str__(self); } +static ten_ptr set_lt_scalar(const py::float_ n) { + auto adcmem_ptr = std::shared_ptr(new AdcMemory()); + libtensor::bispace<1> bis(1); + std::vector ax{{"x", 1}}; + double vector[] = {n}; + auto v_ptr = std::make_shared>(bis); + libtensor::btod_import_raw<1>(vector, bis.get_bis().get_dims()).perform(*v_ptr); + std::shared_ptr tensor_ptr = wrap_libtensor(adcmem_ptr, ax, v_ptr); + return tensor_ptr; +} + // // Operations with a scalar // @@ -540,6 +554,9 @@ void export_Tensor(py::module& m) { m.def("trace", &Tensor_trace_2, "tensor"_a); m.def("linear_combination_strict", &linear_combination_strict, "coefficients"_a, "tensors"_a); + // This is necessary for smooth handling of ground state contributions + // in AmplitudeVectors + m.def("set_lt_scalar", &set_lt_scalar, "a"_a); } } // namespace libadcc From 529c6a64a2e6c8c4349e0a73775699bfa4bf3646 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 27 Oct 2022 21:39:14 +0200 Subject: [PATCH 60/64] further code cleanup, due to full qed handling via AmplitudeVector, along with other minor changes --- adcc/AdcMatrix.py | 37 ++--- adcc/AmplitudeVector.py | 179 --------------------- adcc/ElectronicTransition.py | 22 +-- adcc/ExcitedStates.py | 14 -- adcc/LazyMp.py | 4 +- adcc/ReferenceState.py | 15 +- adcc/adc_pp/matrix.py | 208 +++++++------------------ adcc/adc_pp/transition_dm.py | 8 +- adcc/backends/psi4.py | 17 +- adcc/functions.py | 22 +-- adcc/guess/guesses_from_diagonal.py | 18 +-- adcc/solver/LanczosIterator.py | 4 +- adcc/solver/davidson.py | 9 +- adcc/solver/explicit_symmetrisation.py | 43 +---- adcc/solver/preconditioner.py | 8 +- adcc/test_qed.py | 2 +- adcc/workflow.py | 133 ++++------------ 17 files changed, 146 insertions(+), 597 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index 1a909224..ab6f10c0 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -30,7 +30,7 @@ from .AdcMethod import AdcMethod from .functions import ones_like from .Intermediates import Intermediates -from .AmplitudeVector import QED_AmplitudeVector, AmplitudeVector +from .AmplitudeVector import AmplitudeVector class AdcExtraTerm: @@ -82,12 +82,6 @@ class AdcMatrix(AdcMatrixlike): "adc3": dict(ph_ph=3, ph_pphh=2, pphh_ph=2, pphh_pphh=1), # noqa: E501 } - qed_default_block_orders = { - "adc0": dict(ph_gs=0, ph_ph=0, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc1": dict(ph_gs=1, ph_ph=1, ph_pphh=None, pphh_ph=None, pphh_pphh=None), # noqa: E501 - "adc2": dict(ph_gs=2, ph_ph=2, ph_pphh=1, pphh_ph=1, pphh_pphh=0), # noqa: E501 - } - def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, diagonal_precomputed=None): """ @@ -134,27 +128,20 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, self.ndim = 2 self.extra_terms = [] - if self.reference_state.qed: - if method.base_method.name in ["adc2x", "adc3"]: - raise NotImplementedError("Neither adc2x nor adc3 " + if self.reference_state.is_qed: + if method.base_method.name in ["adc2x", "adc3"] or self.is_core_valence_separated: # noqa: E501 + raise NotImplementedError("Neither adc2x and adc3 nor cvs methods " "are implemented for QED-ADC") - if hasattr(self.reference_state, "first_order_coupling") and method.base_method.name == "adc2": # noqa: E501 - # this way we only need to include the separate case - # in the ph_ph=1 blocks, and the 4 non-zero coupling blocks - self.qed_default_block_orders["adc2"] = dict( - ph_gs=1, ph_ph=1, ph_pphh=1, pphh_ph=1, pphh_pphh=0) - self.intermediates = intermediates if self.intermediates is None: self.intermediates = Intermediates(self.ground_state) # Determine orders of PT in the blocks if block_orders is None: - if self.reference_state.qed and not self.reference_state.approx: - block_orders = self.qed_default_block_orders[method.base_method.name] # noqa: E501 - else: - block_orders = self.default_block_orders[method.base_method.name] + block_orders = self.default_block_orders[method.base_method.name] + if self.reference_state.is_qed and not self.reference_state.approx: + block_orders["ph_gs"] = block_orders["ph_ph"] else: tmp_orders = self.default_block_orders[method.base_method.name].copy() tmp_orders.update(block_orders) @@ -205,19 +192,13 @@ def get_pp_blocks(disp_str): if method.is_core_valence_separated: variant = "cvs" # Build full QED-matrix - if self.reference_state.qed and not self.reference_state.approx: + if self.reference_state.is_qed and not self.reference_state.approx: blocks = { bl + "_" + key: get_pp_blocks(key)[bl] for bl, order in block_orders.items() if order is not None for key in self.qed_dispatch_dict } - - #if hf_or_mp.reference_state.qed: - #for key in self.qed_dispatch_dict: - # for bl, order in block_orders.items(): - # if order is not None: - # blocks[bl + "_" + key] = get_pp_blocks(key)[bl] else: # Build "standard" ADC-matrix blocks = { bl: get_pp_blocks("elec")[bl] @@ -229,7 +210,7 @@ def get_pp_blocks(disp_str): self.__diagonal = diagonal_precomputed else: self.__diagonal = sum(bl.diagonal for bl in blocks.values() - if bl.diagonal) + if bl.diagonal) self.__diagonal.evaluate() self.__init_space_data(self.__diagonal) diff --git a/adcc/AmplitudeVector.py b/adcc/AmplitudeVector.py index fcefea10..bc661d39 100644 --- a/adcc/AmplitudeVector.py +++ b/adcc/AmplitudeVector.py @@ -22,8 +22,6 @@ ## --------------------------------------------------------------------- import warnings -import numpy as np - class AmplitudeVector(dict): def __init__(self, *args, **kwargs): @@ -143,20 +141,12 @@ def dot(self, other): or the dot products with a list of AmplitudeVectors. In the latter case a np.ndarray is returned. """ - # __forward_to_blocks cannot handle int and float - #gs_keys = ("gs1", "gs2") if isinstance(other, list): # Make a list where the first index is all singles parts, # the second is all doubles parts and so on return sum(self[b].dot([av[b] for av in other]) for b in self.keys()) - # if b not in gs_keys) - # + sum(self[b] * [av[b] for av in other] - # for b in self.keys() if b in gs_keys)) else: return sum(self[b].dot(other[b]) for b in self.keys()) - # if b not in gs_keys) - # + sum(self[b] * other[b] for b in self.keys() - # if b in gs_keys)) def __matmul__(self, other): if isinstance(other, AmplitudeVector): @@ -166,16 +156,6 @@ def __matmul__(self, other): return self.dot(other) return NotImplemented - #def __forward_to_blocks_scalar(self, fname, other, scalar_keys): - # if isinstance(other, AmplitudeVector): - # if sorted(other.blocks_ph) != sorted(self.blocks_ph): - # raise ValueError("Blocks of both AmplitudeVector objects " - # f"need to agree to perform {fname}") - # ret = {k: getattr(tensor, fname)(other[k]) - # for k, tensor in self.items()} - # else: - # ret = {k: getattr(tensor, fname)(other) for k, tensor in self.items()} - def __forward_to_blocks(self, fname, other): if isinstance(other, AmplitudeVector): if sorted(other.blocks_ph) != sorted(self.blocks_ph): @@ -186,8 +166,6 @@ def __forward_to_blocks(self, fname, other): else: ret = {k: getattr(tensor, fname)(other) for k, tensor in self.items()} if any(r == NotImplemented for r in ret.values()): - #if "gs1" in self.blocks_ph: - # scalar_keys = [k for k in ret.keys() if ret[k] == NotImplemented] return NotImplemented else: return AmplitudeVector(**ret) @@ -239,160 +217,3 @@ def __radd__(self, other): else: ret = {k: other + tensor for k, tensor in self.items()} return AmplitudeVector(**ret) - - -class QED_AmplitudeVector: - - # TODO: Initialize this class with **kwargs, and think of a functionality, - # which then eases up e.g. the matvec function in the AdcMatrix.py for - # arbitrarily large QED vectors. However, this is not necessarily required, - # since e.g. QED-ADC(3) is very complicated to derive and QED-ADC(1) (with - # just the single dispersion mode) is purely for academic purposes and - # hence not required to provide optimum performance. - - def __init__(self, ph=None, pphh=None, gs1=None, ph1=None, pphh1=None, - gs2=None, ph2=None, pphh2=None): - - if pphh is not None: - self.elec = AmplitudeVector(ph=ph, pphh=pphh) - self.phot = AmplitudeVector(ph=ph1, pphh=pphh1) - self.phot2 = AmplitudeVector(ph=ph2, pphh=pphh2) - else: - self.elec = AmplitudeVector(ph=ph) - self.phot = AmplitudeVector(ph=ph1) - self.phot2 = AmplitudeVector(ph=ph2) - - self.gs1 = gs1 - self.gs2 = gs2 - self.ph = ph - self.ph1 = ph1 - self.ph2 = ph2 - self.pphh = pphh - self.pphh1 = pphh1 - self.pphh2 = pphh2 - - def dot(self, invec): - def dot_(self, invec): - if "pphh" in self.elec.blocks_ph: - return (self.elec.ph.dot(invec.elec.ph) - + self.elec.pphh.dot(invec.elec.pphh) - + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) - + self.phot.pphh.dot(invec.phot.pphh) - + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph) - + self.phot2.pphh.dot(invec.phot2.pphh)) - else: - return (self.elec.ph.dot(invec.elec.ph) - + self.gs1 * invec.gs1 + self.phot.ph.dot(invec.phot.ph) - + self.gs2 * invec.gs2 + self.phot2.ph.dot(invec.phot2.ph)) - if isinstance(invec, list): - return np.array([dot_(self, elem) for elem in invec]) - else: - return dot_(self, invec) - - def __matmul__(self, other): - if isinstance(other, QED_AmplitudeVector): - return self.dot(other) - if isinstance(other, list): - if all(isinstance(elem, QED_AmplitudeVector) for elem in other): - return self.dot(other) - return NotImplemented - - def __sub__(self, invec): - if isinstance(invec, (float, int)): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector( - ph=self.elec.ph.__sub__(invec), - pphh=self.elec.pphh.__sub__(invec), - gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), - pphh1=self.phot.pphh.__sub__(invec), - gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec), - pphh2=self.phot2.pphh.__sub__(invec)) - else: - return QED_AmplitudeVector( - ph=self.elec.ph.__sub__(invec), - gs1=self.gs1 - invec, ph1=self.phot.ph.__sub__(invec), - gs2=self.gs2 - invec, ph2=self.phot2.ph.__sub__(invec)) - - def __truediv__(self, other): - if isinstance(other, QED_AmplitudeVector): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector( - ph=self.elec.ph.__truediv__(other.elec.ph), - pphh=self.elec.pphh.__truediv__(other.elec.pphh), - gs1=self.gs1 / other.gs1, - ph1=self.phot.ph.__truediv__(other.phot.ph), - pphh1=self.phot.pphh.__truediv__(other.phot.pphh), - gs2=self.gs2 / other.gs2, - ph2=self.phot2.ph.__truediv__(other.phot2.ph), - pphh2=self.phot2.pphh.__truediv__(other.phot2.pphh)) - else: - return QED_AmplitudeVector( - ph=self.elec.ph.__truediv__(other.elec.ph), - gs1=self.gs1 / other.gs1, - ph1=self.phot.ph.__truediv__(other.phot.ph), - gs2=self.gs2 / other.gs2, - ph2=self.phot2.ph.__truediv__(other.phot2.ph)) - elif isinstance(other, (float, int)): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector( - ph=self.elec.ph.__truediv__(other), - pphh=self.elec.pphh.__truediv__(other), - gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), - pphh1=self.phot.pphh.__truediv__(other), - gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other), - pphh2=self.phot2.pphh.__truediv__(other)) - else: - return QED_AmplitudeVector( - ph=self.elec.ph.__truediv__(other), - gs1=self.gs1 / other, ph1=self.phot.ph.__truediv__(other), - gs2=self.gs2 / other, ph2=self.phot2.ph.__truediv__(other)) - - def zeros_like(self): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector( - ph=self.elec.zeros_like().ph, - pphh=self.elec.zeros_like().pphh, - gs1=0, ph1=self.phot.zeros_like().ph, - pphh1=self.phot.zeros_like().pphh, - gs2=0, ph2=self.phot2.zeros_like().ph, - pphh2=self.phot2.zeros_like().pphh) - else: - return QED_AmplitudeVector( - ph=self.elec.zeros_like().ph, pphh=None, - gs1=0, ph1=self.phot.zeros_like().ph, pphh1=None, - gs2=0, ph2=self.phot2.zeros_like().ph, pphh2=None) - - def empty_like(self): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector( - ph=self.elec.empty_like().ph, - pphh=self.elec.empty_like().pphh, - gs1=0, ph1=self.phot.empty_like().ph, - pphh1=self.phot.empty_like().pphh, - gs2=0, ph2=self.phot2.empty_like().ph, - pphh2=self.phot2.empty_like().pphh) - else: - QED_AmplitudeVector( - ph=self.elec.empty_like().ph, pphh=None, - gs1=0, ph1=self.phot.empty_like().ph, pphh1=None, - gs2=0, ph2=self.phot2.empty_like().ph, pphh2=None) - - def copy(self): - if "pphh" in self.elec.blocks_ph: - return QED_AmplitudeVector( - ph=self.elec.copy().ph, pphh=self.elec.copy().pphh, - gs1=self.gs1, ph1=self.phot.copy().ph, - pphh1=self.phot.copy().pphh, - gs2=self.gs2, ph2=self.phot2.copy().ph, - pphh2=self.phot2.copy().pphh) - else: - QED_AmplitudeVector( - ph=self.elec.copy().ph, pphh=None, - gs1=self.gs1, ph1=self.phot.copy().ph, pphh1=None, - gs2=self.gs2, ph2=self.phot2.copy().ph, pphh2=None) - - def evaluate(self): - self.elec.evaluate() - self.phot.evaluate() - self.phot2.evaluate() - return self diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py index ea92ea66..111064c0 100644 --- a/adcc/ElectronicTransition.py +++ b/adcc/ElectronicTransition.py @@ -187,20 +187,12 @@ def tdm(i, prop_level): return transition_dm(self.method, self.ground_state, self.excitation_vector[i]) - if hasattr(self.reference_state, "first_order_coupling"): - - return np.array([ - [product_trace(comp, tdm(i, "adc0")) - for comp in dipole_integrals] - for i in np.arange(len(self.excitation_energy)) - ]) - else: - prop_level = "adc" + str(self.property_method.level - 1) - return np.array([ - [product_trace(comp, tdm(i, prop_level)) - for comp in dipole_integrals] - for i in np.arange(len(self.excitation_energy)) - ]) + prop_level = "adc" + str(self.property_method.level - 1) + return np.array([ + [product_trace(comp, tdm(i, prop_level)) + for comp in dipole_integrals] + for i in np.arange(len(self.excitation_energy)) + ]) else: return ("transition_dipole_moments_qed are only calculated," "if reference_state contains 'approx' attribute") @@ -235,7 +227,7 @@ def final_block(name): block_dict["qed_adc1_off_diag"] = final_block("adc1") - if self.method.name == "adc2" and not hasattr(self.reference_state, "first_order_coupling"): # noqa: E501 + if self.method.name == "adc2": block_dict["qed_adc2_diag"] = final_block("qed_adc2_diag") block_dict["qed_adc2_edge_couple"] = final_block("qed_adc2_edge_couple") # noqa: E501 block_dict["qed_adc2_edge_phot_couple"] = final_block("qed_adc2_edge_phot_couple") # noqa: E501 diff --git a/adcc/ExcitedStates.py b/adcc/ExcitedStates.py index f796e87f..4614da46 100644 --- a/adcc/ExcitedStates.py +++ b/adcc/ExcitedStates.py @@ -35,7 +35,6 @@ from .OneParticleOperator import product_trace from .ElectronicTransition import ElectronicTransition from .FormatDominantElements import FormatDominantElements -from .AmplitudeVector import QED_AmplitudeVector class EnergyCorrection: @@ -417,12 +416,6 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): # Optimise the formatting by pre-inspecting all tensors for tensor in self.excitation_vector: - if isinstance(tensor, QED_AmplitudeVector): - # TODO: Implement tdm and s2s_tdm for QED, so properties can also - # be evaluated for QED_AmplitudeVector objects. For now only - # use AmplitudeVector describing the electric part, so the - # formatting does not have to be adapted. - tensor = tensor.elec vector_format.optimise_formatting(tensor) # Determine width of a line @@ -431,13 +424,6 @@ def describe_amplitudes(self, tolerance=0.01, index_format=None): ret = separator for i, vec in enumerate(self.excitation_vector): - if isinstance(vec, QED_AmplitudeVector): - # TODO: Implement tdm and s2s_tdm for QED, so properties can also - # be evaluated for QED_AmplitudeVector objects. For now only - # use AmplitudeVector describing the electric part, since - # most low-energy states are almost purely electric, so the - # formatting does not have to be adapted. - vec = vec.elec ene = self.excitation_energy[i] eev = ene * eV head = f"State {i:3d} , {ene:13.7g} au" diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index af6c487d..51289c81 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -181,7 +181,7 @@ def density(self, level=2): up to the specified order of perturbation theory """ if level == 1: - if self.reference_state.qed: + if self.reference_state.is_qed: return self.reference_state.density else: return self.reference_state.density @@ -278,7 +278,7 @@ def energy_correction(self, level=2): qed_mp2_correction = 0 if level == 2 and not is_cvs: terms = [(1.0, hf.oovv, self.t2oo)] - if hf.qed: + if hf.is_qed: total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) omega = ReferenceState.get_qed_omega(hf) total_dip.ov = self.get_qed_total_dip.ov diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index dd04440f..7424a4f8 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -38,8 +38,8 @@ class ReferenceState(libadcc.ReferenceState): def __init__(self, hfdata, core_orbitals=None, frozen_core=None, frozen_virtual=None, symmetry_check_on_import=False, - import_all_below_n_orbs=10, qed=False, coupl=None, - freq=None, qed_hf=True, qed_approx=False, qed_full_diag=False): + import_all_below_n_orbs=10, is_qed=False, coupl=None, + freq=None, qed_hf=True, qed_approx=False): """Construct a ReferenceState holding information about the employed SCF reference. @@ -140,13 +140,12 @@ def __init__(self, hfdata, core_orbitals=None, frozen_core=None, which would place the 2nd and 3rd alpha and the 1st and second beta orbital into the core space. """ - self.qed = qed + self.is_qed = is_qed self.coupling = coupl self.frequency = np.real(freq) self.freq_with_loss = freq self.qed_hf = qed_hf self.approx = qed_approx - self.full_diagonalization = qed_full_diag if not isinstance(hfdata, libadcc.HartreeFockSolution_i): hfdata = import_scf_results(hfdata) @@ -194,7 +193,7 @@ def get_qed_total_dip(self, block): # factor needs to be adjusted depending on the input, but since the # hilbert package is currently the best in terms of performance, at # least to my knowledge, the factor should be included here. - if self.qed: + if self.is_qed: dips = self.operators.electric_dipole couplings = self.coupling freqs = self.frequency @@ -209,7 +208,7 @@ def get_qed_omega(self): """ Return the cavity frequency """ - if self.qed: + if self.is_qed: freqs = self.frequency return np.linalg.norm(freqs) @@ -218,7 +217,7 @@ def qed_D_object(self, block): """ Return the object, which is added to the ERIs in a PT QED calculation """ - if self.qed: + if self.is_qed: from . import block as b from .functions import einsum total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) @@ -247,7 +246,7 @@ def qed_D_object(self, block): return ds[block] def eri(self, block): - if self.qed: + if self.is_qed: from . import block as b from .functions import einsum # Since there is no TwoParticleOperator object, diff --git a/adcc/adc_pp/matrix.py b/adcc/adc_pp/matrix.py index 1a4b97be..d86500ef 100644 --- a/adcc/adc_pp/matrix.py +++ b/adcc/adc_pp/matrix.py @@ -127,6 +127,8 @@ def apply(ampl): block_ph_gs_0_couple_inner = block_ph_gs_0_phot_couple_inner =\ block_ph_gs_0 +block_cvs_ph_gs_0 = block_cvs_ph_gs_1 = block_cvs_ph_gs_2 = block_ph_gs_0 + # # 0th order main @@ -134,7 +136,7 @@ def apply(ampl): def block_ph_ph_0(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - if hf.qed: + if hf.is_qed: diagonal = AmplitudeVector(ph=direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal())) @@ -169,9 +171,9 @@ def block_ph_ph_0_couple(hf, mp, intermediates): def block_ph_ph_0_phot(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo - if hf.qed: + if hf.is_qed: diagonal = AmplitudeVector(ph1=direct_sum("a-i->ia", hf.fvv.diagonal(), - fCC.diagonal())) + fCC.diagonal())) def apply(ampl): return AmplitudeVector(ph1=( @@ -187,7 +189,7 @@ def apply(ampl): def block_ph_ph_0_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo diagonal = AmplitudeVector(ph2=direct_sum("a-i->ia", hf.fvv.diagonal(), - fCC.diagonal())) + fCC.diagonal())) def apply(ampl): return AmplitudeVector(ph2=( @@ -296,21 +298,13 @@ def block_pphh_ph_0(hf, mp, intermediates): # 1st order gs blocks # -def block_ph_gs_1(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - - -def block_ph_gs_1_phot(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - def apply(ampl): - return AmplitudeVector(gs1=omega * ampl.gs1) - return AdcBlock(apply, AmplitudeVector(gs1=set_lt_scalar(omega))) +block_ph_gs_1_phot = block_ph_gs_0_phot +block_ph_gs_1_phot2 = block_ph_gs_0_phot2 block_ph_gs_1_phot_couple = block_ph_gs_1_phot_couple_edge =\ block_ph_gs_1_couple_edge = block_ph_gs_1_phot_couple_inner =\ - block_ph_gs_1 + block_ph_gs_1 = block_ph_gs_0 def block_ph_gs_1_couple(hf, mp, intermediates): @@ -323,14 +317,6 @@ def apply(ampl): return AdcBlock(apply, 0) -def block_ph_gs_1_phot2(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - def apply(ampl): - return AmplitudeVector(gs2=2 * omega * ampl.gs2) - return AdcBlock(apply, AmplitudeVector(gs2=set_lt_scalar(2 * omega))) - - def block_ph_gs_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) @@ -341,10 +327,6 @@ def apply(ampl): return AdcBlock(apply, 0) -#def block_ph_gs_1_phot_couple_inner(hf, mp, intermediates): -# return AdcBlock(lambda ampl: 0, 0) - - # # 1st order main # @@ -352,7 +334,7 @@ def apply(ampl): def block_ph_ph_1(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - if hf.qed and not hf.qed_hf: + if hf.is_qed and not hf.qed_hf: diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 - einsum("IaIa->Ia", CvCv) # order 1 @@ -368,37 +350,19 @@ def apply(ampl): + (1 / 2) * einsum("ij,ja->ia", mp.qed_t0_df(b.oo), ampl.ph) - (1 / 2) * einsum("ib,ab->ia", ampl.ph, mp.qed_t0_df(b.vv)) )) - elif hf.qed and hf.qed_hf: - if hasattr(hf, "first_order_coupling"): - i1 = intermediates.adc2_i1 - i2 = intermediates.adc2_i2 - term_t2_eri = intermediates.term_t2_eri - diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) - - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - )) + elif hf.is_qed and hf.qed_hf: + diagonal = AmplitudeVector(ph=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 + - einsum("IaIa->Ia", CvCv) # 1 + )) - def apply(ampl): - return AmplitudeVector(ph=( - + einsum("ib,ab->ia", ampl.ph, i1) - - einsum("ij,ja->ia", i2, ampl.ph) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph) # 1 - - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph) # 2 - )) - else: - diagonal = AmplitudeVector(ph=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 - - einsum("IaIa->Ia", CvCv) # 1 + def apply(ampl): + return AmplitudeVector(ph=( # PT order + + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 )) - def apply(ampl): - return AmplitudeVector(ph=( # PT order - + einsum("ib,ab->ia", ampl.ph, hf.fvv) # 0 - - einsum("IJ,Ja->Ia", fCC, ampl.ph) # 0 - - einsum("JaIb,Jb->Ia", CvCv, ampl.ph) # 1 - )) - else: diagonal = AmplitudeVector(ph=( + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # order 0 @@ -420,7 +384,7 @@ def apply(ampl): def block_ph_ph_1_phot(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - if hf.qed and not hf.qed_hf: + if hf.is_qed and not hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) diagonal = AmplitudeVector(ph1=( @@ -440,42 +404,22 @@ def apply(ampl): - (1 / 2) * einsum("ib,ab->ia", ampl.ph1, mp.qed_t0_df(b.vv)) + omega * ampl.ph1 )) - elif hf.qed and hf.qed_hf: + elif hf.is_qed and hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) - if hasattr(hf, "first_order_coupling"): - i1 = intermediates.adc2_i1 - i2 = intermediates.adc2_i2 - term_t2_eri = intermediates.term_t2_eri - diagonal = AmplitudeVector(ph1=( - + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) - - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + intermediates.delta_ia_omega - )) + diagonal = AmplitudeVector(ph1=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 + - einsum("IaIa->Ia", CvCv) # 1 + + intermediates.delta_ia_omega + )) - def apply(ampl): - return AmplitudeVector(ph1=( - + einsum("ib,ab->ia", ampl.ph1, i1) - - einsum("ij,ja->ia", i2, ampl.ph1) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph1) # 1 - - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph1) # 2 - + omega * ampl.ph1 - )) - else: - diagonal = AmplitudeVector(ph1=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 - - einsum("IaIa->Ia", CvCv) # 1 - + intermediates.delta_ia_omega + def apply(ampl): + return AmplitudeVector(ph1=( # PT order + + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 + + omega * ampl.ph1 )) - - def apply(ampl): - return AmplitudeVector(ph1=( # PT order - + einsum("ib,ab->ia", ampl.ph1, hf.fvv) # 0 - - einsum("IJ,Ja->Ia", fCC, ampl.ph1) # 0 - - einsum("JaIb,Jb->Ia", CvCv, ampl.ph1) # 1 - + omega * ampl.ph1 - )) else: raise NotImplementedError("block_ph_ph_1_phot is requested, " "but ReferenceState has no coupling attribute") @@ -484,7 +428,7 @@ def apply(ampl): def block_ph_ph_1_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - if hf.qed: + if hf.is_qed: def apply(ampl): return AmplitudeVector(ph1=( sqrt(omega / 2) * ( @@ -496,7 +440,7 @@ def apply(ampl): def block_ph_ph_1_phot_couple(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - if hf.qed: + if hf.is_qed: def apply(ampl): return AmplitudeVector(ph=( sqrt(omega / 2) * ( @@ -516,7 +460,7 @@ def block_ph_ph_1_couple_edge(hf, mp, intermediates): def block_ph_ph_1_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - if hf.qed: + if hf.is_qed: def apply(ampl): return AmplitudeVector(ph2=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) @@ -527,7 +471,7 @@ def apply(ampl): def block_ph_ph_1_phot_couple_inner(hf, mp, intermediates): omega = float(ReferenceState.get_qed_omega(hf)) - if hf.qed: + if hf.is_qed: def apply(ampl): return AmplitudeVector(ph1=( sqrt(omega) * (- einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) @@ -540,7 +484,7 @@ def apply(ampl): def block_ph_ph_1_phot2(hf, mp, intermediates): fCC = hf.fcc if hf.has_core_occupied_space else hf.foo CvCv = hf.cvcv if hf.has_core_occupied_space else hf.ovov - if hf.qed and not hf.qed_hf: + if hf.is_qed and not hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) diagonal = AmplitudeVector(ph2=( @@ -560,42 +504,25 @@ def apply(ampl): - (1 / 2) * einsum("ib,ab->ia", ampl.ph2, mp.qed_t0_df(b.vv)) + 2 * omega * ampl.ph2 )) - elif hf.qed and hf.qed_hf: + elif hf.is_qed and hf.qed_hf: omega = float(ReferenceState.get_qed_omega(hf)) - if hasattr(hf, "first_order_coupling"): - i1 = intermediates.adc2_i1 - i2 = intermediates.adc2_i2 - term_t2_eri = intermediates.term_t2_eri - diagonal = AmplitudeVector(ph2=( - + direct_sum("a-i->ia", i1.diagonal(), i2.diagonal()) - - einsum("IaIa->Ia", hf.ovov) - - einsum("ikac,ikac->ia", mp.t2oo, hf.oovv) - + intermediates.delta_ia_omega * 2 - )) + diagonal = AmplitudeVector(ph2=( + + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 + - einsum("IaIa->Ia", CvCv) # 1 + + intermediates.delta_ia_omega * 2 + )) - def apply(ampl): - return AmplitudeVector(ph2=( - + einsum("ib,ab->ia", ampl.ph2, i1) - - einsum("ij,ja->ia", i2, ampl.ph2) - - einsum("jaib,jb->ia", hf.ovov, ampl.ph2) # 1 - - 0.5 * einsum("ikac,kc->ia", term_t2_eri, ampl.ph2) # 2 - + 2 * omega * ampl.ph2 - )) - else: - diagonal = AmplitudeVector(ph2=( - + direct_sum("a-i->ia", hf.fvv.diagonal(), fCC.diagonal()) # 0 - - einsum("IaIa->Ia", CvCv) # 1 - + intermediates.delta_ia_omega * 2 + def apply(ampl): + return AmplitudeVector(ph2=( # PT order + + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 + - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 + - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 + + 2 * omega * ampl.ph2 )) - - def apply(ampl): - return AmplitudeVector(ph2=( # PT order - + einsum("ib,ab->ia", ampl.ph2, hf.fvv) # 0 - - einsum("IJ,Ja->Ia", fCC, ampl.ph2) # 0 - - einsum("JaIb,Jb->Ia", CvCv, ampl.ph2) # 1 - + 2 * omega * ampl.ph2 - )) + else: + raise NotImplementedError("block_ph_ph_1_phot2 is requested, " + "but ReferenceState has no coupling attribute") return AdcBlock(apply, diagonal) @@ -786,13 +713,9 @@ def apply(ampl): # 2nd order gs blocks # -def block_ph_gs_2(hf, mp, intermediates): - return AdcBlock(lambda ampl: 0, 0) - - block_ph_gs_2_phot_couple = block_ph_gs_2_phot_couple_edge =\ block_ph_gs_2_couple_edge = block_ph_gs_2_phot_couple_inner =\ - block_ph_gs_2 + block_ph_gs_2 = block_ph_gs_0 def block_ph_gs_2_couple(hf, mp, intermediates): @@ -805,21 +728,8 @@ def apply(ampl): return AdcBlock(apply, 0) -def block_ph_gs_2_phot(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - def apply(ampl): - return AmplitudeVector(gs1=(omega * ampl.gs1)) # 0. order - - return AdcBlock(apply, AmplitudeVector(gs1=set_lt_scalar(omega))) - - -def block_ph_gs_2_phot2(hf, mp, intermediates): - omega = float(ReferenceState.get_qed_omega(hf)) - - def apply(ampl): - return AmplitudeVector(gs2=(2 * omega * ampl.gs2)) # 0. order - return AdcBlock(apply, AmplitudeVector(gs2=set_lt_scalar(2 * omega))) +block_ph_gs_2_phot = block_ph_gs_0_phot +block_ph_gs_2_phot2 = block_ph_gs_0_phot2 def block_ph_gs_2_couple_inner(hf, mp, intermediates): @@ -842,7 +752,7 @@ def block_ph_ph_2(hf, mp, intermediates): term_t2_eri = intermediates.term_t2_eri - if hf.qed and not hf.approx: + if hf.is_qed and not hf.approx: omega = float(ReferenceState.get_qed_omega(hf)) if hf.qed_hf: qed_i1 = intermediates.adc2_qed_i1 @@ -946,7 +856,7 @@ def apply(ampl): + sqrt(omega / 2) * ( - einsum("ib,ab->ia", ampl.ph1, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph1) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs1.to_ndarray()) # gs_ph block 1. order + - mp.qed_t1_df(b.ov) * ampl.gs1.to_ndarray()) # 1. order )) return AdcBlock(apply, 0) @@ -1063,7 +973,7 @@ def apply(ampl): + sqrt(omega) * ( - einsum("ib,ab->ia", ampl.ph2, mp.qed_t1_df(b.vv)) # 1. order + einsum("ij,ja->ia", mp.qed_t1_df(b.oo), ampl.ph2) # 1. order - - mp.qed_t1_df(b.ov) * ampl.gs2.to_ndarray()) # gs_ph block # 1. order + - mp.qed_t1_df(b.ov) * ampl.gs2.to_ndarray()) # 1. order )) return AdcBlock(apply, 0) diff --git a/adcc/adc_pp/transition_dm.py b/adcc/adc_pp/transition_dm.py index 241bb38d..8864641c 100644 --- a/adcc/adc_pp/transition_dm.py +++ b/adcc/adc_pp/transition_dm.py @@ -27,7 +27,7 @@ from adcc.AdcMethod import AdcMethod from adcc.functions import einsum from adcc.Intermediates import Intermediates -from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector from adcc.OneParticleOperator import OneParticleOperator from .util import check_doubles_amplitudes, check_singles_amplitudes @@ -132,12 +132,8 @@ def transition_dm(method, ground_state, amplitude, intermediates=None): method = AdcMethod(method) if not isinstance(ground_state, LazyMp): raise TypeError("ground_state should be a LazyMp object.") - if not isinstance(amplitude, (AmplitudeVector, QED_AmplitudeVector)): + if not isinstance(amplitude, AmplitudeVector): raise TypeError("amplitude should be an AmplitudeVector object.") - if isinstance(amplitude, QED_AmplitudeVector): - # TODO: Implement the actual transition densities for the qed methods, - # but for now just return the electric contribution - amplitude = amplitude.elec if intermediates is None: intermediates = Intermediates(ground_state) diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index 2911581b..f8b5e5ec 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -169,20 +169,15 @@ def get_conv_tol(self): def get_restricted(self): if isinstance(self.wfn, (psi4.core.RHF, psi4.core.ROHF)): return True - elif isinstance(self.wfn, (psi4.core.Wavefunction)): - # This is the object returned by the hilbert package, which does + elif isinstance(self.wfn, psi4.core.UHF): + return False + else: + # The hilbert package returns a more basic object, which does # not provide a restricted indicator, so we determine it here - # and print the result orben_a = np.asarray(self.wfn.epsilon_a()) orben_b = np.asarray(self.wfn.epsilon_b()) - if all(orben_a == orben_b): - print("This is a restricted calculation") - return True - else: - print("This is an unrestricted calculation") - return False - else: - return False + # TODO Maybe give a small tolerance here + return all(orben_a == orben_b) def get_energy_scf(self): return self.wfn.energy() diff --git a/adcc/functions.py b/adcc/functions.py index 6dc1ce16..ff036efa 100644 --- a/adcc/functions.py +++ b/adcc/functions.py @@ -24,7 +24,7 @@ import opt_einsum -from .AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from .AmplitudeVector import AmplitudeVector def dot(a, b): @@ -122,26 +122,6 @@ def lincomb(coefficients, tensors, evaluate=False): evaluate=evaluate) for block in tensors[0].blocks_ph }) - elif isinstance(tensors[0], QED_AmplitudeVector): - gs1_part = 0 - gs2_part = 0 - elec_list = [ten.elec for ten in tensors] - phot_list = [ten.phot for ten in tensors] - phot2_list = [ten.phot2 for ten in tensors] - for coeff_ind, ten in enumerate(tensors): - gs1_part += coefficients[coeff_ind] * ten.gs1 - gs2_part += coefficients[coeff_ind] * ten.gs2 - elec_part = lincomb(coefficients, elec_list, evaluate=evaluate) - phot_part = lincomb(coefficients, phot_list, evaluate=evaluate) - phot2_part = lincomb(coefficients, phot2_list, evaluate=evaluate) - if "pphh" in elec_part.blocks_ph: - return QED_AmplitudeVector(elec_part.ph, elec_part.pphh, - gs1_part, phot_part.ph, phot_part.pphh, - gs2_part, phot2_part.ph, phot2_part.pphh) - else: - return QED_AmplitudeVector(elec_part.ph, None, - gs1_part, phot_part.ph, None, - gs2_part, phot2_part.ph, None) elif not isinstance(tensors[0], libadcc.Tensor): raise TypeError("Tensor type not supported") diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index c8cd31f1..8eeb67ac 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -29,6 +29,7 @@ from ..AdcMatrix import AdcMatrixlike from .guess_zero import guess_zero +from ..AmplitudeVector import AmplitudeVector def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, @@ -91,19 +92,16 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, else: raise ValueError(f"Don't know how to generate guesses for block {block}") - diag = matrix.diagonal().copy() + diag = matrix.diagonal() if qed_subblock == "phot": - diag.ph = diag.ph1 - if block == "pphh": - diag.pphh = diag.pphh1 + diag = AmplitudeVector(**{"ph": diag.ph1, "pphh": diag.pphh1}) + #if block == "pphh": + # diag.pphh = diag.pphh1 elif qed_subblock == "phot2": - diag.ph = diag.ph2 - if block == "pphh": - diag.pphh = diag.pphh2 - #else: - # raise KeyError("qed_subblock can only be None, elec, phot or phot2" - # "for guesses from diagonal") + diag = AmplitudeVector(**{"ph": diag.ph2, "pphh": diag.pphh2}) + #if block == "pphh": + # diag.pphh = diag.pphh2 return guessfunction(matrix, n_guesses, diag, spin_change, spin_block_symmetrisation, degeneracy_tolerance, diff --git a/adcc/solver/LanczosIterator.py b/adcc/solver/LanczosIterator.py index a91d48c0..b3193381 100644 --- a/adcc/solver/LanczosIterator.py +++ b/adcc/solver/LanczosIterator.py @@ -26,7 +26,7 @@ from adcc import evaluate, lincomb from adcc.timings import Timer -from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector from .orthogonaliser import GramSchmidtOrthogonaliser @@ -64,7 +64,7 @@ def __init__(self, matrix, guesses, ritz_vectors=None, ritz_values=None, if not isinstance(guesses, list): guesses = [guesses] for guess in guesses: - if not isinstance(guess, (AmplitudeVector, QED_AmplitudeVector)): + if not isinstance(guess, AmplitudeVector): raise TypeError("One of the guesses is not an AmplitudeVector") n_block = len(guesses) # Lanczos block size diff --git a/adcc/solver/davidson.py b/adcc/solver/davidson.py index b1158859..4819de69 100644 --- a/adcc/solver/davidson.py +++ b/adcc/solver/davidson.py @@ -28,7 +28,7 @@ from adcc import evaluate, lincomb from adcc.AdcMatrix import AdcMatrixlike -from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector from .common import select_eigenpairs from .preconditioner import JacobiPreconditioner @@ -358,7 +358,7 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, if not isinstance(matrix, AdcMatrixlike): raise TypeError("matrix is not of type AdcMatrixlike") for guess in guesses: - if not isinstance(guess, (AmplitudeVector, QED_AmplitudeVector)): + if not isinstance(guess, AmplitudeVector): raise TypeError("One of the guesses is not of type AmplitudeVector") if preconditioner is not None and isinstance(preconditioner, type): @@ -375,10 +375,7 @@ def eigsh(matrix, guesses, n_ep=None, max_subspace=None, if not max_subspace: # TODO Arnoldi uses this: # max_subspace = max(2 * n_ep + 1, 20) - if matrix.reference_state.full_diagonalization: - max_subspace = len(guesses) - else: - max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) + max_subspace = max(6 * n_ep, 20, 5 * len(guesses)) def convergence_test(state): state.residuals_converged = state.residual_norms < conv_tol diff --git a/adcc/solver/explicit_symmetrisation.py b/adcc/solver/explicit_symmetrisation.py index 8af52abf..81229482 100644 --- a/adcc/solver/explicit_symmetrisation.py +++ b/adcc/solver/explicit_symmetrisation.py @@ -23,7 +23,7 @@ from libadcc import amplitude_vector_enforce_spin_kind from adcc import evaluate -from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector # TODO # This interface is not that great and leads to duplicate information @@ -49,34 +49,13 @@ def __init__(self, matrix): def symmetrise(self, new_vectors): """ Symmetrise a set of new vectors to be added to the subspace. - new_vectors Vectors to symmetrise (updated in-place) - Returns: The updated new_vectors """ - - def symm_subroutine(vec): - if not isinstance(vec, AmplitudeVector): - raise TypeError("new_vectors has to be an " - "iterable of AmplitudeVector") - for b in vec.blocks_ph: - if b not in self.symmetrisation_functions: - continue - vec[b] = evaluate(self.symmetrisation_functions[b](vec[b])) - return vec - - if isinstance(new_vectors, (AmplitudeVector, QED_AmplitudeVector)): + if isinstance(new_vectors, AmplitudeVector): return self.symmetrise([new_vectors])[0] - elif isinstance(new_vectors[0], QED_AmplitudeVector): - if "pphh" in new_vectors[0].elec.blocks_ph: - for vec in new_vectors: - vec.elec = self.symmetrise([vec.elec])[0] - vec.phot = self.symmetrise([vec.phot])[0] - vec.phot2 = self.symmetrise([vec.phot2])[0] - elif isinstance(new_vectors[0], AmplitudeVector): - for vec in new_vectors: - vec = symm_subroutine(vec) + for vec in new_vectors: if not isinstance(vec, AmplitudeVector): raise TypeError("new_vectors has to be an " "iterable of AmplitudeVector") @@ -97,29 +76,17 @@ def __init__(self, matrix, enforce_spin_kind="singlet"): self.enforce_spin_kind = enforce_spin_kind def symmetrise(self, new_vectors): - if isinstance(new_vectors, (AmplitudeVector, QED_AmplitudeVector)): + if isinstance(new_vectors, AmplitudeVector): return self.symmetrise([new_vectors])[0] - new_vectors = super().symmetrise(new_vectors) # Enforce singlet (or other spin_kind) spin in the doubles block # of all amplitude vectors for vec in new_vectors: - if isinstance(vec, QED_AmplitudeVector): - if "pphh" in vec.elec.blocks_ph: - amplitude_vector_enforce_spin_kind( - vec.elec.pphh, "d", self.enforce_spin_kind - ) - amplitude_vector_enforce_spin_kind( - vec.phot.pphh, "d", self.enforce_spin_kind - ) - amplitude_vector_enforce_spin_kind( - vec.phot2.pphh, "d", self.enforce_spin_kind - ) # Only work on the doubles part # the other blocks are not yet implemented # or nothing needs to be done ("ph" block) - elif "pphh" in vec.blocks_ph: + if "pphh" in vec.blocks_ph: # TODO: Note that the "d" is needed here because the C++ side # does not yet understand ph and pphh amplitude_vector_enforce_spin_kind( diff --git a/adcc/solver/preconditioner.py b/adcc/solver/preconditioner.py index 47c49936..7f55dac6 100644 --- a/adcc/solver/preconditioner.py +++ b/adcc/solver/preconditioner.py @@ -23,7 +23,7 @@ import numpy as np from adcc.AdcMatrix import AdcMatrixlike -from adcc.AmplitudeVector import AmplitudeVector, QED_AmplitudeVector +from adcc.AmplitudeVector import AmplitudeVector class PreconditionerIdentity: @@ -69,12 +69,6 @@ def apply(self, invecs): "to a single vector if shifts is " "only a single number.") return invecs / (self.diagonal - self.shifts) - elif isinstance(invecs, QED_AmplitudeVector): - if not isinstance(self.shifts, (float, np.number)): - raise TypeError("Can only apply JacobiPreconditioner " - "to a single vector if shifts is " - "only a single number.") - return invecs / (self.diagonal - self.shifts) elif isinstance(invecs, list): if len(self.shifts) != len(invecs): raise ValueError("Number of vectors passed does not agree " diff --git a/adcc/test_qed.py b/adcc/test_qed.py index 52d0b138..9da52fe2 100644 --- a/adcc/test_qed.py +++ b/adcc/test_qed.py @@ -55,7 +55,7 @@ def set_refstate(self, case): self.refstate.frequency = [0.0, 0.0, 0.5] self.refstate.freq_with_loss = self.refstate.frequency self.refstate.qed_hf = True - self.refstate.qed = True + self.refstate.is_qed = True def template_approx(self, case, method): self.set_refstate(case) diff --git a/adcc/workflow.py b/adcc/workflow.py index 20ac000e..ca67384a 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -51,7 +51,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, frozen_core=None, frozen_virtual=None, method=None, n_singlets=None, n_triplets=None, n_spin_flip=None, environment=None, coupl=None, freq=None, qed_hf=True, - qed_approx=False, qed_full_diag=False, **solverargs): + qed_approx=False, **solverargs): """Run an ADC calculation. Main entry point to run an ADC calculation. The reference to build the ADC @@ -153,11 +153,6 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, Indicates whether the approximate solution for the qed method should be calculated. (After paper is published, put link here) - qed_full_diag : bool, optional - If the full solution to the qed method is required, the performance - of the standard qed guess is very poor. In that case, this guess - performs much better. - Other parameters ---------------- @@ -205,14 +200,12 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, matrix = construct_adcmatrix( data_or_matrix, core_orbitals=core_orbitals, frozen_core=frozen_core, frozen_virtual=frozen_virtual, method=method, coupl=coupl, - freq=freq, qed_hf=qed_hf, qed_approx=qed_approx, - qed_full_diag=qed_full_diag) + freq=freq, qed_hf=qed_hf, qed_approx=qed_approx) n_states, kind = validate_state_parameters( matrix.reference_state, n_states=n_states, n_singlets=n_singlets, n_triplets=n_triplets, n_spin_flip=n_spin_flip, kind=kind, coupl=coupl, - freq=freq, qed_hf=qed_hf, qed_approx=qed_approx, - qed_full_diag=qed_full_diag) + freq=freq, qed_hf=qed_hf, qed_approx=qed_approx) # Determine spin change during excitation. If guesses is not None, # i.e. user-provided, we cannot guarantee for obtaining a particular @@ -244,11 +237,10 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, exstates += env_energy_corrections # Build QED (approximated) matrix from "standard" ADC matrix - # and expectation values, if requested. - + # and expectation values, if requested if matrix.reference_state.approx: qed_matrix = qed_matrix_from_diag_adc(exstates, matrix.reference_state) - if method == "adc2" and not hasattr(matrix.reference_state, "first_order_coupling"): # noqa: E501 + if method == "adc2": qed_eigvals, qed_eigvecs = qed_matrix.second_order_coupling() else: qed_eigvals, qed_eigvecs = qed_matrix.first_order_coupling() @@ -269,8 +261,7 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, # def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, frozen_virtual=None, method=None, coupl=None, - freq=None, qed_hf=True, qed_approx=False, - qed_full_diag=False): + freq=None, qed_hf=True, qed_approx=False): """ Use the provided data or AdcMatrix object to check consistency of the other passed parameters and construct the AdcMatrix object representing @@ -294,16 +285,16 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, "core_orbitals.") try: if coupl is not None: - qed = True + is_qed = True else: - qed = False + is_qed = False refstate = adcc_ReferenceState(data_or_matrix, core_orbitals=core_orbitals, frozen_core=frozen_core, frozen_virtual=frozen_virtual, - qed=qed, coupl=coupl, qed_hf=qed_hf, - freq=freq, qed_approx=qed_approx, - qed_full_diag=qed_full_diag) + is_qed=is_qed, coupl=coupl, + qed_hf=qed_hf, freq=freq, + qed_approx=qed_approx) except ValueError as e: raise InputError(str(e)) # In case of an issue with the spaces data_or_matrix = refstate @@ -343,7 +334,7 @@ def construct_adcmatrix(data_or_matrix, core_orbitals=None, frozen_core=None, def validate_state_parameters(reference_state, n_states=None, n_singlets=None, n_triplets=None, n_spin_flip=None, kind="any", coupl=None, freq=None, qed_hf=True, - qed_approx=False, qed_full_diag=False): + qed_approx=False): """ Check the passed state parameters for consistency with itself and with the passed reference and normalise them. In the end return the number of @@ -423,9 +414,6 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, if not qed_hf: raise InputError("QED-ADC of zeroth and first level are not yet " "properly tested and second order is not implemented") - if qed_full_diag: - raise Warning("Care to only request qed_full_diag=True, if you ask for " - "the maximum number of states possible") return n_states, kind @@ -477,12 +465,11 @@ def diagonalise_adcmatrix(matrix, n_states, kind, eigensolver="davidson", if guesses is None: if n_guesses is None: n_guesses = estimate_n_guesses(matrix, n_states, n_guesses_per_state) - if matrix.reference_state.qed and not matrix.reference_state.approx: - guesses = obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, - n_guesses_doubles) + if matrix.reference_state.is_qed and not matrix.reference_state.approx: + guess_function = obtain_guesses_by_inspection_qed else: - guesses = obtain_guesses_by_inspection(matrix, n_guesses, kind, - n_guesses_doubles) + guess_function = obtain_guesses_by_inspection + guesses = guess_function(matrix, n_guesses, kind, n_guesses_doubles) else: if len(guesses) < n_states: raise InputError("Less guesses provided via guesses (== {}) " @@ -583,7 +570,7 @@ def obtain_guesses_by_inspection(matrix, n_guesses, kind, n_guesses_doubles=None def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles=None): # noqa: E501 """ - Obtain guesses for QED_AmplitudeVectors, by pushing the subblocks + Obtain guesses for full qed method, by pushing the subblocks into obtain_guesses_by_inspection. Internal function called from run_adc. """ @@ -593,28 +580,21 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot") guesses_phot2 = obtain_guesses_by_inspection( matrix, n_guesses, kind, n_guesses_doubles, qed_subblock="phot2") - n_guess = len(guesses_elec) - #omega = matrix.reference_state.get_qed_omega() - #guesses_phot = guesses_elec.ph.copy() + omega - #guesses_phot2 = guesses_elec.ph.copy() + 2 * omega - # Usually only few states are requested and most of them are close # to pure electronic states, so we initialize the guess vectors # as almost purely electric guesses. - if not matrix.reference_state.full_diagonalization: - for i in np.arange(n_guess): - # TODO: maybe make these values accessible by a keyword, since - # they can tune the performance. From my experience these work - # very well though - guesses_phot[i] *= 0.02 - guesses_phot2[i] *= 0.001 - if n_guess != len(guesses_phot): + for i in np.arange(n_guesses): + # TODO: maybe make these values accessible by a keyword, since + # they can tune the performance. From my experience these work + # very well though + guesses_phot[i] *= 0.02 + guesses_phot2[i] *= 0.001 + if n_guesses != len(guesses_phot): raise InputError("amount of guesses for electronic and photonic must be " "equal, but are {} electronic and {} photonic " "guesses".format(len(guesses_elec), len(guesses_phot))) - #final_guesses = [] zero = set_lt_scalar(0.0) if hasattr(guesses_elec[0], "pphh"): @@ -625,73 +605,26 @@ def obtain_guesses_by_inspection_qed(matrix, n_guesses, kind, n_guesses_doubles= "pphh1": guesses_phot[guess_index].pphh, "gs2": zero.copy(), "ph2": guesses_phot2[guess_index].ph, "pphh2": guesses_phot2[guess_index].pphh - }) for guess_index in np.arange(n_guess)] + }) for guess_index in np.arange(n_guesses)] else: final_guesses = [AmplitudeVector(**{ "ph": guesses_elec[guess_index].ph, "gs1": zero.copy(), "ph1": guesses_phot[guess_index].ph, "gs2": zero.copy(), "ph2": guesses_phot2[guess_index].ph - }) for guess_index in np.arange(n_guess)] - - #for vec in final_guesses: - # vec.gs1.set_from_ndarray(np.array([0])) - # vec.gs2.set_from_ndarray(np.array([0])) - """ - if matrix.reference_state.full_diagonalization: - full_guess = [] - - for qed_vec in guesses_tmp: - tmp_elec_vec = qed_vec.elec.copy() * 10 - tmp_phot_vec = qed_vec.phot.copy() * 10 - tmp_phot2_vec = qed_vec.phot2.copy() * 10 - - tmp_zero_vec = qed_vec.elec.zeros_like() - - if "pphh" in matrix.axis_blocks: - full_guess.append(QED_AmplitudeVector( - tmp_elec_vec.ph, tmp_elec_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) - full_guess.append(QED_AmplitudeVector( - tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_phot_vec.ph, tmp_phot_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh)) - full_guess.append(QED_AmplitudeVector( - tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_zero_vec.ph, tmp_zero_vec.pphh, - 0, tmp_phot2_vec.ph, tmp_phot2_vec.pphh)) - else: - full_guess.append(QED_AmplitudeVector( - tmp_elec_vec.ph, None, - 0, tmp_zero_vec.ph, None, - 0, tmp_zero_vec.ph, None)) - full_guess.append(QED_AmplitudeVector( - tmp_zero_vec.ph, None, - 0, tmp_phot_vec.ph, None, - 0, tmp_zero_vec.ph, None)) - full_guess.append(QED_AmplitudeVector( - tmp_zero_vec.ph, None, - 0, tmp_zero_vec.ph, None, - 0, tmp_phot2_vec.ph, None)) - - full_guess.append(guesses_tmp[0].zeros_like()) - full_guess.append(guesses_tmp[0].zeros_like()) - - final_guesses = full_guess - else: - final_guesses = guesses_tmp - """ + }) for guess_index in np.arange(n_guesses)] # TODO: maybe make these values accessible by a keyword, since # they can tune the performance. From my experience these work - # very well though, but depending on how strong a state couples + # quite well though, but depending on how strong a state couples # to the single photon dispersion mode and how many states one - # requests, adjusting these values can significantly increase the + # requests, adjusting these values can increase the # convergence rate - final_guesses[len(final_guesses) - 2].gs1.set_from_ndarray(np.array([5])) # for stronger coupling e.g. 2 - final_guesses[len(final_guesses) - 1].gs2.set_from_ndarray(np.array([20])) # for stronger coupling e.g. 5 + # for stronger coupling e.g. 2 + final_guesses[n_guesses - 2].gs1.set_from_ndarray(np.array([5])) + # for stronger coupling e.g. 5 + final_guesses[n_guesses - 1].gs2.set_from_ndarray(np.array([20])) - return [vec / (np.sqrt(vec @ vec)) for vec in final_guesses] + return [vec / np.sqrt(vec @ vec) for vec in final_guesses] def setup_solver_printing(solmethod_name, matrix, kind, default_print, From f9771b164fae5bd207ba36cc5049fadb60e0eba7 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Fri, 28 Oct 2022 12:58:30 +0200 Subject: [PATCH 61/64] further cleanup --- adcc/AdcMatrix.py | 3 +++ adcc/ElectronicTransition.py | 14 ++++++-------- adcc/LazyMp.py | 5 +---- adcc/guess/guesses_from_diagonal.py | 13 ++++++------- adcc/workflow.py | 6 +++--- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/adcc/AdcMatrix.py b/adcc/AdcMatrix.py index ab6f10c0..6776f81b 100644 --- a/adcc/AdcMatrix.py +++ b/adcc/AdcMatrix.py @@ -132,6 +132,9 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None, if method.base_method.name in ["adc2x", "adc3"] or self.is_core_valence_separated: # noqa: E501 raise NotImplementedError("Neither adc2x and adc3 nor cvs methods " "are implemented for QED-ADC") + elif method.base_method.name == "adc2" and not self.reference_state.qed_hf: # noqa: E501 + raise NotImplementedError("QED-ADC(2) is only available for a " + "QED-HF reference") self.intermediates = intermediates if self.intermediates is None: diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py index 111064c0..059a9604 100644 --- a/adcc/ElectronicTransition.py +++ b/adcc/ElectronicTransition.py @@ -223,16 +223,14 @@ def final_block(name): for j in np.arange(n_states)] for i in np.arange(n_states)]) - block_dict = {} - - block_dict["qed_adc1_off_diag"] = final_block("adc1") + block_dict = {"qed_adc1_off_diag": final_block("adc1")} if self.method.name == "adc2": - block_dict["qed_adc2_diag"] = final_block("qed_adc2_diag") - block_dict["qed_adc2_edge_couple"] = final_block("qed_adc2_edge_couple") # noqa: E501 - block_dict["qed_adc2_edge_phot_couple"] = final_block("qed_adc2_edge_phot_couple") # noqa: E501 - block_dict["qed_adc2_ph_pphh"] = final_block("qed_adc2_ph_pphh") - block_dict["qed_adc2_pphh_ph"] = final_block("qed_adc2_pphh_ph") + keys = ("qed_adc2_diag", "qed_adc2_edge_couple", + "qed_adc2_edge_phot_couple", "qed_adc2_ph_pphh", + "qed_adc2_pphh_ph") + for key in keys: + block_dict[key] = final_block(key) return block_dict else: return ("s2s_dipole_moments_qed are only calculated," diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index 51289c81..50cf5534 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -181,10 +181,7 @@ def density(self, level=2): up to the specified order of perturbation theory """ if level == 1: - if self.reference_state.is_qed: - return self.reference_state.density - else: - return self.reference_state.density + return self.reference_state.density elif level == 2: return self.reference_state.density + self.mp2_diffdm else: diff --git a/adcc/guess/guesses_from_diagonal.py b/adcc/guess/guesses_from_diagonal.py index 8eeb67ac..5ea478b6 100644 --- a/adcc/guess/guesses_from_diagonal.py +++ b/adcc/guess/guesses_from_diagonal.py @@ -29,7 +29,6 @@ from ..AdcMatrix import AdcMatrixlike from .guess_zero import guess_zero -from ..AmplitudeVector import AmplitudeVector def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, @@ -95,13 +94,13 @@ def guesses_from_diagonal(matrix, n_guesses, block="ph", spin_change=0, diag = matrix.diagonal() if qed_subblock == "phot": - diag = AmplitudeVector(**{"ph": diag.ph1, "pphh": diag.pphh1}) - #if block == "pphh": - # diag.pphh = diag.pphh1 + diag.ph = diag.ph1 + if block == "pphh": + diag.pphh = diag.pphh1 elif qed_subblock == "phot2": - diag = AmplitudeVector(**{"ph": diag.ph2, "pphh": diag.pphh2}) - #if block == "pphh": - # diag.pphh = diag.pphh2 + diag.ph = diag.ph2 + if block == "pphh": + diag.pphh = diag.pphh2 return guessfunction(matrix, n_guesses, diag, spin_change, spin_block_symmetrisation, degeneracy_tolerance, diff --git a/adcc/workflow.py b/adcc/workflow.py index ca67384a..e8092f00 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -411,9 +411,9 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, else: raise Warning("polarizations of the cavity photon different from " "z polarization have not been thoroughly tested yet") - if not qed_hf: - raise InputError("QED-ADC of zeroth and first level are not yet " - "properly tested and second order is not implemented") + if not qed_hf and qed_approx: + raise InputError("Approximate QED ADC method is only available for " + "QED-HF reference") return n_states, kind From 9b7b618836fba19a466bb3e9b2f1a863fa138406 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Tue, 21 Feb 2023 12:57:09 +0100 Subject: [PATCH 62/64] corrected qed_t1 denominator --- adcc/LazyMp.py | 7 ++++--- adcc/backends/psi4.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/adcc/LazyMp.py b/adcc/LazyMp.py index 50cf5534..e441cc10 100644 --- a/adcc/LazyMp.py +++ b/adcc/LazyMp.py @@ -52,7 +52,7 @@ def __init__(self, hf): self.get_qed_total_dip.oo = hf.get_qed_total_dip(b.oo) self.get_qed_total_dip.ov = hf.get_qed_total_dip(b.ov) self.get_qed_total_dip.vv = hf.get_qed_total_dip(b.vv) - self.get_qed_omega = hf.get_qed_omega + self.omega = hf.get_qed_omega() def __getattr__(self, attr): # Shortcut some quantities, which are needed most often @@ -191,7 +191,7 @@ def density(self, level=2): @cached_member_function def qed_t1_df(self, space): """ - qed_t1 amplitude times df + qed_t1 amplitude times (df+omega) """ total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) if space == b.oo: @@ -213,7 +213,8 @@ def qed_t1(self, space): if space != b.ov: raise NotImplementedError("qed_t1 term not implemented " f"for space {space}.") - return self.qed_t1_df(b.ov) / self.df(b.ov) + omega = ReferenceState.get_qed_omega(self.reference_state) + return self.qed_t1_df(b.ov) / (self.df(b.ov) + omega) @cached_member_function def qed_t0_df(self, space): diff --git a/adcc/backends/psi4.py b/adcc/backends/psi4.py index f8b5e5ec..b0cf11d1 100644 --- a/adcc/backends/psi4.py +++ b/adcc/backends/psi4.py @@ -267,9 +267,9 @@ def import_scf(wfn): # the actual set of options ... theoretically they could differ scf_type = psi4.core.get_global_option('SCF_TYPE') # CD = Choleski, DF = density-fitting - unsupported_scf_types = ["CD"] + unsupported_scf_types = [] if not isinstance(wfn, psi4.core.Wavefunction): # hilbert package only uses DF - unsupported_scf_types += ["DISK_DF", "MEM_DF"] + unsupported_scf_types += ["DISK_DF", "MEM_DF", "CD"] if scf_type in unsupported_scf_types: raise InvalidReference("Unsupported Psi4 SCF_TYPE, should not be one " f"of {unsupported_scf_types}") From 5bbb3ebe0396cbf3d03e3d87d328ca62ec250735 Mon Sep 17 00:00:00 2001 From: BauerMarco Date: Thu, 14 Dec 2023 17:54:03 +0100 Subject: [PATCH 63/64] arbitrary spatial orientations for polaritonic frequency and coupling are now supported --- adcc/ElectronicTransition.py | 3 +- adcc/ReferenceState.py | 5 +-- adcc/qed_matrix_from_diag_adc.py | 60 +++++++++++++++++++------------- adcc/workflow.py | 7 ---- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/adcc/ElectronicTransition.py b/adcc/ElectronicTransition.py index 059a9604..a147d724 100644 --- a/adcc/ElectronicTransition.py +++ b/adcc/ElectronicTransition.py @@ -219,7 +219,8 @@ def s2s(i, f, s2s_contribution): vec[i], vec[f]) def final_block(name): - return np.array([[product_trace(dipole_integrals[2], s2s(i, j, name)) # noqa: E501 + return np.array([[[product_trace(comp, s2s(i, j, name)) + for comp in dipole_integrals] for j in np.arange(n_states)] for i in np.arange(n_states)]) diff --git a/adcc/ReferenceState.py b/adcc/ReferenceState.py index 7424a4f8..befddf71 100644 --- a/adcc/ReferenceState.py +++ b/adcc/ReferenceState.py @@ -198,8 +198,9 @@ def get_qed_total_dip(self, block): couplings = self.coupling freqs = self.frequency total_dip = OneParticleOperator(self.mospaces, is_symmetric=True) - for coupling, freq, dip in zip(couplings, freqs, dips): - total_dip += coupling * np.sqrt(2 * freq) * dip + for coupling, dip in zip(couplings, dips): + total_dip += coupling * dip + total_dip *= np.sqrt(2 * np.linalg.norm(freqs)) total_dip.evaluate() return total_dip[block] diff --git a/adcc/qed_matrix_from_diag_adc.py b/adcc/qed_matrix_from_diag_adc.py index 037f623a..0a1e744d 100644 --- a/adcc/qed_matrix_from_diag_adc.py +++ b/adcc/qed_matrix_from_diag_adc.py @@ -29,12 +29,20 @@ def __init__(self, exstates, refstate): self.s2s = exstates.s2s_dipole_moments_qed self.tdm = exstates.transition_dipole_moments_qed self.h1 = exstates.qed_second_order_ph_ph_couplings - self.coupl = refstate.coupling[2] - self.freq = refstate.frequency[2] - self.full_freq = refstate.freq_with_loss[2] + self.coupl = np.array(refstate.coupling) + self.freq = np.linalg.norm(refstate.frequency) + self.full_freq = np.linalg.norm(np.real(refstate.freq_with_loss)) +\ + np.linalg.norm(np.imag(refstate.freq_with_loss)) * 1j self.n_adc = len(exstates.excitation_energy) self.exc_en = exstates.excitation_energy + def loop_helper(self, s2s_tensor): + ret = np.array([[self.coupl.dot(s2s) + for s2s in s2s_block] + for s2s_block in s2s_tensor]) + ret *= - np.sqrt(self.freq / 2) * np.sqrt(2 * self.freq) + return ret + def first_order_coupling(self): # build the blocks of the matrix @@ -42,11 +50,12 @@ def first_order_coupling(self): tdm_block = np.empty(self.n_adc) for i, tdm in enumerate(self.tdm): - tdm_block[i] = self.coupl * np.sqrt(2 * self.freq) * tdm[2] + tdm_block[i] = self.coupl.dot(tdm) + tdm_block *= np.sqrt(2 * self.freq) + + s2s_block = self.loop_helper(self.s2s["qed_adc1_off_diag"]) - s2s_block = - np.sqrt(self.freq / 2) * self.coupl *\ - np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] - tdm_block = - np.sqrt(self.freq / 2) * tdm_block + tdm_block *= - np.sqrt(self.freq / 2) if np.iscomplex(self.full_freq): self.freq = self.full_freq @@ -79,42 +88,43 @@ def second_order_coupling(self): qed_adc2_tdm_vec = np.empty(self.n_adc) for i, tdm in enumerate(self.tdm): - qed_adc2_tdm_vec[i] = - np.sqrt(self.freq / 2) * self.coupl *\ - np.sqrt(2 * self.freq) * tdm[2] + qed_adc2_tdm_vec[i] = self.coupl.dot(tdm) + qed_adc2_tdm_vec *= - np.sqrt(self.freq / 2) * np.sqrt(2 * self.freq) # s2s_dipole parts of the ph_ph blocks - qed_adc1_off_diag_block = - np.sqrt(self.freq / 2) * self.coupl *\ - np.sqrt(2 * self.freq) * self.s2s["qed_adc1_off_diag"] + qed_adc1_off_diag_block = self.loop_helper(self.s2s["qed_adc1_off_diag"]) - qed_adc2_diag_block = - np.sqrt(self.freq / 2) * self.coupl *\ - np.sqrt(2 * self.freq) * self.s2s["qed_adc2_diag"] + qed_adc2_diag_block = self.loop_helper(self.s2s["qed_adc2_diag"]) # missing factor from state.s2s_dipole_moments_qed_adc2_diag - # TODO: commit to one way of defining these factors within the approx method + # TODO: commit to one way of defining these factors + # within the approx method qed_adc2_diag_block *= np.sqrt(self.freq / 2) - qed_adc2_edge_block_couple = - np.sqrt(self.freq / 2) * self.coupl *\ - np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_couple"] + qed_adc2_edge_block_couple = self.loop_helper( + self.s2s["qed_adc2_edge_couple"]) # missing factor from state.s2s_dipole_moments_qed_adc2_edge qed_adc2_edge_block_couple *= np.sqrt(self.freq) - qed_adc2_edge_block_phot_couple = - np.sqrt(self.freq / 2) * self.coupl *\ - np.sqrt(2 * self.freq) * self.s2s["qed_adc2_edge_phot_couple"] + qed_adc2_edge_block_phot_couple = self.loop_helper( + self.s2s["qed_adc2_edge_phot_couple"]) # missing factor from state.s2s_dipole_moments_qed_adc2_edge qed_adc2_edge_block_phot_couple *= np.sqrt(self.freq) # s2s_dipole parts of the pphh_ph and ph_pphh blocks - qed_adc2_ph_pphh_couple_block = - np.sqrt(self.freq / 2) * self.coupl *\ - np.sqrt(2 * self.freq) * self.s2s["qed_adc2_ph_pphh"] + qed_adc2_ph_pphh_couple_block = self.loop_helper( + self.s2s["qed_adc2_ph_pphh"]) - qed_adc2_pphh_ph_phot_couple_block = - np.sqrt(self.freq / 2) *\ - self.coupl * np.sqrt(2 * self.freq) * self.s2s["qed_adc2_pphh_ph"] + qed_adc2_pphh_ph_phot_couple_block = self.loop_helper( + self.s2s["qed_adc2_pphh_ph"]) # we still need the H_1 expectation value "as property" - qed_adc2_couple_block = np.sqrt(self.freq / 2) * self.h1["couple"] - qed_adc2_phot_couple_block = np.sqrt(self.freq / 2) * self.h1["phot_couple"] + qed_adc2_couple_block = np.sqrt(self.freq / 2) *\ + self.h1["couple"] + qed_adc2_phot_couple_block = np.sqrt(self.freq / 2) *\ + self.h1["phot_couple"] # build the blocks of the matrix @@ -140,7 +150,7 @@ def second_order_coupling(self): # build the matrix - matrix_1 = np.vstack((elec_block, qed_adc2_tdm_vec.reshape((1, self.n_adc)), + matrix_1 = np.vstack((elec_block, qed_adc2_tdm_vec.reshape((1, self.n_adc)), # noqa: E501 phot_couple_block, np.zeros((1, self.n_adc)), qed_adc2_edge_block_phot_couple)) matrix_2 = np.concatenate((qed_adc2_tdm_vec, np.array([self.freq]), diff --git a/adcc/workflow.py b/adcc/workflow.py index e8092f00..3d1ee91c 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -404,13 +404,6 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, if any(np.iscomplex(freq)) and not qed_approx: raise InputError("imaginary contribution to freq will only be " "processed, if qed_approx=True") - if freq[0] != 0 or freq[1] != 0: - if qed_approx: - raise InputError("only request qed_approx=True with the cavity " - "photon polarized in z direction") - else: - raise Warning("polarizations of the cavity photon different from " - "z polarization have not been thoroughly tested yet") if not qed_hf and qed_approx: raise InputError("Approximate QED ADC method is only available for " "QED-HF reference") From 889e213313c1f0721043bcfb4391c655a0c4a9ad Mon Sep 17 00:00:00 2001 From: Marco Bauer Date: Wed, 28 Feb 2024 15:30:36 +0100 Subject: [PATCH 64/64] make quasi-diabatic matrices accessible from resulting ExcitedStates object. --- adcc/qed_matrix_from_diag_adc.py | 20 +++++++++++--------- adcc/workflow.py | 14 +++++++++++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/adcc/qed_matrix_from_diag_adc.py b/adcc/qed_matrix_from_diag_adc.py index 0a1e744d..d3830049 100644 --- a/adcc/qed_matrix_from_diag_adc.py +++ b/adcc/qed_matrix_from_diag_adc.py @@ -21,7 +21,7 @@ ## ## --------------------------------------------------------------------- import numpy as np -import scipy.linalg as sp +#import scipy.linalg as sp class qed_matrix_from_diag_adc: @@ -76,10 +76,11 @@ def first_order_coupling(self): matrix_middle.reshape((len(matrix_middle), 1)), matrix_lower)) - if np.iscomplex(self.full_freq): - return sp.eig(matrix) - else: - return sp.eigh(matrix) + #if np.iscomplex(self.full_freq): + # return sp.eig(matrix) + #else: + # return sp.eigh(matrix) + return matrix def second_order_coupling(self): @@ -170,7 +171,8 @@ def second_order_coupling(self): matrix_3, matrix_4.reshape((len(matrix_4), 1)), matrix_5)) - if np.iscomplex(self.full_freq): - return sp.eig(matrix) - else: - return sp.eigh(matrix) + #if np.iscomplex(self.full_freq): + # return sp.eig(matrix) + #else: + # return sp.eigh(matrix) + return matrix diff --git a/adcc/workflow.py b/adcc/workflow.py index 3d1ee91c..b672a90d 100644 --- a/adcc/workflow.py +++ b/adcc/workflow.py @@ -23,6 +23,7 @@ import sys import warnings import numpy as np +import scipy.linalg as sp from libadcc import ReferenceState, set_lt_scalar @@ -241,15 +242,22 @@ def run_adc(data_or_matrix, n_states=None, kind="any", conv_tol=None, if matrix.reference_state.approx: qed_matrix = qed_matrix_from_diag_adc(exstates, matrix.reference_state) if method == "adc2": - qed_eigvals, qed_eigvecs = qed_matrix.second_order_coupling() + #qed_eigvals, qed_eigvecs = qed_matrix.second_order_coupling() + qed_approx_matrix = qed_matrix.second_order_coupling() else: - qed_eigvals, qed_eigvecs = qed_matrix.first_order_coupling() + #qed_eigvals, qed_eigvecs = qed_matrix.first_order_coupling() + qed_approx_matrix = qed_matrix.first_order_coupling() # TODO: This is a bad solution, but filtering out the final excitation # vectors and properly feeding them back into the corresponding libtensor is # usually just unnecessary, especially since consistent qed properties # are not implemented yet. This way the user is at least provided with # the ADC result without polaritonic coupling between the states and can # then request the energies and vectors from the exstates object. + exstates.qed_approx_matrix = qed_approx_matrix + if any(np.iscomplex(freq)): + qed_eigvals, qed_eigvecs = sp.eig(qed_approx_matrix) + else: + qed_eigvals, qed_eigvecs = sp.eigh(qed_approx_matrix) exstates.qed_excitation_energy = qed_eigvals exstates.qed_excitation_vector = qed_eigvecs @@ -398,7 +406,7 @@ def validate_state_parameters(reference_state, n_states=None, n_singlets=None, if coupl is not None or freq is not None: if not (coupl is not None and freq is not None): raise InputError("qed calculation requires coupl and freq") - if len(coupl) != 3 or len(freq) != 3: + if len(coupl) != 3: # or len(freq) != 3: raise InputError("freq and coupl must contain 3 elements, " "i.e. x, y, z") if any(np.iscomplex(freq)) and not qed_approx: