Is it possible to get the order of inputs when "overloading" an orderless function?

16

6

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?

jVincent

Posted 2012-05-11T09:56:53.113

Reputation: 14 423

Check out this answer to my question about block inversion. A package called NCAlgebra may help.

– Eli Lansey – 2012-05-11T10:25:37.893

Answers

15

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

Posted 2012-05-11T09:56:53.113

Reputation: 108 027

I tried to put something like this together and failed. Once again you prove your mastery. :-) – Mr.Wizard – 2012-05-11T21:14:31.747

@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 – 2012-05-11T21:16:03.127

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 – 2012-05-11T21:17:32.053

@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 – 2012-05-11T21:24:16.500

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 – 2012-05-13T22:06:08.237

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 – 2012-05-14T07:02:01.537

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.

gpap

Posted 2012-05-11T09:56:53.113

Reputation: 9 237

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 – 2012-05-11T10:45:57.127

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 – 2012-05-11T10:51:04.670

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 – 2012-05-11T10:56:51.497

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 – 2012-05-11T11:27:45.730

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 – 2012-05-11T12:38:18.763

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 – 2012-05-11T13:34:25.230

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 – 2012-05-11T13:38:08.003

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 – 2012-05-12T14:22:08.793

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

Posted 2012-05-11T09:56:53.113

Reputation: 259 163

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

Posted 2012-05-11T09:56:53.113

Reputation: 821