Disclaimer: I'm thoroughly unfamiliar with NCAlgebra
, but I thought this question was a good excuse to take a look. I have done a fair amount of monkeying around with NonCommutativeMultiply
on my own.
First, notice that NCAlgebra
removes the attribute Flat
from NonCommutativeMultiply
(in fact, all of NonCommutativeMultiply
's attributes are removed).
Let's take a look at what actually happens in the evaluation of NCReplaceRepeated
. We can query
?? NCReplaceRepeated
NCReplaceAll[NCReplace`Private`expr_,NCReplace`Private`rule_]:=
ReplaceAll@@({NCReplace`Private`expr,NCReplace`Private`rule}/.
NCReplace`Private`NCReplaceFlatRules)/.
NCReplace`Private`NCReplaceReverseFlatRules
If you're unfamiliar with packages and contexts, NCReplace`Private`symbolName
just means it's a 'private' symbol in the NCReplace
package -- that is, a symbol the user doesn't usually need, and hence its name is hidden. (More properly, it's a symbol which belongs to a different Context
, but I won't get into that.)
In any case, let's first look at the values of NCReplace`Private`NCReplaceFlatRules
and NCReplace`Private`NCReplaceReverseFlatRules
:
NCReplace`Private`NCReplaceFlatRules
{NonCommutativeMultiply -> NCReplace`Private`FlatNCMultiply}
NCReplace`Private`NCReplaceReverseFlatRules
{NCReplace`Private`FlatNCMultiply -> NonCommutativeMultiply}
? NCReplace`Private`FlatNCMultiply
Attributes[NCReplace`Private`FlatNCMultiply]={Flat,OneIdentity}
? NonCommutativeMultiply
(* No attributes mentioned *)
Ah, ok. First, note that before loading NCAlgebra
, NonCommutativeMultiply
had attributes {Flat, OneIdentity, Protected}
. NCAlgebra
(kind of) mentions this in the docs :
The reason is that making an operator Flat is a convenience that comes with a price: lack of control over execution and evaluation. Since NCAlgebra has to operate at a very low level this lack of control over evaluation is fatal. Indeed, making NonCommutativeMultiply have an attribute Flat will throw Mathematica into infinite loops in seemingly trivial noncommutative expression. Hey, email us if you find a way around that :)
If you're unsure what the Flat
attribute does, the NCAlgebra
docs have a good explanation in this context at the start of section 5.1.
As the package mentions, problems (apparently, though believably) occur when a symbol has both Flat
attribute and NCAlgebra
's built-in definitions for dealing with commutative/non-commutative symbols. One such rule is the one that pulls powers of t
out of NonCommutativeMultiply
:
L ** t^2 ** M
t^2 L ** M
The whole point is that this can't happen automatically in NCReplace`Private`FlatNCMultiply
, because of the Flat
attribute. Why does this matter? Because what NCReplaceRepeated
is doing is precisely replacing all instances of NonCommutativeMultiply
(both in the expression you're replacing and the replacement rules themselves) with NCReplace`Private`FlatNCMultiply
, and then replacing back to NonCommutativeMultiply
at the end.
Let's take a look at what happens if we do this process ourselves:
L ** M^2 /. NCReplace`Private`NCReplaceFlatRules
NCReplace`Private`FlatNCMultiply[L, M, M]
% /. (L ** M -> t^2 M ** L /. NCReplace`Private`NCReplaceFlatRules)
NCReplace`Private`FlatNCMultiply[ t^2 NCReplace`Private`FlatNCMultiply[M, L], M]
% /. NCReplace`Private`NCReplaceReverseFlatRules
t^2 M ** L ** M
We can see the issue in the second-to-last step: the t^2
prevents NCReplace`Private`FlatNCMultiply
from flattening -- the flattening only occurs after converting back to NonCommutativeMultiply
.
This is the reason why applying NCReplaceRepeated
a second time works -- everything is flattened out by the end of the first call, allowing the replacement to happen in the second call.
What can we do about it?
Quick and dirty
Note that
NCReplaceRepeated[L ** M ** M, A___ ** L ** M ** B___ :> t^2 A ** M ** L ** B]
t^4 M ** M ** L
works right away. Of course, this requires putting all your rules into such a 'non-flat' form manually. You could try to automate that, but the following method is hopefully more robust.
More elegant solution
We can define our own function:
ClearAll@NCreallyReplaceRepeated
NCreallyReplaceRepeated[expr_, rule_] :=
FixedPoint[ NCReplaceRepeated[#, rule] &, expr]
NCreallyReplaceRepeated[L ** M ** M, L ** M -> t^2 M ** L]
t^4 M ** M ** L
Small caveat
Note that the above involves doing the exact same ReplaceAll on the transformation rules themselves within each iteration of FixedPoint
. Usually this is fine, though it could be the bottleneck for very long rules. To avoid this, I recommend instead:
ClearAll@NCreallyReplaceRepeated2
NCreallyReplaceRepeated2[expr_, rule_] :=
With[{ruleTrans = rule /. NCReplace`Private`NCReplaceFlatRules},
FixedPoint[
ReplaceAll[NCReplace`Private`NCReplaceReverseFlatRules]
@*(ReplaceRepeated[#, ruleTrans] &)
@*ReplaceAll[NCReplace`Private`NCReplaceFlatRules],
expr]
]
NCreallyReplaceRepeated2[L ** M ** M, L ** M -> t^2 M ** L]
t^4 M ** M ** L