We are in the process of making this properly supported, but in the meantime, give this function a shot (you need to build Cadabra from source for this, the functionality is not yet in any of the binary packages):
def expand_conjugate(ex):
tst:= (A??)^{\dagger};
for node in ex:
if tst.matches( node ):
rep=$P$
lst=[]
for prod in node["\\prod"]:
for factor in prod.factors():
lst.append($ @(factor) $)
for factor in list(reversed(lst)):
rep.top().append_child($ @(factor)^{\dagger} $)
rep.top().name=r"\prod"
node.replace(rep)
return ex
If you stick the above in a cell and evaluate it, you can then do
\dagger::Symbol;
ex:= (A B C)^{\dagger} + Q + (D E)^{\dagger};
expand_conjugate(ex);
to produce
$$C^{\dagger} B^{\dagger} A^{\dagger} + Q + E^{\dagger} D^{\dagger}$$
There will be better support for generic conjugation operations in Cadabra soon.
For the other two lines, try simple substitution with a rule of the type
rl:= { (A?^{\dagger})^{\dagger} = A?,
( (A??)^{\dagger} )^{\dagger} = A??,
A?^{\dagger} A? = 1,
A? A^{\dagger} = 1,
(A??)^{\dagger} A?? = 1,
A?? (A??)^{\dagger} = 1 };
You can then do e.g.
\dagger::Symbol;
{A,B,U}::NonCommuting;
ex:= ( U^{\dagger} )^{\dagger} + U^{\dagger} U
+ (A B)^{\dagger} + ( (A B)^{\dagger} )^{\dagger};
substitute(ex, rl);
Hope this helps.