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.