Welcome to Cadabra Q&A, where you can ask questions and receive answers from other members of the community.
0 votes

Hello, how could I implement fermionic operator algebra, so have operators a_n and a_m^\dag which would behave anticommutatively for all combinations of them except for a_n and a_n^\dag, so if indices are same.

So for example:

a_n a_m = - a_m a_n
a_n a_n = - a_n a_n
a_n a_m^\dag = - a_m^\dag a_n

.... and conjugates of those

but this case should be NonCommuting:

a_n a_n^\dag != - a_n^\dag a_n,
asked in General questions by (240 points)
edited by

1 Answer

+1 vote
 
Best answer

You would first declare the $a$ and $a^\dagger$ operators to be anti-commuting among themselves, so

{n,m,p,q}::Indices(position=free);
a_{n}::SelfAntiCommuting;
ad_{n}::SelfAntiCommuting;
ad_{n}::LaTeXForm("a^\dagger").

where the last line is just to make the ad_{n} print nicely. Then ensure that the default is for daggered and non-daggered operators to be non-commuting,

{a_{n}, ad_{m}}::NonCommuting;

Now the only thing you need to do is to decide what you want to do with expressions which involve $a$ and $a^\dagger$. In many cases, the logic is to 'move' all $a$ to the far right of an expression (so they can annihilate a vacuum state on which they act). You can do that with the rule

\delta_{m n}::KroneckerDelta;
rl:= a_{m} ad_{n} -> - ad_{n} a_{m} + \delta_{n m};

You then apply this rule repeatedly to an expression, distributing products at every step and eliminating Kronecker deltas. If you want you can do this automatically at every step of a calculation, by sticking that logic into the post_process function. Example:

def post_process(ex):
   # move all a operators to the right
   converge(ex):
      substitute(ex, rl)
      distribute(ex)
      eliminate_kronecker(ex)
  sort_product(ex)
  collect_terms(ex)

If you now enter e.g. the expression

ex:= a_{n} ad_{p} a_{m} ad_{m} a_{n};

you get the answer

$$ -a^\dagger_m a_m a_p + \delta_{m m} a_p .$$

Hope this helps.

answered by (37.9k points)
selected by

Thank you! This does help me very much!!

One issue with this though is the eliminate_kronecker(ex) operator, which contracts indices. For example, a_{n} ad_{m} a_{n} would evaluate to a_{m} which isn't in the original equation, while it should evaluate to 0 if n!=m and a_{n} if n=m.

The example you provided should also evaluate to 0 if n!=m.

I suppose I can do it by writing a substitution rule which substitutes first all \delta_{n n} with 1 and then \delta_{m n} with 0:

r2:= \delta_{n n} -> 1;
r3:= \delta_{n m} -> 0;

def post_process(ex):
   # move all a operators to the right
   converge(ex):
      substitute(ex, rl)
      distribute(ex)
      # eliminate_kronecker(ex)
   converge(ex):
      substitute(ex, r2)
      substitute(ex, r3)
      distribute(ex)
   sort_product(ex)
   collect_terms(ex)

This seems to produce correct results.

Another thing is I can't have more than two operators with the same index in one factor, so terms like a_{n} a_{n} ad_{n}, which can for example be produced by an exponential. Is there a way around that?

Sorry, I misunderstood your original question, I thought repeated subscript indices were meant to be summed over.

If that is not the case, you have to declare the symbols in the subscripts as Symbol. The first part of the example then becomes something like

{m,n,p,q}::Symbol;
a_{n?}::SelfAntiCommuting;
ad_{n?}::SelfAntiCommuting;
ad_{n?}::LaTeXForm("a^\dagger").
{a_{n?},ad_{m?} }::NonCommuting;

rl1:= a_{n?} ad_{n?} -> - ad_{n?} a_{n?} + 1; 
rl2:= a_{n?} ad_{m?} | n?!=m? -> - ad_{m?} a_{n?};  

Note that for symbols you need to specify wildcard question marks.

Now the post_process is something like

def post_process(ex):
   # move all a operators without dagger to the right
   converge(ex):
      substitute(ex, rl2)
      substitute(ex, rl1)
      distribute(ex)
      eliminate_kronecker(ex)
   sort_product(ex)
   collect_terms(ex)

which will turn

 ex:= a_{m} ad_{m} a_{m};

into $a_{m}$.

Sorry for the late reply. Thank you very much for help!

...