edeprince3 / pdaggerq

Apache License 2.0
44 stars 10 forks source link

commutator of 2-body terms #54

Closed jagunther closed 4 months ago

jagunther commented 4 months ago

Hi, I want to use pdaggerq for the following task:

For two different 2-body hamiltonians $H, H'$, both of the form $\sum{p,q,r,s}h{pqrs}a_p^+ a_q a_r^+ as$, compute $<\Phi{HF}|[H,H']|\Phi{HF}>$. Here $|\Phi{HF}>$ is the Hartree-Fock state, or Fermi-vaccum, $p,q,r,s$ are indices of arbitrary spin-orbitals.

I tried with

pq = pdaggerq.pq_helper("fermi")
pq.add_commutator(1.0, ['a*(p)', 'a(q)', 'a*(r)', 'a(s)'], ['a*(t)', 'a(u)', 'a*(v)', 'a(w)'])
pq.simplify()
pq.print()

and got the output


    // fully-contracted strings:
    //     +     1.00000000000000 P(k,l) d(i,j) d(l,m) d(a,v) d(k,n) 
    //     +     2.00000000000000 P(i,l) d(i,j) d(a,b) d(c,v) d(k,l) 
    //     +     1.00000000000000 P(i,l) d(j,k) d(l,m) d(a,v) d(i,n) 
    //     -     1.00000000000000 P(j,l) d(i,k) d(l,m) d(a,v) d(j,n) 
    //     +     2.00000000000000 d(j,k) d(a,b) d(c,v) d(i,l) 
    //     -     1.00000000000000 P(i,j) d(a,c) d(j,k) d(b,v) d(i,l) 
    //     +     1.00000000000000 d(a,b) d(c,d) d(e,v) d(i,j) 
    //     -     2.00000000000000 d(a,v) d(i,j) d(b,c) d(k,l) 
    //     +     1.00000000000000 d(a,v) d(b,c) d(d,e) d(i,j) 

It seems that I don't quite understand the output yet. Is d(l,m) the Dirac delta? What does P(k,l) stand for? Also, I expected the outcome to use the same dummy indices as I provided in the input.

Any help is very welcome!

edeprince3 commented 4 months ago

Yes, d(l,m) is a Kronecker delta function.

P(k,l) is a permutation/antisymmetrizer operator, e.g. P(k,l) A(a,b,k,l) = A(a,b,k,l) - A(a,b,l,k)

For the fermi vacuum, general summation labels are split into occupied and virtual labels. The way the code decides if you're working with a general label or not is by checking the label against the list of labels it considers to be occupied or virtual.

looking at the code now, I think there will be a problem with your input for the label "v". It is being interpreted as a virtual label because, internally, v0, v1, etc. are used as virtual labels, and I was only checking the first character of the string. I'll fix this.

edeprince3 commented 4 months ago

there is a new feature branch "general_operators" which is more appropriate for what you're trying to do. The interface is a little clunky, but I think it should work:

"""

    direct specification of pq strings including tensors

    pq.add_new_string(factor, type, order, amps_labels, ops_labels, has_permutational_symmetry)

    :param factor: numerical factor
    :param type: list of labels for amplitudes (e.g., ['A', 'B'])
    :param order: list of order for amplitudes (e.g., [1, 2] -> 'A1', 'B2')
    :param amps_labels: list of list of labels for the amplitudes (e.g., [['i'], ['j', 'k']] -> 'A1(i)', 'B2(j,k)')
    :param ops_labels: list of labels for the creation/annihilation operators (include "*" for creators)
    :param has_permutational_symmetry: do the amplitudes have permutational symmetry, e.g., t2(a,b,i,j) = -t2(b,a,i,j), etc.?

"""

import pdaggerq

def main():

    pq = pdaggerq.pq_helper('true')

    A_op = ['p*', 'q',  'r*', 's']
    B_op = ['t*', 'u',  'w*', 'x']

    A_labels = ['p', 'q', 'r', 's']
    B_labels = ['t', 'u', 'w', 'x']

    pq.add_new_string( 1.0, ['A', 'B'], [2, 2], [A_labels, B_labels], A_op + B_op, False)
    pq.add_new_string(-1.0, ['B', 'A'], [2, 2], [B_labels, A_labels], B_op + A_op, False)

    pq.simplify()
    terms = pq.strings()
    for term in terms:
        print(term)
    pq.clear()

if __name__ == "__main__":
    main()
edeprince3 commented 4 months ago

note that in the above snippet, I've avoided using "v" as a label because of the bug I mentioned

jagunther commented 4 months ago

Thanks for the reply!

I switched to the "general_operators" branch and ran your script; it seems that add_new_string() is not expecting a numerical factor, I am getting this error:

TypeError: add_new_string(): incompatible function arguments. The following argument types are supported:
    1. (self: pdaggerq._pdaggerq.pq_helper, arg0: List[str], arg1: List[int], arg2: List[List[str]], arg3: List[str], arg4: bool) -> None

Without the prefactor it works fine

pq.add_new_string(['A', 'B'], [2, 2], [A_labels, B_labels], A_op + B_op, False)
pq.add_new_string(['B', 'A'], [2, 2], [B_labels, A_labels], B_op + A_op, False)

but I guess I need the minus sign for the commutator.

Lastly, I wanted to ask what types of permutational symmetry are possible? The Hamiltonians I have obey the 8-fold symmetry of Coulomb-tensors, i.e.

$H{pqrs} = H{qprs} = H{pqsr} = H{qpsr} = H_{rspq} = ...$

The docstring mentions the antisymmetric properties of the amplitudes, t2(a,b,i,j) = -t2(b,a,i,j), which is not quite what I want, but perhaps there is an option I don't see

edeprince3 commented 4 months ago

whoops, I hadn't pushed my most recent changes. if you pull now, the numerical factor parameter should be there.

for the permutational symmetry, only antisymmetry is implemented with this add_new_string() function. the only impact for you is that you'll end up with terms that you can combine by hand given the symmetry properties of your tensors.

jagunther commented 4 months ago

alright, it works, thanks. And yes, the permutational symmetry I will take care of manually then.

One more question: what exactly does the parameter order control?

edeprince3 commented 4 months ago

Currently, order doesn't do much because I'm still working on this branch and the functionality isn't complete yet. For your use case, it doesn't matter what you set it to, as long as it is not zero. If the order is zero, then the print statements will not include labels on the tensors.

jagunther commented 4 months ago

I see, thanks again!