# This Source Code Form is subject to the terms of the Mozilla Public# License, v. 2.0. If a copy of the MPL was not distributed with this# file, You can obtain one at https://mozilla.org/MPL/2.0/.from__future__importannotationsfromtypingimportAny,Optionalimportnumpy.typingasnptfromsisl._internalimportset_modulefrom.baseimportBaseHistoryWeightMixer,T__all__=["LinearMixer","AndersonMixer"]@set_module("sisl.mixing")classLinearMixer(BaseHistoryWeightMixer):r"""Linear mixing The linear mixing is solely defined using a weight, and the resulting functional may then be calculated via: .. math:: \mathbf f^{i+1} = \mathbf f^i + w \delta \mathbf f^i Parameters ---------- weight : float, optional mixing weight """__slots__=()
[docs]def__call__(self,f:T,df:T,append:bool=True)->T:r"""Calculate a new variable :math:`\mathbf f'` using input and output of the functional Parameters ---------- f : object input variable for the functional df : object derivative of the functional append : bool, optional whether to append to the history """super().__call__(f,df,append=append)returnf+self.weight*df
[docs]classAndersonMixer(BaseHistoryWeightMixer):r""" Anderson mixing The Anderson mixing assumes that the mixed input/output are linearly related. Hence .. math:: |\bar{n}^{m}_{\mathrm{in}/\mathrm{out}\rangle = (1 - \beta)|n^{m}_{\mathrm{in}/\mathrm{out}\rangle + \beta|n^{m-1}_{\mathrm{in}/\mathrm{out}\rangle Here the optimal choice :math:`\beta` is calculated as: .. math:: \boldsymbol\delta_i &= \mathbf f_i^{\mathrm{out}} - \mathbf f_i^{\mathrm{in}} \\ \beta &= \frac{\langle \boldsymbol\delta_i | \boldsymbol\delta_i - \boldsymbol\delta_{i-1}\rangle} {\langle \boldsymbol\delta_i - \boldsymbol\delta_{i-1}| \boldsymbol\delta_i - \boldsymbol\delta_{i-1} \rangle} Finally the resulting output becomes: .. math:: |n^{m+1}\rangle = (1 - \alpha)|\bar n^m_{\mathrm{in}}\rangle + \alpha|\bar n^m_{\mathrm{out}}\rangle See :cite:`Johnson1988` for more details. """__slots__=()@staticmethoddef_beta(df1:T,df2:T)->npt.NDArray:# Minimize the average densities for the delta variabledefmetric(a,b):returna.ravel().conj().dot(b.ravel()).realddf=df2-df1beta=metric(df2,ddf)/metric(ddf,ddf)returnbeta
[docs]def__call__(self,f:T,df:T,delta:Optional[Any]=None,append:bool=True)->T:r"""Calculate a new variable :math:`\mathbf f'` using input and output of the functional Parameters ---------- f : object input variable for the functional df : object derivative of the functional """ifdeltaisNone:# not a copy, simply the same referencedelta=df# Get last elementsiflen(self.history)>0:last=self.history[-1]f1=last[0]fdf1=last[1]d1=last[-1]else:f1=None# the current iterations input + output variablesf2=ffdf2=f+dfd2=delta# store new last variables# delta is used for calculating beta, nothing more# here n refers to the variable (density) we are mixing# and the integer corresponds to the iteration countsuper().__call__(f2,fdf2,d2,append=append)iff1isNone:# this is linear mixing for the first stepreturnf+self.weight*df# calculate next positionbeta=self._beta(d1,d2)# Now calculate the new averagesnin=(1-beta)*f2+beta*f1nout=(1-beta)*fdf2+beta*fdf1return(1-self.weight)*nin+self.weight*nout