Cadabra's algorithms (in both version 1.x and 2.x) act 'in-place', that is to say, they modify the original expression. This is in contrast to many other computer algebra systems, which leave the original expression unmodified, but return a new expression (which, in terms of your example, you then have to assign explicitly to ex
if you want ex
to contain the 'current state').
So if you want to preserve the initial form of ex
, you need to make a copy before you start modifying it; something along the lines of
{m,n,p,q,r}::Indices(position=free);
\nabla{#}::Derivative;
\partial{#}::PartialDerivative;
A_{m n}::AntiSymmetric;
V_{m}::Depends(\partial{#});
orig:=\partial_{m}{V_{n} A_{m}} + \partial_{m}{A_{n m}}:
ex:=@(orig):
unwrap(_);
and then
ex2:=@(orig);
to get another copy of the original expression.
The _
symbol refers to 'the last expression that was modified', not 'the thing returned by the last function call'.
There are advantages and disadvantages of both approaches ('in-place' and 'functional'). Cadabra historically (and for very good reasons) used 'act in place' operations, and even if I wanted to (which I don't), it's too late to change that now.
(For completeness: note that ex:=@(orig)
is a Cadabra-style assignment, not a Python assignment. That is to say, when Cadabra encounters a @(...)
expression, it always makes a copy of the ...
. Contrast this with the Python assignment
ex=orig
which would simply give an additional name ex
to the orig
expression; acting on this
ex
would also modify the expression pointed to by orig
. Both ex
and orig
are handles (references) to the same expression object in this case).