K i^cdZddlmZddlmZddlmZddlmZddl m Z ddl m Z ddl mZdd lmZdd lmZdd lmZdd lmZdd lmZddlmZmZddlmZddlmZddl m!Z!ddl"m#Z#ddl$m%Z%ddl&m'Z'ddl(m)Z)gdZ*Gdde+Z,Gdde,Z-Gdde,Z.Gdde,Z/Gdd e,Z0Gd!d"e,Z1Gd#d$e Z2Gd%d&e Z3Gd'd(e3Z4Gd)d*e Z5Gd+d,e5Z6Gd-d.e5Z7Gd/d0e5Z8Gd1d2e6e7Z9Gd3d4e6e8Z:e9Z;e:Z<Gd5d6e5Z=Gd7d8e=e7Z>Gd9d:e=e8Z?e?Z@e>ZAGd;deBZCGd?d@eBZDGdAdBeBZEGdCdDeBZFGdEdFeCeEZGGdGdHeCeFZHGdIdJeDeEZIGdKdLeDeFZJeHZKeGZLeJZMeIZNdMZOdNZPGdOdPeZQdQZRGdRdSZSGdTdUZTGdVdWeSZUGdXdYeZVGdZd[e ZWd\ZXd]ZYd^ZZeYfd_Z[d`Z\daifdbZ]Gdcdde'Z^deZ_dqdfZ`dgZaGdhdiZbebdjZcedqdkZddlZeGdmdne ZfdoZgyp)rz Second quantization operators and states for bosons. This follow the formulation of Fetter and Welecka, "Quantum Theory of Many-Particle Systems." ) defaultdict)Add)Basic)cacheit)Tuple)Expr)Function)Mul)I)Pow)Sdefault_sort_key)DummySymbol)sympify) conjugate)sqrt)KroneckerDelta)zeros) StrPrinter)has_dups)%DaggerrBosonicOperatorAnnihilateBoson CreateBosonAnnihilateFermion CreateFermion FockState FockStateBra FockStateKetFockStateBosonKetFockStateBosonBraFockStateFermionKetFockStateFermionBraBBraBKetFBraFKetFFdBBdapply_operators InnerProduct BosonicBasisVarBosonicBasisFixedBosonicBasis Commutator matrix_rep contractionwicksNOevaluate_deltasAntiSymmetricTensorsubstitute_dummiesPermutationOperatorsimplify_index_permutationsc eZdZy)SecondQuantizationErrorN__name__ __module__ __qualname___/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/sympy/physics/secondquant.pyr>r>FrDr>c eZdZy)AppliesOnlyToSymbolicIndexNr?rCrDrErHrHJrFrDrHc eZdZy) ContractionAppliesOnlyToFermionsNr?rCrDrErJrJNrFrDrJc eZdZy)ViolationOfPauliPrincipleNr?rCrDrErLrLRrFrDrLc eZdZy)$SubstitutionOfAmbigousOperatorFailedNr?rCrDrErNrNVrFrDrNc eZdZy)WicksTheoremDoesNotApplyNr?rCrDrErPrPZrFrDrPc,eZdZdZdZedZdZy)ra( Hermitian conjugate of creation/annihilation operators. Examples ======== >>> from sympy import I >>> from sympy.physics.secondquant import Dagger, B, Bd >>> Dagger(2*I) -2*I >>> Dagger(B(0)) CreateBoson(0) >>> Dagger(Bd(0)) AnnihilateBoson(0) ct|}|j|}t|tr|Stj||}|SN)reval isinstancer__new__)clsargrobjs rErVzDagger.__new__ps<cl HHSM a HmmC% rDc t|dd}||St|tr|jr t |St|t r|j r)tttt|jS|jr2ttttt|jS|jr|S|j r/t#t|jd|jdS|t$k(r| St|t&rMt)d|jDr0|j*|jDcgc] }t|c}Syycc}w)as Evaluates the Dagger instance. Examples ======== >>> from sympy import I >>> from sympy.physics.secondquant import Dagger, B, Bd >>> Dagger(2*I) -2*I >>> Dagger(B(0)) CreateBoson(0) >>> Dagger(Bd(0)) AnnihilateBoson(0) The eval() method is called automatically. _dagger_Nrc34K|]}|jywrS)is_commutative).0as rE zDagger.eval..s61##6s)getattrrUrr_rris_Addrtuplemaprargsis_Mulr reversed is_Numberis_Powr r r allfunc)rWrXdaggerras rErTz Dagger.evalxs (j$/  8O c6 "s'9'9S> ! c5 !zzE#fchh"7899zzE#fhsxx.@"ABCC}} zz6#((1+. <<axt c8 $6SXX66sxxSXX!>&)!>??7"?sE4c |jdSNrrgselfs rEr\zDagger._dagger_yy|rDN)r@rArB__doc__rV classmethodrTr\rCrDrErr^s&"''RrDrceZdZdZy) TensorSymbolTN)r@rArBr_rCrDrErxrxsNrDrxcReZdZdZdZdZedZedZedZ dZ y) r9a\Stores upper and lower indices in separate Tuple's. Each group of indices is assumed to be antisymmetric. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import AntiSymmetricTensor >>> i, j = symbols('i j', below_fermi=True) >>> a, b = symbols('a b', above_fermi=True) >>> AntiSymmetricTensor('v', (a, i), (b, j)) AntiSymmetricTensor(v, (a, i), (b, j)) >>> AntiSymmetricTensor('v', (i, a), (b, j)) -AntiSymmetricTensor(v, (a, i), (b, j)) As you can see, the indices are automatically sorted to a canonical form. c: t|t\}}t|t\}}t |}t |}t |}||zdzrtj|||| Stj||||S#t$rtjcYSwxYw)Nkey) _sort_anticommuting_fermions _sqkey_indexrLr ZerorrrxrV)rWsymbolupperlowersignusignls rErVzAntiSymmetricTensor.__new__s 7<)LE57<)LE5 u u  EMQ  ((feUCC C ''VUEB B) 66M s(A>>BBc &d|jddj|jdDcgc]}|j|c}ddj|jdDcgc]}|j|c}dScc}wcc}w)N{z^{r]z}_{r}z}})rjoinrg_print)rsprinteris rE_latexzAntiSymmetricTensor._latexsi KK GG1>AgnnQ'> ? GG1>AgnnQ'> ?  >>s B 'Bc |jdS)a Returns the symbol of the tensor. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import AntiSymmetricTensor >>> i, j = symbols('i,j', below_fermi=True) >>> a, b = symbols('a,b', above_fermi=True) >>> AntiSymmetricTensor('v', (a, i), (b, j)) AntiSymmetricTensor(v, (a, i), (b, j)) >>> AntiSymmetricTensor('v', (a, i), (b, j)).symbol v rrqrrs rErzAntiSymmetricTensor.symbol$yy|rDc |jdS)a Returns the upper indices. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import AntiSymmetricTensor >>> i, j = symbols('i,j', below_fermi=True) >>> a, b = symbols('a,b', above_fermi=True) >>> AntiSymmetricTensor('v', (a, i), (b, j)) AntiSymmetricTensor(v, (a, i), (b, j)) >>> AntiSymmetricTensor('v', (a, i), (b, j)).upper (a, i) r]rqrrs rErzAntiSymmetricTensor.uppers&yy|rDc |jdS)a Returns the lower indices. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import AntiSymmetricTensor >>> i, j = symbols('i,j', below_fermi=True) >>> a, b = symbols('a,b', above_fermi=True) >>> AntiSymmetricTensor('v', (a, i), (b, j)) AntiSymmetricTensor(v, (a, i), (b, j)) >>> AntiSymmetricTensor('v', (a, i), (b, j)).lower (b, j) r}rqrrs rErzAntiSymmetricTensor.lowerrrDc d|jzS)Nz %s(%s,%s)rqrrs rE__str__zAntiSymmetricTensor.__str__sTYY&&rDN) r@rArBrurVrpropertyrrrrrCrDrEr9r9sS(C* &(&'rDr9cPeZdZdZdZdZdZedZedZ dZ dZ d Z y ) SqOperatorz7 Base class for Second Quantization operators. sqFcDtj|t|}|SrS)rrVr)rWkrZs rErVzSqOperator.__new__&smmC, rDc |jdS)an Returns the state index related to this operator. Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F, Fd, B, Bd >>> p = Symbol('p') >>> F(p).state p >>> Fd(p).state p >>> B(p).state p >>> Bd(p).state p rrqrrs rEstatezSqOperator.state*s*yy|rDc2|jjryy)a@ Returns True if the state is a symbol (as opposed to a number). Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> p = Symbol('p') >>> F(p).is_symbolic True >>> F(1).is_symbolic False FT)r is_Integerrrs rE is_symboliczSqOperator.is_symbolicAs" :: rDctSrS)NotImplementedrrs rE__repr__zSqOperator.__repr__WsrDc:|jd|jdS)N()) op_symbolrrrs rErzSqOperator.__str__Zs>>4::66rDctd)z0 Applies an operator to itself. z&implement apply_operator in a subclass)NotImplementedErrorrsrs rEapply_operatorzSqOperator.apply_operator]s""JKKrDN) r@rArBrurr_rVrrrrrrrCrDrErrsPIN,*7LrDrc eZdZy)rNr?rCrDrErrdrFrDrc eZdZy) AnnihilatorNr?rCrDrErrhrFrDrc eZdZy)CreatorNr?rCrDrErrlrFrDrc,eZdZdZdZdZdZdZdZy)rz Bosonic annihilation operator. Examples ======== >>> from sympy.physics.secondquant import B >>> from sympy.abc import x >>> B(x) AnnihilateBoson(x) bc,t|jSrS)rrrrs rEr\zAnnihilateBoson._dagger_s4::&&rDc|js>t|tr.|j}t ||}||j |zSt ||S)a Apply state to self if self is not symbolic and state is a FockStateKet, else multiply self by state. Examples ======== >>> from sympy.physics.secondquant import B, BKet >>> from sympy.abc import x, y, n >>> B(x).apply_operator(y) y*AnnihilateBoson(x) >>> B(0).apply_operator(BKet((n,))) sqrt(n)*FockStateBosonKet((n - 1,)) )rrUr!rrdownr rsrelementamps rErzAnnihilateBoson.apply_operatorsN Jul$CjjGuW~&Cuzz'** *tU# #rDc d|jzS)NzAnnihilateBoson(%s)rrrs rErzAnnihilateBoson.__repr__s$tzz11rDcx|jtjuryd|j|jzS)Nzb_{0}zb_{%s}rr rrrsrs rErzAnnihilateBoson._latex- :: gnnTZZ88 8rDN r@rArBrurr\rrrrCrDrErrps" I'$.29rDrc,eZdZdZdZdZdZdZdZy)rz$ Bosonic creation operator. zb+c,t|jSrS)rrrrs rEr\zCreateBoson._dagger_stzz**rDc|jsAt|tr1|j}t ||dz}||j |zSt ||S) Apply state to self if self is not symbolic and state is a FockStateKet, else multiply self by state. Examples ======== >>> from sympy.physics.secondquant import B, Dagger, BKet >>> from sympy.abc import x, y, n >>> Dagger(B(x)).apply_operator(y) y*CreateBoson(x) >>> B(0).apply_operator(BKet((n,))) sqrt(n)*FockStateBosonKet((n - 1,)) r])rrUr!rrupr rs rErzCreateBoson.apply_operatorsSJul$CjjGuW~)*Cuxx(( (tU# #rDc d|jzS)NzCreateBoson(%s)rrrs rErzCreateBoson.__repr__s 4::--rDcx|jtjuryd|j|jzS)Nz{b^\dagger_{0}}z{b^\dagger_{%s}}rrs rErzCreateBoson._latex- :: %& )CC CrDNrrCrDrErrs#I+$,.DrDrcbeZdZedZedZedZedZedZdZ y)FermionicOperatorc~|jdj}|jdry|jdryy)a Is this FermionicOperator restricted with respect to fermi level? Returns ======= 1 : restricted to orbits above fermi 0 : no restriction -1 : restricted to orbits below fermi Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F, Fd >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> F(a).is_restricted 1 >>> Fd(a).is_restricted 1 >>> F(i).is_restricted -1 >>> Fd(i).is_restricted -1 >>> F(p).is_restricted 0 >>> Fd(p).is_restricted 0 r below_fermi above_fermir]rg assumptions0get)rsasss rE is_restrictedzFermionicOperator.is_restricteds9Fiil'' 77= ! 77= !rDcT|jdjjd S)a! Does the index of this FermionicOperator allow values above fermi? Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> F(a).is_above_fermi True >>> F(i).is_above_fermi False >>> F(p).is_above_fermi True Note ==== The same applies to creation operators Fd rrrrrs rEis_above_fermiz FermionicOperator.is_above_fermis&699Q<,,00???rDcT|jdjjd S)a Does the index of this FermionicOperator allow values below fermi? Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> F(a).is_below_fermi False >>> F(i).is_below_fermi True >>> F(p).is_below_fermi True The same applies to creation operators Fd rrrrrs rEis_below_fermiz FermionicOperator.is_below_fermis&099Q<,,00???rDc8|jxr |j S)a Is the index of this FermionicOperator restricted to values below fermi? Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> F(a).is_only_below_fermi False >>> F(i).is_only_below_fermi True >>> F(p).is_only_below_fermi False The same applies to creation operators Fd )rrrrs rEis_only_below_fermiz%FermionicOperator.is_only_below_fermi2."">4+>+>'>>rDc8|jxr |j S)a Is the index of this FermionicOperator restricted to values above fermi? Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> F(a).is_only_above_fermi True >>> F(i).is_only_above_fermi False >>> F(p).is_only_above_fermi False The same applies to creation operators Fd )rrrrs rEis_only_above_fermiz%FermionicOperator.is_only_above_fermiKrrDct|}t|jd}|jrd||fS|jrd||fSt |t rd||fSt |trd||fSy)Nrr]r})hashstrrgis_only_q_creatoris_only_q_annihilatorrUrr)rshlabels rE_sortkeyzFermionicOperator._sortkeydsx JDIIaL!  ! !eQ;   % %eQ;  dK (eQ;  dG $eQ;  %rDN) r@rArBrrrrrrrrCrDrErrsn ''R@@8@@2??0??0 rDrcleZdZdZdZdZdZedZedZ edZ edZ d Z d Z y ) rz* Fermionic annihilation operator. fc,t|jSrS)rrrrs rEr\zAnnihilateFermion._dagger_ysTZZ((rDcXt|tr|j}|j|St|trb|j \}}t|dtr0|j}t ||dj|gz|ddzSt ||St ||Srrr]N)rUr$rrr args_cncrsrrc_partnc_parts rErz AnnihilateFermion.apply_operator|s e0 1jjG::g& & s ##nn.OFG'!*&9:**Vwqzw'?&@@712;NPP4''tU# #rDc|jryy)a Can we create a quasi-particle? (create hole or create particle) If so, would that be above or below the fermi surface? Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> F(a).is_q_creator 0 >>> F(i).is_q_creator -1 >>> F(p).is_q_creator -1 rrrrrs rE is_q_creatorzAnnihilateFermion.is_q_creator.   rDc|jryy)a Can we destroy a quasi-particle? (annihilate hole or annihilate particle) If so, would that be above or below the fermi surface? Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a', above_fermi=1) >>> i = Symbol('i', below_fermi=1) >>> p = Symbol('p') >>> F(a).is_q_annihilator 1 >>> F(i).is_q_annihilator 0 >>> F(p).is_q_annihilator 1 r]rrrrs rEis_q_annihilatorz"AnnihilateFermion.is_q_annihilator.   rDc|jS)a Always create a quasi-particle? (create hole or create particle) Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> F(a).is_only_q_creator False >>> F(i).is_only_q_creator True >>> F(p).is_only_q_creator False rrrs rErz#AnnihilateFermion.is_only_q_creator,'''rDc|jS)a Always destroy a quasi-particle? (annihilate hole or annihilate particle) Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> F(a).is_only_q_annihilator True >>> F(i).is_only_q_annihilator False >>> F(p).is_only_q_annihilator False rrrs rErz'AnnihilateFermion.is_only_q_annihilatorrrDc d|jzS)NzAnnihilateFermion(%s)rrrs rErzAnnihilateFermion.__repr__s&33rDcx|jtjuryd|j|jzS)Nza_{0}za_{%s}rrs rErzAnnihilateFermion._latexrrDNr@rArBrurr\rrrrrrrrrCrDrErrrsrI)$<44((.((.49rDrcleZdZdZdZdZdZedZedZ edZ edZ d Z d Z y ) rz& Fermionic creation operator. zf+c,t|jSrS)rrrrs rEr\zCreateFermion._dagger_s ,,rDc@t|tr|j}|j|St|trV|j \}}t|dtr0|j}t ||dj|gz|ddzSt ||Sr)rUr$rrr rrs rErzCreateFermion.apply_operators e0 1jjG88G$ $ s ##nn.OFG'!*&9:**Vwqz}}W'=&>>LNN4rDc|jryy)a Can we create a quasi-particle? (create hole or create particle) If so, would that be above or below the fermi surface? Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import Fd >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> Fd(a).is_q_creator 1 >>> Fd(i).is_q_creator 0 >>> Fd(p).is_q_creator 1 r]rrrrs rErzCreateFermion.is_q_creator/rrDc|jryy)a Can we destroy a quasi-particle? (annihilate hole or annihilate particle) If so, would that be above or below the fermi surface? Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import Fd >>> a = Symbol('a', above_fermi=1) >>> i = Symbol('i', below_fermi=1) >>> p = Symbol('p') >>> Fd(a).is_q_annihilator 0 >>> Fd(i).is_q_annihilator -1 >>> Fd(p).is_q_annihilator -1 rrrrrs rErzCreateFermion.is_q_annihilatorJrrDc|jS)a Always create a quasi-particle? (create hole or create particle) Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import Fd >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> Fd(a).is_only_q_creator True >>> Fd(i).is_only_q_creator False >>> Fd(p).is_only_q_creator False rrrs rErzCreateFermion.is_only_q_creatorerrDc|jS)a Always destroy a quasi-particle? (annihilate hole or annihilate particle) Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import Fd >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> Fd(a).is_only_q_annihilator False >>> Fd(i).is_only_q_annihilator True >>> Fd(p).is_only_q_annihilator False rrrs rErz#CreateFermion.is_only_q_annihilator}rrDc d|jzS)NzCreateFermion(%s)rrrs rErzCreateFermion.__repr__s"TZZ//rDcx|jtjuryd|j|jzS)Nz{a^\dagger_{0}}z{a^\dagger_{%s}}rrs rErzCreateFermion._latexrrDNrrCrDrErr ssI- 644((.((.0DrDrc>eZdZdZdZdZdZdZdZdZ dZ d Z y ) rz Many particle Fock state with a sequence of occupation numbers. Anywhere you can have a FockState, you can also have S.Zero. All code must check for this! Base class to represent FockStates. Fcpttt|}tj|t |}|S)a[ occupations is a list with two possible meanings: - For bosons it is a list of occupation numbers. Element i is the number of particles in state i. - For fermions it is a list of occupied orbits. Element 0 is the state that was occupied first, element i is the i'th occupied state. )listrfrrrVr)rW occupationsrZs rErVzFockState.__new__s/3w 45 mmC !45 rDc<t|}|jd|Srp)intrgrsrs rE __getitem__zFockState.__getitem__s Fyy|ArDc d|jzS)Nz FockState(%r)rqrrs rErzFockState.__repr__sDII..rDcZt|dd|jt|ddS)Nlbracketrrbracket)rc_labelsrrs rErzFockState.__str__s*"4R8$,,.'RVXbdfJghhrDc |jdSrprqrrs rErzFockState._labelsrtrDc2t|jdSrplenrgrrs rE__len__zFockState.__len__s499Q<  rDcxt|dd|j|jt|ddS)Nlbracket_latexrrbracket_latex)rcrrrs rErzFockState._latexsC"4)92>t||~@^`ghln~ACaDE ErDN) r@rArBrur_rVr rrrrrrCrDrErrs3N /i!ErDrceZdZdZdZdZy) BosonStatez1 Base class for FockStateBoson(Ket/Bra). ct|}t|jd}||tjz||<|j |S)a Performs the action of a creation operator. Examples ======== >>> from sympy.physics.secondquant import BBra >>> b = BBra([1, 2]) >>> b FockStateBosonBra((1, 2)) >>> b.up(1) FockStateBosonBra((1, 3)) r)r rrgr One __class__rsrnew_occss rErz BosonState.upsD F ! %qkAEE) ~~h''rDct|}t|jd}||tjk(rtjS||tj z ||<|j |S)a" Performs the action of an annihilation operator. Examples ======== >>> from sympy.physics.secondquant import BBra >>> b = BBra([1, 2]) >>> b FockStateBosonBra((1, 2)) >>> b.down(1) FockStateBosonBra((1, 1)) r)r rrgr rrrr s rErzBosonState.downs] F ! % A;!&& 66M"1+-HQK>>(+ +rDN)r@rArBrurrrCrDrErrs(&,rDrcveZdZdZdZddZdZdZedZ edZ dZ d Z ed Z d Zd Zd Zy) FermionStatez3 Base class for FockStateFermion(Ket/Bra). rcttt|}t|dkDr t |t \}}nd}||_ |j||kDrtjS|dzr'tjtj||zStj||S#t $rtjcYSwxYw)Nr]r{rr})rrfrrr~rrLr r fermi_level _count_holes NegativeOnerrV)rWr r&signs rErVzFermionState.__new__s3w 45 { a  &B\'3#d D%   K (; 666M !8==!2!23 !DD D$$S+6 6- vv  sB--C C c||jdv}|j|r#|rtjS|j |S|j |r#|r|j |StjS|r+tdd}t|||j |zStdd}t|||j |zS)a Performs the action of a creation operator. Explanation =========== If below fermi we try to remove a hole, if above fermi we try to create a particle. If general index p we return ``Kronecker(p,i)*self`` where ``i`` is a new symbol with restriction above or below. Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import FKet >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') >>> FKet([]).up(a) FockStateFermionKet((a,)) A creator acting on vacuum below fermi vanishes >>> FKet([]).up(i) 0 rrTrrar) rg_only_above_fermir r _add_orbit_only_below_fermi _remove_orbitrrrsrpresentholeparticles rErzFermionState.ups@tyy|#  ! !! $vv q))  # #A &))!,,vv Sd3%a.t/A/A!/DDD $7%a24??13EEErDc||jdv}|j|r#|r|j|StjS|j |r#|rtjS|j |S|r+tdd}t|||j |zStdd}t|||j|zS)ag Performs the action of an annihilation operator. Explanation =========== If below fermi we try to create a hole, If above fermi we try to remove a particle. If general index p we return ``Kronecker(p,i)*self`` where ``i`` is a new symbol with restriction above or below. Examples ======== >>> from sympy import Symbol >>> from sympy.physics.secondquant import FKet >>> a = Symbol('a', above_fermi=True) >>> i = Symbol('i', below_fermi=True) >>> p = Symbol('p') An annihilator acting on vacuum above fermi vanishes >>> FKet([]).down(a) 0 Also below fermi, it vanishes, unless we specify a fermi level > 0 >>> FKet([]).down(i) 0 >>> FKet([],4).down(i) FockStateFermionKet((i,)) rrTr+rar,) rgr-r0r rr/r.rrr1s rErzFermionState.downOsFtyy|#  ! !! $))!,,vv  # #A &vv q))Sd3%a.tq/AAA $7%a243E3Ea3HHHrDcr|jr||jkS|jjdryy)z Tests if given orbit is only below fermi surface. If nothing can be concluded we return a conservative False. rTF is_numberr&rrrWrs rEr/zFermionState._only_below_fermis2 ;;' ' >>  m ,rDc|jr||jkDS|jjdry|j S)z Tests if given orbit is only above fermi surface. If fermi level has not been set we return True. If nothing can be concluded we return a conservative False. rTr7r9s rEr-zFermionState._only_above_fermis< ;;s& & >>  m ,??""rDct|jd}|j|}||=|dzr-tj|j ||j zS|j ||j S)zX Removes particle/fills hole in orbit i. No input tests performed here. rr})rrgindexr r(rr&)rsrr!poss rEr0zFermionState._remove_orbitsi ! %nnQ SM 19==$:J:J!KK K>>(D,<,<= =rDc\|j|f|jdz|jS)zW Adds particle/creates hole in orbit i. No input tests performed here. r)rrgr&r s rEr.zFermionState._add_orbits*~~qdTYYq\143C3CDDrDcbt|Dcgc]}|j|s|c}Scc}w)zS Returns the number of identified hole states in occupations list. )rr/)rWr rs rEr'zFermionState._count_holess* {G!c.C.CA.FAGHHGs,,c~t|Dcgc]$}|j|r|jr| n|&c}Scc}w)z Returns the occupations list where states below the fermi level have negative labels. For symbolic state labels, no sign is included. )rer/r8)rsr rs rE _negate_holeszFermionState._negate_holess9 VabQRD2215!++qb1Lbccbs):c|jr d|jdd|jdSd|jddS)Nz FockStateKet(rz, fermi_level=r)r&rgrrs rErzFermionState.__repr__s<   9=1tGWGWX X *.17 7rDc>|j|jdSrp)rArgrrs rErzFermionState._labelss!!$))A,//rDN)r)r@rArBrur&rVrrrvr/r-r0r.r'rArrrCrDrEr$r$szK7*2Fh6Ip   # # >E II d8 0rDr$c eZdZdZdZdZdZdZy)r!z" Representation of a ket. |>z\left|z \right\rangleN)r@rArBrurrrrrCrDrEr!r!sHHN%NrDr!c&eZdZdZdZdZdZdZdZy)r z" Representation of a bra. = 0. Examples ======== >>> from sympy.physics.secondquant import BKet >>> BKet([1, 2]) FockStateBosonKet((1, 2)) c&t|jSrS)r#rgrrs rEr\zFockStateBosonKet._dagger_ $)),,rDNr@rArBrur\rCrDrEr"r"s  -rDr"ceZdZdZdZy)r#z Describes a collection of BosonBra particles. Examples ======== >>> from sympy.physics.secondquant import BBra >>> BBra([1, 2]) FockStateBosonBra((1, 2)) c&t|jSrS)r"rgrrs rEr\zFockStateBosonBra._dagger_rNrDNrOrCrDrEr#r#s  -rDr#ceZdZdZdZy)r$aR Many-particle Fock state with a sequence of occupied orbits. Explanation =========== Each state can only have one particle, so we choose to store a list of occupied orbits rather than a tuple with occupation numbers (zeros and ones). states below fermi level are holes, and are represented by negative labels in the occupation list. For symbolic state labels, the fermi_level caps the number of allowed hole- states. Examples ======== >>> from sympy.physics.secondquant import FKet >>> FKet([1, 2]) FockStateFermionKet((1, 2)) c&t|jSrS)r%rgrrs rEr\zFockStateFermionKet._dagger_"DII..rDNrOrCrDrEr$r$s ,/rDr$ceZdZdZdZy)r%z See Also ======== FockStateFermionKet Examples ======== >>> from sympy.physics.secondquant import FBra >>> FBra([1, 2]) FockStateFermionBra((1, 2)) c&t|jSrS)r$rgrrs rEr\zFockStateFermionBra._dagger_0rTrDNrOrCrDrEr%r%"s  /rDr%ct|ts|S|j\}}t|}|dvr|S|d}|d}t|trut|t rO|j r|S|j|}|dk(rtjStt||ddz|gzSt|trt|jt r|jjr|jj r|S|}t|jD]$}|jj|}|dk(s$n|dk(rtjStt||ddz|gzS|St|t r), they are converted to instances of InnerProduct. This does not currently work on double inner products like, . If the argument is not a Mul, it is simply returned as is. )rr]rrN)rUr rrr!rrrr r _apply_Mulr baseexprranger r/)mrrn_nclast next_to_lastresultrs rErYrY9s" a jjlOFG wF{ vv )#"0E0P*RSSL#.l//<$((33#((44 !%!&|'7'7!8&A%1%6%6%E%Ef%MF%{ %&"Q;#$66M#-cFWSb\4IVH4T.V#WWHL,7%lD9Q;66M%cFWSb\,AVH,L&NOOHrDc|j}|jt}t|Dcgc]}|t |f}}|j |Scc}w)z Take a SymPy expression with operators and states and apply the operators. Examples ======== >>> from sympy.physics.secondquant import apply_operators >>> from sympy import sympify >>> apply_operators(sympify(3)+4) 7 )expandatomsr iterrYsubs)emulsr] subs_lists rEr.r.zsP  A 773 An unevaluated inner product between a bra and ket. Explanation =========== Currently this class just reduces things to a product of Kronecker Deltas. In the future, we could introduce abstract states like ``|a>`` and ``|b>``, and leave the inner product unevaluated as ````. Tct|ts tdt|ts td|j ||S)Nz must be a braz must be a ket)rUr TypeErrorr!rT)rWbrakets rErVzInnerProduct.__new__s=#|,O, ,#|,O, ,xxS!!rDctj}t|jd|jdD]\}}|t ||z}|dk(s|S|Srp)r rziprgr)rWrmrnrarjs rErTzInnerProduct.evals\ SXXa[1 DAq nQ* *F{   rDc |jdS)z!Returns the bra part of the staterrqrrs rErmzInnerProduct.brayy|rDc |jdS)z!Returns the ket part of the stater]rqrrs rErnzInnerProduct.ketrsrDcpt|j}t|j}|ddd|ddS)NrrEr])reprrmrn)rssbraskets rErzInnerProduct.__repr__s3DHH~DHH~s)T!"X..rDc"|jSrS)rrrs rErzInnerProduct.__str__s}}rDN) r@rArBrur_rVrvrTrrmrnrrrCrDrEr/r/sY N"/ rDr/ctt|}tt|D]@}tt|D]'}tt |||z||z|||f<)B|S)a Find the representation of an operator in a basis. Examples ======== >>> from sympy.physics.secondquant import VarBosonicBasis, B, matrix_rep >>> b = VarBosonicBasis(5) >>> o = B(0) >>> matrix_rep(o, b) Matrix([ [0, 1, 0, 0, 0], [0, 0, sqrt(2), 0, 0], [0, 0, 0, sqrt(3), 0], [0, 0, 0, 0, 2], [0, 0, 0, 0, 0]]) )rrr\r.r)opbasisrarrqs rEr4r4sv$ c%jA 3u: Ds5z" DA%fU1X&6r&9%(&BCAadG DD HrDceZdZdZy)r0z< Base class for a basis set of bosonic Fock states. N)r@rArBrurCrDrEr0r0s  rDr0c:eZdZdZdZdZdZdZdZdZ dZ y ) r1a A single state, variable particle number basis set. Examples ======== >>> from sympy.physics.secondquant import VarBosonicBasis >>> b = VarBosonicBasis(5) >>> b [FockState((0,)), FockState((1,)), FockState((2,)), FockState((3,)), FockState((4,))] c2||_|jyrS)n_max _build_states)rsrs rE__init__zVarBosonicBasis.__init__s  rDcg|_t|jD]'}|jjt |g)t |j|_yrS)r|r\rappendr"rn_basisr s rErzVarBosonicBasis._build_statessJ tzz" 6A JJ  /4 5 64:: rDc8|jj|S)a Returns the index of state in basis. Examples ======== >>> from sympy.physics.secondquant import VarBosonicBasis >>> b = VarBosonicBasis(3) >>> state = b.state(1) >>> b [FockState((0,)), FockState((1,)), FockState((2,))] >>> state FockStateBosonKet((1,)) >>> b.index(state) 1 r|r<rs rEr<zVarBosonicBasis.indexs"zz&&rDc |j|S)z The state of a single basis. Examples ======== >>> from sympy.physics.secondquant import VarBosonicBasis >>> b = VarBosonicBasis(5) >>> b.state(3) FockStateBosonKet((3,)) r|r s rErzVarBosonicBasis.state szz!}rDc$|j|SrSrr s rEr zVarBosonicBasis.__getitem__zz!}rDc,t|jSrSrr|rrs rErzVarBosonicBasis.__len__4::rDc,t|jSrSrvr|rrs rErzVarBosonicBasis.__repr__DJJrDN) r@rArBrurrr<rr rrrCrDrEr1r1s* ' '&  rDr1c@eZdZdZdZdZdZdZdZdZ dZ d Z y ) r2aV Fixed particle number basis set. Examples ======== >>> from sympy.physics.secondquant import FixedBosonicBasis >>> b = FixedBosonicBasis(2, 2) >>> state = b.state(1) >>> b [FockState((2, 0)), FockState((1, 1)), FockState((0, 2))] >>> state FockStateBosonKet((1, 1)) >>> b.index(state) 1 c`||_||_|j|jyrS) n_particlesn_levels_build_particle_locationsr)rsrrs rErzFixedBosonicBasis.__init__2s)&   &&( rDcpt|jDcgc]}d|z }}d|jz}d}t|dd|D]\}}d|d|d}||z}dd j |z}d |d |d |d } t | } |jdk(r| D cgc]} | f} } | |_ycc}wcc} w) Nzi%izfor i0 in range(%i)rr]zfor z in range(z + 1) z(%s)z, [ ])r\rrrprrTparticle_locations) rsrtup first_loop other_loopscurprevtemp tup_string list_compraitems rErz+FixedBosonicBasis._build_particle_locations8s"'(8(8"9:Quqy::*T]]:  SWc* -IC14d;D%,K -diin, $. KH i   q *01$tg1F1"(;2s B. B3cg|_|jD]J}|jdgz}|D]}||xxdz cc<|jjt |Lt |j|_y)Nrr])r|rrrr"rr)rstuple_of_indices occ_numberslevels rErzFixedBosonicBasis._build_statesFsw $ 7 7 > --+K) (E"a'" ( JJ  / < =  > 4:: rDc8|jj|S)zReturns the index of state in basis. Examples ======== >>> from sympy.physics.secondquant import FixedBosonicBasis >>> b = FixedBosonicBasis(2, 3) >>> b.index(b.state(3)) 3 rrs rEr<zFixedBosonicBasis.indexOszz&&rDc |j|S)aReturns the state that lies at index i of the basis Examples ======== >>> from sympy.physics.secondquant import FixedBosonicBasis >>> b = FixedBosonicBasis(2, 3) >>> b.state(3) FockStateBosonKet((1, 0, 1)) rr s rErzFixedBosonicBasis.state\szz!}rDc$|j|SrSrr s rEr zFixedBosonicBasis.__getitem__irrDc,t|jSrSrrrs rErzFixedBosonicBasis.__len__lrrDc,t|jSrSrrrs rErzFixedBosonicBasis.__repr__orrDN) r@rArBrurrrr<rr rrrCrDrEr2r2!s/  )' '  rDr2c<eZdZdZdZedZdZdZdZ dZ y) r3ab The Commutator: [A, B] = A*B - B*A The arguments are ordered according to .__cmp__() Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import Commutator >>> A, B = symbols('A,B', commutative=False) >>> Commutator(B, A) -Commutator(A, B) Evaluate the commutator with .doit() >>> comm = Commutator(A,B); comm Commutator(A, B) >>> comm.doit() A*B - B*A For two second quantization operators the commutator is evaluated immediately: >>> from sympy.physics.secondquant import Fd, F >>> a = symbols('a', above_fermi=True) >>> i = symbols('i', below_fermi=True) >>> p,q = symbols('p,q') >>> Commutator(Fd(a),Fd(i)) 2*NO(CreateFermion(a)*CreateFermion(i)) But for more complicated expressions, the evaluation is triggered by a call to .doit() >>> comm = Commutator(Fd(p)*Fd(q),F(i)); comm Commutator(CreateFermion(p)*CreateFermion(q), AnnihilateFermion(i)) >>> comm.doit(wicks=True) -KroneckerDelta(i, p)*CreateFermion(q) + KroneckerDelta(i, q)*CreateFermion(p) Fc D|r|stjS||k(rtjS|js |jrtjS|j}t |t r't |j Dcgc] }||| c}S|j}t |t r't |j Dcgc] }||| c}S|j\}}|j\}}t|t|z}|r?tt||tj|tj|St |trt |trt |tr0t |tr t|j|jSt |trAt |tr1tj t|j|jzStjSt |t"r-t |t"rt%||zt%||zz S|j'|j'kDrtj |||zSycc}wcc}w)aQ The Commutator [A,B] is on canonical form if A < B. Examples ======== >>> from sympy.physics.secondquant import Commutator, F, Fd >>> from sympy.abc import x >>> c1 = Commutator(F(x), Fd(x)) >>> c2 = Commutator(Fd(x), F(x)) >>> Commutator.eval(c1, c2) 0 N)r rr_rcrUrrgrrr _from_argsrrrrrr(rr6sort_key) rWrartermcancacbncbrs rErTzCommutator.evalsa66M 666M  q//66M HHJ a !&&9$T19: : HHJ a !&&9$Q9: : **,C**,CbDH$ sF|S)Q%RS S a )jO.L![)jO.L%aggqww77![)jO.L}}^AGGQWW%EEEvv a* + 1>O0P1:ac * * ::>> from sympy.physics.secondquant import Commutator, F, Fd >>> from sympy import symbols >>> i, j = symbols('i,j', below_fermi=True) >>> a, b = symbols('a,b', above_fermi=True) >>> c = Commutator(Fd(a)*F(i),Fd(b)*F(j)) >>> c.doit(wicks=True) 0 rr]r6rC)rgrdoitr6rJrP)rshintsrars rErzCommutator.doits IIaL IIaL 99W AA QqSzE!A#J..  !ac (%(( 4 +  sB B#B#"B#cHd|jdd|jddS)Nz Commutator(r,r]rrqrrs rErzCommutator.__repr__s&*iilDIIaLAArDcHd|jdd|jddS)Nrrrr]rrqrrs rErzCommutator.__str__s IIaL$))A,77rDc vdt|jDcgc]}|j|c}zScc}w)Nz\left[%s,%s\right])rergr)rsrrXs rErzCommutator._latexs;%+/99/6$'GNN3 /6)77 7/6s6 N) r@rArBrur_rvrTrrrrrCrDrEr3r3ss7*XN9+9+v)<B87rDr3c|eZdZdZdZdZedZedZdZ dZ dZ d Z d Z d Zd Zd ZdZdZdZy)r7a This Object is used to represent normal ordering brackets. i.e. {abcd} sometimes written :abcd: Explanation =========== Applying the function NO(arg) to an argument means that all operators in the argument will be assumed to anticommute, and have vanishing contractions. This allows an immediate reordering to canonical form upon object creation. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import NO, F, Fd >>> p,q = symbols('p,q') >>> NO(Fd(p)*F(q)) NO(CreateFermion(p)*AnnihilateFermion(q)) >>> NO(F(q)*Fd(p)) -NO(CreateFermion(p)*AnnihilateFermion(q)) Note ==== If you want to generate a normal ordered equivalent of an expression, you should use the function wicks(). This class only indicates that all operators inside the brackets anticommute, and have vanishing contractions. Nothing more, nothing less. Fct|}|j}|jr&t|jDcgc] }|| c}S|j r1|j \}}|r t|}|s|Stj}g}d}|D]A}t|tr|j|jd}1|j|C|r||t|zSt|dtrt t!|\}} | dzr"tj&|z|t|zS| r||t|zS |tjk7r||t|zSt)j*|t|St|tr|S|Scc}w#t"$rtj$cYSwxYw)ar Use anticommutation to get canonical form of operators. Explanation =========== Employ associativity of normal ordered product: {ab{cd}} = {abcd} but note that {ab}{cd} /= {abcd}. We also employ distributivity: {ab + cd} = {ab} + {cd}. Canonical form also implies expand() {ab(c+d)} = {abc} + {abd}. FTrr})rrcrdrrgrhrr r rrUr7extendrrrr~rLrr(rrV) rWrXrrseqcoeffnewseqfounditfacr)s rErVz NO.__new__,s"cljjl ::9#d)9: : ::,,.KFCV  LFG 'c2&MM#((+"GMM#&  ' Sf...#a&/2)) ;C@ ax e+Sf->>>Sf...~Sf...<<S&\2 2 c2 J g:>- vv  sF F%%GGcN|jdjdjS)a< Return 0 if the leftmost argument of the first argument is a not a q_creator, else 1 if it is above fermi or -1 if it is below fermi. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import NO, F, Fd >>> a = symbols('a', above_fermi=True) >>> i = symbols('i', below_fermi=True) >>> NO(Fd(a)*Fd(i)).has_q_creators 1 >>> NO(F(i)*F(a)).has_q_creators -1 >>> NO(Fd(i)*F(a)).has_q_creators #doctest: +SKIP 0 r)rgrrrs rEhas_q_creatorszNO.has_q_creatorsus#,yy|  #000rDcN|jdjdjS)a3 Return 0 if the rightmost argument of the first argument is a not a q_annihilator, else 1 if it is above fermi or -1 if it is below fermi. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import NO, F, Fd >>> a = symbols('a', above_fermi=True) >>> i = symbols('i', below_fermi=True) >>> NO(Fd(a)*Fd(i)).has_q_annihilators -1 >>> NO(F(i)*F(a)).has_q_annihilators 1 >>> NO(Fd(a)*F(i)).has_q_annihilators 0 rr)rgrrrs rEhas_q_annihilatorszNO.has_q_annihilatorss#,yy|  $555rDc |jddr|jS|jt||jdj di|S)a Either removes the brackets or enables complex computations in its arguments. Examples ======== >>> from sympy.physics.secondquant import NO, Fd, F >>> from textwrap import fill >>> from sympy import symbols, Dummy >>> p,q = symbols('p,q', cls=Dummy) >>> print(fill(str(NO(Fd(p)*F(q)).doit()))) KroneckerDelta(_a, _p)*KroneckerDelta(_a, _q)*CreateFermion(_a)*AnnihilateFermion(_a) + KroneckerDelta(_a, _p)*KroneckerDelta(_i, _q)*CreateFermion(_a)*AnnihilateFermion(_i) - KroneckerDelta(_a, _q)*KroneckerDelta(_i, _p)*AnnihilateFermion(_a)*CreateFermion(_i) - KroneckerDelta(_i, _p)*KroneckerDelta(_i, _q)*AnnihilateFermion(_i)*CreateFermion(_i) remove_bracketsTrrC)r_remove_bracketsrVtypergr)rsrs rErzNO.doitsP( 99& -((* *<<T ,=DIIaL,=,=,F,FG GrDcHg}|jD]}||js||jj}t ||jt r|j ddd|d<t di|}|j ddd|d<t di|}t||}||j||t|||jz||j||t|||jzz}|j|||ft|||rYt|j|}t |tr.t|jD cgc]} | j!c} Sy|jdScc} w)z Returns the sorted string without normal order brackets. The returned string have the property that no nonzero contractions exist. rNTrr)r)ra)iter_q_creatorsrrrrUrpoprrVrrrNr7rfrrgr) rssubslistrassumebelowaboverWsplitrars rErzNO._remove_bracketss%%' HAAw''a33d1gmmU3JJ}d3,0F=)!00EJJ}d3,0F=)!00EtAw-CQU3(Q >?q'//#u5(Q >?? OOT!We$45>tAwGG1 H2  (+,F&#&V[[ATTYY[ABB'99Q< Bs4Fc,t|jS)aG Returns a sum of NO objects that contain no ambiguous q-operators. Explanation =========== If an index q has range both above and below fermi, the operator F(q) is ambiguous in the sense that it can be both a q-creator and a q-annihilator. If q is dummy, it is assumed to be a summation variable and this method rewrites it into a sum of NO terms with unambiguous operators: {Fd(p)*F(q)} = {Fd(a)*F(b)} + {Fd(a)*F(i)} + {Fd(j)*F(b)} -{F(i)*Fd(j)} where a,b are above and i,j are below fermi level. )r7rrrs rE_expand_operatorszNO._expand_operatorss $''((rDct|trJ|jt|}t |Dcgc]}|j dj | c}S|j dj |Scc}wrp)rUsliceindicesrr\rg)rsrrs rEr zNO.__getitem__se a iiD *G27/BQDIIaL%%a(B B99Q<$$Q' 'Cs#A7cFt|jdjSrprrrs rErz NO.__len__s499Q<$$%%rDc#K|jdj}tt|dz dd}|D]}||jr|yyw)a Iterates over the annihilation operators. Examples ======== >>> from sympy import symbols >>> i, j = symbols('i j', below_fermi=True) >>> a, b = symbols('a b', above_fermi=True) >>> from sympy.physics.secondquant import NO, F, Fd >>> no = NO(Fd(a)*F(i)*F(b)*Fd(j)) >>> no.iter_q_creators() >>> list(no.iter_q_creators()) [0, 1] >>> list(no.iter_q_annihilators()) [3, 2] rr]rN)rgr\rrrsopsrers rEiter_q_annihilatorszNO.iter_q_annihilatorssU*iilSX\2r* A1v&&  sAAc#K|jdj}tdt|}|D]}||jr|yyw)a  Iterates over the creation operators. Examples ======== >>> from sympy import symbols >>> i, j = symbols('i j', below_fermi=True) >>> a, b = symbols('a b', above_fermi=True) >>> from sympy.physics.secondquant import NO, F, Fd >>> no = NO(Fd(a)*F(i)*F(b)*Fd(j)) >>> no.iter_q_creators() >>> list(no.iter_q_creators()) [0, 1] >>> list(no.iter_q_annihilators()) [3, 2] rN)rgr\rrrs rErzNO.iter_q_creators!sO,iilQC! A1v""  sA Ac|jd}|j|jd||j|dzdz}t|S)aS Returns a NO() without FermionicOperator at index i. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import F, NO >>> p, q, r = symbols('p,q,r') >>> NO(F(p)*F(q)*F(r)).get_subNO(1) NO(AnnihilateFermion(p)*AnnihilateFermion(r)) rNr])rg _new_rawargsr7)rsrarg0muls rE get_subNOz NO.get_subNO?sJyy|d$))BQ-$))AEF2C"CE#wrDcDd|j|jdzS)Nz\left\{%s\right\}r)rrgrs rErz NO._latexRs& ! )EEErDc&d|jdzS)NzNO(%s)rrqrrs rErz NO.__repr__Us$))A,&&rDc&d|jdzS)Nz:%s:rrqrrs rErz NO.__str__Xs ! $$rDN)r@rArBrur_rVrrrrrrr rrrrrrrrCrDrEr7r7sx!DNGR11.66.H2( T)$(&:<&F'%rDr7ct|trt|trt|trGt|tr6|jj j drtjS|jj j drtjS|jj j dr t|j|jS|jj j dr t|j|jSt|j|jt|jtddzSt|trGt|tr6|jj j drtjS|jj j drtjS|jj j dr t|j|jS|jj j dr t|j|jSt|j|jt|jtddzStjSd||fD}t|) a Calculates contraction of Fermionic operators a and b. Examples ======== >>> from sympy import symbols >>> from sympy.physics.secondquant import F, Fd, contraction >>> p, q = symbols('p,q') >>> a, b = symbols('a,b', above_fermi=True) >>> i, j = symbols('i,j', below_fermi=True) A contraction is non-zero only if a quasi-creator is to the right of a quasi-annihilator: >>> contraction(F(a),Fd(b)) KroneckerDelta(a, b) >>> contraction(Fd(i),F(j)) KroneckerDelta(i, j) For general indices a non-zero result restricts the indices to below/above the fermi surface: >>> contraction(Fd(p),F(q)) KroneckerDelta(_i, q)*KroneckerDelta(p, q) >>> contraction(F(p),Fd(q)) KroneckerDelta(_a, q)*KroneckerDelta(p, q) Two creators or two annihilators always vanishes: >>> contraction(F(p),F(q)) 0 >>> contraction(Fd(p),Fd(q)) 0 rrraTr,rr+c3<K|]}t|tywrS)rUr)r`rs rErbzcontraction..s @1j-. @s) rUrrrrrrr rrrrJ)rarts rEr5r5\sJ!&'Jq:K,L a* + 1m0Lww##'' 6vv ww##'' 6vv ww##'' 6%aggqww77ww##'' 6%aggqww77"177AGG4"177E#4,HIJ K a* + 1m0Lww##'' 6vv ww##'' 6vv ww##'' 6%aggqww77ww##'' 6%aggqww77"177AGG4"177E#4,HIJ Kvv  AA @.22rDc"|jS)z4Generates key for canonical sorting of SQ operators.)r) sq_operators rE_sqkey_operatorrs    !!rDcbt|}t|}t|trE|jj drd||fS|jj drd||fSd||fS|jj drd||fS|jj drd||fSd||fS) zwKey for sorting of indices. particle < hole < general FIXME: This is a bottle-neck, can we do it faster? rr )rrrUrrr)r<rrs rErrs U A JE%    ! !- 0q> !    # #M 2q> !q> ! m,E1~      .E1~E1~rDc2d}d}ttt|dz }ttt|dz dd}tt||}t tt ||}|sd}|D]8}||} ||dz} | | k(r t | | g| | kDs(d}| | g|||dz|dz}:|rn@|D]8}||} ||dz} | | k(r t | | g| | kDs(d}| | g|||dz|dz}:|s|D cgc]} ||  }} ||fScc} w)aRSort fermionic operators to canonical order, assuming all pairs anticommute. Explanation =========== Uses a bidirectional bubble sort. Items in string1 are not referenced so in principle they may be any comparable objects. The sorting depends on the operators '>' and '=='. If the Pauli principle is violated, an exception is raised. Returns ======= tuple (sorted_str, sign) sorted_str: list containing the sorted operators sign: int telling how many times the sign should be changed (if sign==0 the string was already sorted) Frr]rrTr})rr\rrfdictrprL) string1r|verifiedr)rngrevkeyskey_valrleftrightrs rEr~r~sl,H D uS\A%& 'C uS\A%r2. /C C! "D4D'*+,G A7DQKEu}/u >>e| !& Qq1u ax    A7DQKEu}/u >>e| !& Qq1u ax ,%)*q *G* T?+s Dc<tf}t||r0|j|jDcgc] }t |c}St|t rBg}i}|jD]L}|j D]}||vr||xxdz cc<d||<t|ts<|j|N|D]}|jjrQ||jrB|j|j|j}t|dkDs]t |cS|jjr]||jrN|jrB|j|j|j}t|dkDst |cS|S|Scc}w)a We evaluate KroneckerDelta symbols in the expression assuming Einstein summation. Explanation =========== If one index is repeated it is summed over and in effect substituted with the other one. If both indices are repeated we substitute according to what is the preferred index. this is determined by KroneckerDelta.preferred_index and KroneckerDelta.killable_index. In case there are no possible substitutions or if a substitution would imply a loss of information, nothing is done. In case an index appears in more than one KroneckerDelta, the resulting substitution depends on the order of the factors. Since the ordering is platform dependent, the literal expression resulting from this function may be hard to predict. Examples ======== We assume the following: >>> from sympy import symbols, Function, Dummy, KroneckerDelta >>> from sympy.physics.secondquant import evaluate_deltas >>> i,j = symbols('i j', below_fermi=True, cls=Dummy) >>> a,b = symbols('a b', above_fermi=True, cls=Dummy) >>> p,q = symbols('p q', cls=Dummy) >>> f = Function('f') >>> t = Function('t') The order of preference for these indices according to KroneckerDelta is (a, b, i, j, p, q). Trivial cases: >>> evaluate_deltas(KroneckerDelta(i,j)*f(i)) # d_ij f(i) -> f(j) f(_j) >>> evaluate_deltas(KroneckerDelta(i,j)*f(j)) # d_ij f(j) -> f(i) f(_i) >>> evaluate_deltas(KroneckerDelta(i,p)*f(p)) # d_ip f(p) -> f(i) f(_i) >>> evaluate_deltas(KroneckerDelta(q,p)*f(p)) # d_qp f(p) -> f(q) f(_q) >>> evaluate_deltas(KroneckerDelta(q,p)*f(q)) # d_qp f(q) -> f(p) f(_p) More interesting cases: >>> evaluate_deltas(KroneckerDelta(i,p)*t(a,i)*f(p,q)) f(_i, _q)*t(_a, _i) >>> evaluate_deltas(KroneckerDelta(a,p)*t(a,i)*f(p,q)) f(_a, _q)*t(_a, _i) >>> evaluate_deltas(KroneckerDelta(p,q)*f(p,q)) f(_p, _p) Finally, here are some cases where nothing is done, because that would imply a loss of information: >>> evaluate_deltas(KroneckerDelta(i,p)*f(q)) f(_q)*KroneckerDelta(_i, _p) >>> evaluate_deltas(KroneckerDelta(i,p)*f(i)) f(_i)*KroneckerDelta(_i, _p) r]r)rrUrmrgr8r free_symbolsrrkillable_index is_Symbolrfpreferred_indexr!indices_contain_equal_information)rgaccepted_functionsrXdeltasrrsds rEr8r8sN !'(qvv?,?@@ As  !A^^ #<AJ!OJ!"GAJ  # !^, a  ! A))ga6F6F.GFF1++Q->->?v;?*1--##--'!:K:K2L99FF1,,a.>.>?v;?*1-- E@sFFc |ri|jdd|jdd|jdd ttt fd}fd} fd}g}g}g}|jt} |st | t } d x} x} } | D]} | j }|jd r|r | }| d z } |}n4|jd r|r | }| d z } |}n|r | }| d z } |}|r|jtfi||j| |j}tj|}g}|D]R}t|} t|} t|} t|}i}|D]d} | j jd rt| || <-| j jd rt| || <Wt| || <fg}g}|jD]p\}}||k(r ||vrN|||vr2td }|j||f|j||fI|jd ||f^|j||fr|j!||j|j#|Ut|S)aG Collect terms by substitution of dummy variables. Explanation =========== This routine allows simplification of Add expressions containing terms which differ only due to dummy variables. The idea is to substitute all dummy variables consistently depending on the structure of the term. For each term, we obtain a sequence of all dummy variables, where the order is determined by the index range, what factors the index belongs to and its position in each factor. See _get_ordered_dummies() for more information about the sorting of dummies. The index sequence is then substituted consistently in each term. Examples ======== >>> from sympy import symbols, Function, Dummy >>> from sympy.physics.secondquant import substitute_dummies >>> a,b,c,d = symbols('a b c d', above_fermi=True, cls=Dummy) >>> i,j = symbols('i j', below_fermi=True, cls=Dummy) >>> f = Function('f') >>> expr = f(a,b) + f(c,d); expr f(_a, _b) + f(_c, _d) Since a, b, c and d are equivalent summation indices, the expression can be simplified to a single term (for which the dummy indices are still summed over) >>> substitute_dummies(expr) 2*f(_a, _b) Controlling output: By default the dummy symbols that are already present in the expression will be reused in a different permutation. However, if new_indices=True, new dummies will be generated and inserted. The keyword 'pretty_indices' can be used to control this generation of new symbols. By default the new dummies will be generated on the form i_1, i_2, a_1, etc. If you supply a dictionary with key:value pairs in the form: { index_group: string_of_letters } The letters will be used as labels for the new dummy symbols. The index_groups must be one of 'above', 'below' or 'general'. >>> expr = f(a,b,i,j) >>> my_dummies = { 'above':'st', 'below':'uv' } >>> substitute_dummies(expr, new_indices=True, pretty_indices=my_dummies) f(_s, _t, _u, _v) If we run out of letters, or if there is no keyword for some index_group the default dummy generator will be used as a fallback: >>> p,q = symbols('p q', cls=Dummy) # general indices >>> expr = f(p,q) >>> substitute_dummies(expr, new_indices=True, pretty_indices=my_dummies) f(_p_0, _p_1) rrrgeneralcP |S#t$rdt|z zcYSwxYw)Ni_ IndexErrorr)number len_below letters_belows rE_izsubstitute_dummies.._i 7 6$V,, 6c&9"4555 6 %%cP |S#t$rdt|z zcYSwxYw)Na_r)r len_above letters_aboves rE_azsubstitute_dummies.._a rrcP |S#t$rdt|z zcYSwxYw)Np_r)r len_generalletters_generals rE_pzsubstitute_dummies.._p s7 8&v.. 8c&;"6777 8rr{rrr]rx)rrrdrsortedrrrrcr make_argsre_get_ordered_dummiesnextitemsinsertrrf)!expr new_indicespretty_indicesrrrabovesbelowsgeneralsdummiesrarpr assumsyml1terms new_termsrorderedsubsdictr final_subsrvr rrrrrrs! @@@@@@rEr:r:i sF&**7B7 &**7B7 (,,Y; &  & /*  6  6  8 F FHjjG &67MAMA  99] #eQB YY} %eQBeQB  IIeC)5) * IIaL-0 ;;=D MM$ EI". L L N&t, &A~~!!-0"1g ##M2"1g "1g  & NN$ (DAqAvH}A;(*c AOOQF+%%q!f-%%a!Q0A'# ($  #8,-E".F  ?rDceZdZdZdZy) KeyPrinterz7Printer for which only equal objects are equal in printc8d|j|jfzS)Nz(%s_%i))name dummy_index)rsr's rE _print_DummyzKeyPrinter._print_Dummy sDIIt'7'7888rDN)r@rArBrur=rCrDrEr9r9 s A9rDr9c8t}|j|SrS)r9doprint)r'r.s rE__kprintr@ s A 99T?rDc  tj|}|Dcic]}||jtc} |Dcic]}|t |c} t j j}i|D]I}|jjdrd|<$|jjdrd|<Ed|<K|Dcic]}|t |c} fd}ttt|tt|| t| fd}tt! jrt#t } j%D]\}} || j'||D cgc]} t)|| d ks| c} D]} || =t|D cgc]} ||  }} t+|||}|Scc}wcc}wcc}wcc} wcc} w) a Returns all dummies in the mul sorted in canonical order. Explanation =========== The purpose of the canonical ordering is that dummies can be substituted consistently across terms with the result that equivalent terms can be simplified. It is not possible to determine if two terms are equivalent based solely on the dummy order. However, a consistent substitution guided by the ordered dummies should lead to trivially (non-)equivalent terms, thereby revealing the equivalence. This also means that if two terms have identical sequences of dummies, the (non-)equivalence should already be apparent. Strategy -------- The canonical order is given by an arbitrary sorting rule. A sort key is determined for each dummy as a tuple that depends on all factors where the index is present. The dummies are thereby sorted according to the contraction structure of the term, instead of sorting based solely on the dummy symbol itself. After all dummies in the term has been assigned a key, we check for identical keys, i.e. unorderable dummies. If any are found, we call a specialized method, _determine_ambiguous(), that will determine a unique order based on recursive calls to _get_ordered_dummies(). Key description --------------- A high level description of the sort key: 1. Range of the dummy index 2. Relation to external (non-dummy) indices 3. Position of the index in the first factor 4. Position of the index in the second factor The sort key is a tuple with the following components: 1. A single character indicating the range of the dummy (above, below or general.) 2. A list of strings with fully masked string representations of all factors where the dummy is present. By masked, we mean that dummies are represented by a symbol to indicate either below fermi, above or general. No other information is displayed about the dummies at this point. The list is sorted stringwise. 3. An integer number indicating the position of the index, in the first factor as sorted in 2. 4. An integer number indicating the position of the index, in the second factor as sorted in 2. If a factor is either of type AntiSymmetricTensor or SqOperator, the index position in items 3 and 4 is indicated as 'upper' or 'lower' only. (Creation operators are considered upper and annihilation operators lower.) If the masked factors are identical, the two factors cannot be ordered unambiguously in item 2. In this case, items 3, 4 are left out. If several indices are contracted between the unorderable factors, it will be handled by _determine_ambiguous() r0r12c  Dcgc] }| |vs |}}tj|Dcgc]} | c}}|d}| |ur |j}|j||Dcgc]}| }}|D]'}|Dcgc]}|j ||}})|Dcgc]}|j ||}}t t t||}t|r |j|t|fSt t t|| |j fd|jg}|D]9}t|tr@||jvr|jd||jvsB|jdTt|t r|jdvt|t"r|jdt|t$r]|Dcgc]}|j'|s|} }| D]5}t|t r|jd%|jd7d} ||j) || dz} | dk(r*|j| 4|t||d|dfScc}wcc}wcc}wcc}wcc}wcc}w)Nrc|SrSrC)r keydicts rEz4_get_ordered_dummies.._key.. s WQZrDr{ulr]r)setunioncopyremovereplacerrrprsortrerUr9rrrrrr7hasfind)r r dumstruct other_dums masked_facsd2 all_maskedpos_valr{rfacposrGdum_reprfac_dumfac_reprmasks @rE_keyz"_get_ordered_dummies.._keyl s%,CcWS\0AcC C SU[[9"EC73<"EF m  % **,J!1:<# < < -B*- KK d2h?-K- -#.0{{8A;Q80 04I{ ;<=  J  OO 7E*-- -tC :678/0 +C#23 >NN3' >NN3'C)s#C-s#C$%(7rBFF1I77,B!"g.s+s+ ,(-228A; KF|NN6* ) +2Qz*GAJ DDeD"E =-048s- J:J: J?6 K K 4KK(Kc|SrSrC)r dumkeys rErHz&_get_ordered_dummies.. s F1IrDr{r})r r"rdrr@rKrLvaluesrrrrrprfr!rrerr%addr_determine_ambiguous)rverbosergrall_dumsr r^ra unorderedrrZr`r[r\r]s @@@@@rEr#r# sD == D157#SYYu%%7G/35hsm#5Hsu{{GNN,-H D  >>  m ,DG ^^   .DGDG )1118A;1H3Eh$s8T#dH*=%>?@ AF H"5 6FV]]_%&$ LLN DAq aL  Q  'B3y|+s" EE> EE5 E#?E#c.eZdZdZdZedZdZy)_SymbolFactoryc d|_||_yrp) _counterVar_label)rsrs rErz_SymbolFactory.__init__ s rDc||_y)z( Sets counter to value. Nry)rsvalues rErkz_SymbolFactory._set_counter s !rDc|jS)z/ What counter is currently at. r|rrs rEriz_SymbolFactory._counter s rDcxtd|j|jfz}|xjdz c_|S)zI Generates the next symbols and increments counter by 1. z%s%ir])rrzry)rsrs rErjz_SymbolFactory._next s8 6T[[$*:*:;; < ArDN)r@rArBrrkrrirjrCrDrErwrw s%!   rDrwz_]"]_c |r|rg}ntt|g}tt|dz D]}t|dzt|D]}t ||||}|s||z dzdz}|rt j |z}n|}||dz|||dzdz}|r5|j|tt|d|t||zz|j|tt|d|z|st|St|S)z Returns Add-object with contracted terms. Uses recursion to find all contractions. -- Internal helper function -- Will find nonzero contractions in string1 between indices given in leftrange and rightrange. r]r}Nkeep_only_fully_contracted) r7r r\rr5r r(r_get_contractionsr) rrrarrqcr)roplists rErr sA"gS']#$ 3w">?E" @H &  <Q&P <rDc |stjSddddd}|j|t|tr|drtjS|St|t r|drtjS|S|j d}|j}t|trb|dr3tt|jDcgc]}t|fi|c}St|jDcgc]}t|fi|c}St|trg}g}|jD]1}|jr|j|!|j|3t|}|dk(r|}n\|dk(r|drtjS|}n?t|dt rt"t%|}t'||d }t||z}|d r|j}|d r t)|}|S|Scc}wcc}w) a Returns the normal ordered equivalent of an expression using Wicks Theorem. Examples ======== >>> from sympy import symbols, Dummy >>> from sympy.physics.secondquant import wicks, F, Fd >>> p, q, r = symbols('p,q,r') >>> wicks(Fd(p)*F(q)) KroneckerDelta(_i, q)*KroneckerDelta(p, q) + NO(CreateFermion(p)*AnnihilateFermion(q)) By default, the expression is expanded: >>> wicks(F(p)*(F(q)+F(r))) NO(AnnihilateFermion(p)*AnnihilateFermion(q)) + NO(AnnihilateFermion(p)*AnnihilateFermion(r)) With the keyword 'keep_only_fully_contracted=True', only fully contracted terms are returned. By request, the result can be simplified in the following order: -- KroneckerDelta functions are evaluated -- Dummy variables are substituted consistently across terms >>> p, q, r = symbols('p q r', cls=Dummy) >>> wicks(Fd(p)*(F(q)+F(r)), keep_only_fully_contracted=True) KroneckerDelta(_i, _q)*KroneckerDelta(_p, _q) + KroneckerDelta(_i, _r)*KroneckerDelta(_p, _r) FT)simplify_kronecker_deltasrcsimplify_dummiesrr)r6rrr]rrcr)r rupdaterUr7rrrcrr:rgr6r r_rrrrrerr8) rgkw_argsoptsrrrfactornras rEr6r6A s> vv &+!&+  D  KK!R , -66MH A( ) , -66MH TA  A!S " #%cPQPVPV+WU4-C7-C+W&XY YQVVDT%00DE E!Sff 'F$$ f%v&  ' L 6F !V01vv '!*o6))GnG'w+/0L+MPF&\&(F >]]_F + ,$V,F  H_,XDs G9 6G>c&eZdZdZdZdZdZdZy)r;zg Represents the index permutation operator P(ij). P(ij)*f(i)*g(j) = f(i)*g(j) - f(j)*g(i) Tc|ttt||ft\}}t j |||}|S)Nr{)r!rfrrrrV)rWrrqrZs rErVzPermutationOperator.__new__ s5c'Aq6*0@A1mmCA& rDc,|jd}|jd}|j|rd|j|rSt}|j||}|j||}|j||}tj |zS|S)aa Returns -expr with permuted indices. Explanation =========== >>> from sympy import symbols, Function >>> from sympy.physics.secondquant import PermutationOperator >>> p,q = symbols('p,q') >>> f = Function('f') >>> PermutationOperator(p,q).get_permuted(f(p,q)) -f(q, p) rr])rgrQrrfr r()rsr'rrqtmps rE get_permutedz PermutationOperator.get_permuted s} IIaL IIaL 88A;488A;'C99Q$D99Q?D99S!$D==% %KrDcFdtfd|jDzS)NzP(%s%s)c3@K|]}j|ywrS)r)r`rrs rErbz-PermutationOperator._latex.. s Fq!2 Fs)rergrs `rErzPermutationOperator._latex s5 FDII FFFFrDN)r@rArBrur_rVrrrCrDrEr;r; s N 4GrDr;c fd fd}|j}t|trt|j}|D]}t}t}|r|j }|j |}|||zvr; |j|||||j} |j|| znw|} t|}| |k(r|j|nS|||zvr; |j|||||j} |j|| zn|j||r||z}t|S|S#t$r|j|YwxYw#t$r|j|YwxYw)a Performs simplification by introducing PermutationOperators where appropriate. Explanation =========== Schematically: [abij] - [abji] - [baij] + [baji] -> P(ab)*P(ij)*[abij] permutation_operators is a list of PermutationOperators to consider. If permutation_operators=[P(ab),P(ij)] we will try to introduce the permutation operators P(ij) and P(ab) in the expression. If there are other possible simplifications, we ignore them. >>> from sympy import symbols, Function >>> from sympy.physics.secondquant import simplify_index_permutations >>> from sympy.physics.secondquant import PermutationOperator >>> p,q,r,s = symbols('p,q,r,s') >>> f = Function('f') >>> g = Function('g') >>> expr = f(p)*g(q) - f(q)*g(p); expr f(p)*g(q) - f(q)*g(p) >>> simplify_index_permutations(expr,[PermutationOperator(p,q)]) f(p)*g(q)*PermutationOperator(p, q) >>> PermutList = [PermutationOperator(p,q),PermutationOperator(r,s)] >>> expr = f(p,r)*g(q,s) - f(q,r)*g(p,s) + f(q,s)*g(p,r) - f(p,s)*g(q,r) >>> simplify_index_permutations(expr,PermutList) f(p, r)*g(q, s)*PermutationOperator(p, q)*PermutationOperator(r, s) cg}|jD]=}||vr|j||js&|j||?|S)zD Collects indices recursively in predictable order. )rgrr)r'indrarX _get_indicess rErz1simplify_index_permutations.._get_indices sQ99 :Ccz c"88MM,sC"89  :  rDc*t||fdS)Nc(t|SrSr)r rrs rErHzJsimplify_index_permutations.._choose_one_to_keep.. s'7 Q8L'MrDr{)min)rarrrs `rE_choose_one_to_keepz8simplify_index_permutations.._choose_one_to_keep s1aMNNrD) rcrUrrKrgrrrNKeyErrorrbr:) r'permutation_operatorsrr2Pr3on_holdrpermutedkeep permuted1rs @rEr<r< s|F O ;;=D$DII& (AIeGyy{>>$/uw.1 X./tXqvvFDMM!D&)!)I1(;H H, D)!UW_45!LL2 34166J! af-! d+9:'EA (BE{ K3$1x01" (5#NN845s$E ,E+ E('E(+FFN)F)hru collectionsrsympy.core.addrsympy.core.basicrsympy.core.cachersympy.core.containersrsympy.core.exprrsympy.core.functionr sympy.core.mulr sympy.core.numbersr sympy.core.powerr sympy.core.singletonr sympy.core.sortingrsympy.core.symbolrrsympy.core.sympifyr$sympy.functions.elementary.complexesr(sympy.functions.elementary.miscellaneousr(sympy.functions.special.tensor_functionsrsympy.matrices.densersympy.printing.strrsympy.utilities.iterablesr__all__ Exceptionr>rHrJrLrNrPrrxr9rrrrrrr,r-rrrr+r*rrr$r!r r"r#r$r%r&r'r(r)rYr.r/r4r0r1r2r3r7r5rrr~r8r:r9r@r#rcrwrhrr6r;r<rCrDrErs $"$' ( "/+&:9C&).& R i  !8  '>  7  +B  6 ETEP4 o',o'dDLDLN j  *  j 09o{09d'D/7'DR` `FU9);U9pRD%wRDh+E+E\,,,,^K09K0\&9& -9 - - L-" - L -/, /6/, /">B$/5/d 2  @ @ FO O dQ7Q7hR%R%j E3P"4/>5pl^*/reP99  Pf6r2!) 9 9xl ^(G$(GVZrD