18

When working with symbolic matrix operations or other objects which don't have commutative multiplication, it would be nice to not have to constantly switch out times for NonCommutativeMultiply (**). So I was wondering if there is a way to do something like

matrix /: Times[pre___, a_matrix, post___] :=  NonCommutativeMultiply[pre, a, post]
a = matrix["a"];
b = matrix["b"];

and then have it replaced automatically. However since Times is orderless, this doesn't preserve the order. In general, is it impossible to get the order of inputs when doing this kind of "overloading" though upvalues of an orderless function?

rm -rf
  • 88,781
  • 21
  • 293
  • 472
jVincent
  • 14,766
  • 1
  • 42
  • 74

4 Answers4

16

Well, you can sort of do this, by creating something like a continuation. This requires playing games with the Stack, and I don't claim that it is robust, but it may represent some theoretical interest, particularly to those of us looking for ways to implement continuations in Mathematica. Here is the code (edit please note that I had to add Update[matrix] to address some improper behavior noted in the comments end edit):

ClearAll[matrix, inMatrix];
matrix /: HoldPattern[Times[pre___, a_matrix, post___]] :=
  (
      Update[matrix];
      ReleaseHold[NonCommutativeMultiply @@@ $stack]
  );
matrix[args__] /; ! TrueQ[inMatrix] :=
  Block[{inMatrix = True},
    Update[matrix];
    $stack = Stack[_][[-5]];
    matrix[args]]

It combines UpValues, Villegas-Gayley trick to redefine a function, and manipuations with Stack. What happens is that first, of course, the attributes of Times are applied, I can't fight that. Then, the DownValues of matrix are applied, and at this point I record the relevant part of the Stack. Then, UpValues of matrix are applied, and at that point I communicate the recorded part of the stack, where the attributes of Times weren't yet applied, and Times gets replaced with NonCommutativeMultiply, after which I re-evaluate this, as if it was not evaluated before. The Update sommand is used to prevent caching the values for the $stack, as this is inappropriate here and resulted in some erroneous behavior noted in the comments.

Here are some examples:

a = matrix["a"];
b = matrix["b"];

a*c*b

matrix[a]**c**matrix[b]

f[g[1 + c*a*d*b*e]]

f[g[1 + c ** matrix["a"] ** d ** matrix["b"] ** e]]

I would not probably recommend such tricks for serious use, it is just interesting that you can use them to divert evaluation sequence in ways which seem to be impossible otherwise.

Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
  • I tried to put something like this together and failed. Once again you prove your mastery. :-) – Mr.Wizard May 11 '12 at 21:14
  • @Mr.Wizard Well, thanks, but I am not really sure that actually using this code is a good idea. It may not be robust enough. I just like this direction of thought. – Leonid Shifrin May 11 '12 at 21:16
  • Really really cool. I never looked at Stack[] before, but was hoping something like this would be possible. Now comes the follow up question, how do you break it, or really when does this go horribly wrong? – jVincent May 11 '12 at 21:17
  • @jVincent I wasn't able to break this with a few tests, but I think it should be possible, and I did not try hard enough. For one thing, all code inside Times will be executed twice, and so if it contains side effects, those would also happen twice. But I think one can probably break this even more severely. For symbolic expressions, though, it may be ok. – Leonid Shifrin May 11 '12 at 21:24
  • I seem to be having some problems implementing this, and having removed any packages and additions, it seems to not work no my 8.0.4 installation. The problem seems to reside in the down-values for matrix never being called in the function pattern. Any clues as to what might be wrong, or where I should look for potential packages. Note, it works fine for matrix["a"] c matrix["b"] or similar , just not a c b as you showed. Also seems to work fine if a=matrix[""] is replaced with a:=matrix[""]. – jVincent May 13 '12 at 22:06
  • 1
    @jVincent Yes you are right, but this is rather subtle. The value of the $stack was cached and not re-computed as it should've been. Fixed now - I added the Update statements in appropriate places. – Leonid Shifrin May 14 '12 at 07:02
5

there is a package called NCAlgebra I used some time ago to handle non-commutative variables (not just in matrix representation but also quaternions etc) in a multitude of situations.

http://www.math.ucsd.edu/~ncalg/

I think it would solve your problem as it defines its own substitution and expansion rules. You get the option to define a variable as non-commutative and then "do stuff" to it. In your example, and using this package

SetNonCommutative[A, B]

would define A, B to be treated as non-commutative symbols and the order in multiplication will be preserved.

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
gpap
  • 9,707
  • 3
  • 24
  • 66
  • Looking thought the example notebooks from this package, it appears that they just use NonCommutativeMultiply exclusively, and provide an external framework for doing other manipulations. – jVincent May 11 '12 at 10:45
  • Yes, this is correct. But Mathematica cannot handle expansions in NonCommutativeMultiply and you'd need to type long expressions with **s yourself. I thought that's what triggered the question in the first place (?) – gpap May 11 '12 at 10:51
  • No, what triggered my question is just the annoyance at the fact that Times being defined as orderless in general makes it seamingly impossible to make it non orderless in individual cases based no the elements. Mathematically the communicative nature of multiplication is dependent on the elements, not the operator, I was hoping for some clever fix that didn't involve hunting down every single occurance of times, and replacing it, or blocking it out in order to retain order information. – jVincent May 11 '12 at 10:56
  • I see - in that case my answer is useless. So, in your example above, if you wanted multiplication over Z4 you would define a z4 "times" that would give you z4[a] z4[b]=Mod[a,b,4] but in your case wouldn't that transpose the problem into defining all your elements of z4 in an expression? – gpap May 11 '12 at 11:27
  • Naturally, but lets assume you have a large collection of functions that do things like f[a_,b_]:=a+b-b^2-b*a, or similar. Suddenly all of these expression would obay the nocommutativity of the newly defined matrix objects, if you called f[matrix[a],matrix[b]]. Really what I want is to have the evaluation depending on the upvalues of the members, but that's not possible, since Times is orderless independent of what it's used on. – jVincent May 11 '12 at 12:38
  • Hmmm - you still could use NCAlgebra in this case while using the same function definitions. You'd have to use a rule /. Times -> NonCommutativeMultiply (which, granted, is what you are trying to avoid) on all your functions and then define whichever variables you want to be either commuting or noncommuting via SetCommutative, SetNonCommutative. Assuming expressions of the form (1-q)(a b) for q real, a,b noncommuting, haven't come from, say, a**b - q b**a - which would be a problem you'd have anyway - you can save yourself some time.. – gpap May 11 '12 at 13:34
  • For the record, I am in no way affiliated with the NCAlgebra people :) I just tried their package and it saved my life so I'm gathering karma points really. – gpap May 11 '12 at 13:38
  • Note that if the functions were written with commutative multiplication in mind, there's a high probability that they don't work correctly for non-commutative multiplication (for example, they may implicitly rely on the binomial formula $(a+b)(a-b) = a^2-b^2$ which doesn't hold for non-commutative algebras; there it's $(a+b)(a-b) = a^2-b^2-[a,b]$ where $[a,b] = ab-ba$ denotes the commutator between $a$ and $b$, which of course vanishes for commutative multiplication). – celtschk May 12 '12 at 14:22
4

As far as I know this cannot be made to work in the manner you show so long as the Orderless property of Times remains, because this property is applied before pattern rules are applied.

You can of course do things such as Blocking Times but I cannot think of a way to get (automatically) the behavior you desire without bad side effects.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
2

What you want to do is tricky. You are mixing two things:

  1. You want noncommutative products based on upvalues of the symbols involved;
  2. You want to be able to replace the symbols by matrices.

Both can be achieved with NCAlgebra. The latest distribution is available from:

https://github.com/NCAlgebra/NC

The example from one of your comments in NCAlgebra would look like this:

<< NC`
<< NCAlgebra`
f[a_, b_] := a + b - b^2 - b ** a

Because a and b are noncommutative by default,

f[a, b]

results in

a + b - b ** a - b ** b

whereas

f[A, b]

results in

A + b - A b - b ** b

The second ** gets downgraded to a regular Times because A is commutative. This accomplishes task 1.

The task 2. is more subtle. If you want to substitute a and b for matrices you can use NCMatrixReplaceAll. For example:

AA = {{1, 2}, {3, 4}}
BB = {{-1, 0}, {1, 2}}

and

NCMatrixReplaceAll[f[a, b], {a -> AA, b -> BB}]

would result in

{{0, 4}, {-4, -8}}

which is what you get out of AA + BB - BB.BB - BB.AA, as you might expect. Note that the regular RepleceAll (\.) will fail because of the way Mathematica mixes Lists with other expressions when Plus is involved. This is why f[AA,BB] would also fail.

NCMatrixReplaceAll also works with matrices that have noncommutative entries. For example:

AA = {{1, d}, {c, 4}}
BB = {{a, b}, {1, d}}
NCMatrixReplaceAll[f[a, b], {a -> AA, b -> BB}]

returns

{{1 - b - a ** a - b ** c, -3 b + d - a ** b - a ** d - b ** d}, {-a + c - d - d ** c, 4 - b - 4 d - d ** d}}

which is what you would obtain from AA + BB - NCDot[BB, BB] - NCDot[BB, AA].

Mauricio de Oliveira
  • 2,001
  • 13
  • 15