Times and NonCommutativeMultiply, handing the difference automatically

1.4k Views Asked by At

I've got some symbols which should are non-commutative, but I don't want to have to remember which expressions have this behaviour whilst constructing equations.

I've had the thought to use MakeExpression to act on the raw boxes, and automatically uplift multiply to non-commutative multiply when appropriate (for instance when some of the symbols are non-commutative objects).

I was wondering whether anyone had any experience with this kind of configuration.

Here's what I've got so far:

(* Detect whether a set of row boxes represents a multiplication *)

Clear[isRowBoxMultiply];
isRowBoxMultiply[x_RowBox] := (Print["rowbox: ", x]; 
  Head[ToExpression[x]] === Times)
isRowBoxMultiply[x___] := (Print["non-rowbox: ", x]; False)

(* Hook into the expression maker, so that we can capture any \
expression of the form F[x___], to see how it is composed of boxes, \
and return true or false on that basis *)

MakeExpression[
  RowBox[List["F", "[", x___, "]"]], _] := (HoldComplete[
   isRowBoxMultiply[x]])

(* Test a number of expressions to see whether they are automatically \
detected as multiplies or not. *)
F[a]
F[a b]
F[a*b]
F[a - b]
F[3 x]
F[x^2]
F[e f*g ** h*i j]

Clear[MakeExpression]

This appears to correctly identify expressions that are multiplication statements:

During evaluation of In[561]:= non-rowbox: a
Out[565]= False

During evaluation of In[561]:= rowbox: RowBox[{a,b}]
Out[566]= True

During evaluation of In[561]:= rowbox: RowBox[{a,*,b}]
Out[567]= True

During evaluation of In[561]:= rowbox: RowBox[{a,-,b}]
Out[568]= False

During evaluation of In[561]:= rowbox: RowBox[{3,x}]
Out[569]= True

During evaluation of In[561]:= non-rowbox: SuperscriptBox[x,2]
Out[570]= False

During evaluation of In[561]:= rowbox: RowBox[{e,f,*,RowBox[{g,**,h}],*,i,j}]
Out[571]= True

So, it looks like it's not out of the questions that I might be able to conditionally rewrite the boxes of the underlying expression; but how to do this reliably?

Take the expression RowBox[{"e","f","*",RowBox[{"g","**","h"}],"*","i","j"}], this would need to be rewritten as RowBox[{"e","**","f","**",RowBox[{"g","**","h"}],"**","i","**","j"}] which seems like a non trivial operation to do with the pattern matcher and a rule set.

I'd be grateful for any suggestions from those more experienced with me.

I'm trying to find a way of doing this without altering the default behaviour and ordering of multiply.

Thanks! :)

Joe

4

There are 4 best solutions below

9
On

This is not a most direct answer to your question, but for many purposes working as low-level as directly with the boxes might be an overkill. Here is an alternative: let the Mathematica parser parse your code, and make a change then. Here is a possibility:

ClearAll[withNoncommutativeMultiply];
SetAttributes[withNoncommutativeMultiply, HoldAll];
withNoncommutativeMultiply[code_] :=
  Internal`InheritedBlock[{Times},
     Unprotect[Times];
     Times = NonCommutativeMultiply;
     Protect[Times];
     code];

This replaces Times dynamically with NonCommutativeMultiply, and avoids the intricacies you mentioned. By using Internal`InheritedBlock, I make modifications to Times local to the code executed inside withNoncommutativeMultiply.

You now can automate the application of this function with $Pre:

$Pre  = withNoncommutativeMultiply;

Now, for example:

In[36]:= 
F[a]
F[a b]
F[a*b]
F[a-b]
F[3 x]
F[x^2]
F[e f*g**h*i j]

Out[36]= F[a]
Out[37]= F[a**b]
Out[38]= F[a**b]
Out[39]= F[a+(-1)**b]
Out[40]= F[3**x]
Out[41]= F[x^2]
Out[42]= F[e**f**g**h**i**j]

Surely, using $Pre in such manner is hardly appropriate, since in all your code multiplication will be replaced with noncommutative multiplication - I used this as an illustration. You could make a more complicated redefinition of Times, so that this would only work for certain symbols.

Here is a safer alternative based on lexical, rather than dynamic, scoping:

ClearAll[withNoncommutativeMultiplyLex];
SetAttributes[withNoncommutativeMultiplyLex, HoldAll];
withNoncommutativeMultiplyLex[code_] :=
  With @@ Append[
      Hold[{Times = NonCommutativeMultiply}], 
      Unevaluated[code]]

you can use this in the same way, but only those instances of Times which are explicitly present in the code would be replaced. Again, this is just an illustration of the principles, one can extend or specialize this as needed. Instead of With, which is rather limited in its ability to specialize / add special cases, one can use replacement rules which have similar semantics.

2
On

If I understand correctly, you want to input a b and a*b and have MMA understand automatically that Times is really a non commutative operator (which has its own -separate - commutation rules). Well, my suggestion is that you use the Notation package. It is very powerful and (relatively) easy to use (especially for a sophisticated user like you seem to be). It can be used programmatically and it can reinterpret predefined symbols like Times. Basically it can intercept Times and change it to MyTimes. You then write code for MyTimes deciding for example which symbols are non commuting and then the output can be pretty formatted again as times or whatever else you wish. The input and output processing are 2 lines of code. That’s it! You have to read the documentation carefully and do some experimentation, if what you want is not more or less “standard hacking” of the input-output jobs. Your case seems to me pretty much standard (again: If I understood well what you want to achieve) and you should find useful to read the “advanced” pages of the Notation package. To give you an idea of how powerful and flexible the package is, I am using it to write the input-output formatting of a sizable package of Category Theory where noncommutative operations abound. But wait! I am not just defining ONE noncommutative operation, I am defining an unlimited number of noncommutative operations. Another thing I did was to reinterpret Power when the arguments are categories, without overloading Power. This allows me to treat functorial categories using standard mathematics notation. Now my “infinite” operations and "super Power" have the same look and feel of standard MMA symbols, including copy-paste functionality.

4
On

This answer does not address your question but rather the problem that leads you to ask that question. Mathematica is pretty useless when dealing with non-commuting objects but since such objects abound in, e.g., particle physics, there are some usefull packages around to deal with the situation.

Look at the grassmanOps package. They have a method to define symbols as either commuting or anti-commuting and overload the standard NonCommutativeMultiply to handle, i.e. pass through, commuting symbols. They also define several other operators, such as Derivative, to handle anti-commuting symbols. It is probably easily adapted to cover arbitrary commutation rules and it should at the very least give you an insigt into what things need to be changed if you want to roll your own.

0
On

So, this doesn't directly answer the question, but it's does provide the sort of implementation that I was thinking about.

So, after a bit of investigation and taking on board some of @LeonidShifrin's suggestions, I've managed to implement most of what I was thinking of. The idea is that it's possible to define patterns that should be considered to be non-commuting quantities, using commutingQ[form] := False. Then any multiplicative expression (actually any expression) can be wrapped with withCommutativeSensitivity[expr] and the expression will be manipulated to separate the quantities into Times[] and NonCommutativeMultiply[] sub-expressions as appropriate,

In[1]:= commutingQ[b] ^:= False;
In[2]:= withCommutativeSensitivity[ a (a + b + 4) b (3 + a) b ]
Out[1]:= a (3 + a) (a + b + 4) ** b ** b

Of course it's possible to use $Pre = withCommutativeSensitivity to have this behaviour become default (come on Wolfram! Make it default already ;) ). It would, however, be nice to have it a more fundamental behaviour though. I'd really like to make a module and Needs[NonCommutativeQuantities] at the beginning of any note book that is needs it, and not have all the facilities that use $Pre break on me (doesn't tracing use it?).

Intuitively I feel that there must be a natural way to hook this functionality into Mathematica on at the level of box parsing and wire it up using MakeExpression[]. Am I over extending here? I'd appreciate any thoughts as to whether I'm chasing up a blind alley. (I've had a few experiments in this direction, but always get caught in a recursive definition that I can't work out how to break).

Any thoughts would be gladly received, Joe.

Code

Unprotect[NonCommutativeMultiply];
ClearAll[NonCommutativeMultiply]
NonCommutativeMultiply[a_] := a
Protect[NonCommutativeMultiply];

ClearAll[commutingQ]
commutingQ::usage = "commutingQ[\!\(\*
    StyleBox[\"expr\", \"InlineFormula\",\nFontSlant->\"Italic\"]\)] \
    returns True if expr doesn't contain any constituent parts that fail \
    the commutingQ test. By default all objects return True to \
    commutingQ.";
commutingQ[x_] :=  If[Length[x] == 0, True, And @@ (commutingQ /@ List @@ x)]

ClearAll[times2, withCommutativeSensitivity] 
SetAttributes[times2, {Flat, OneIdentity, HoldAll}]
SetAttributes[withCommutativeSensitivity, HoldAll];

gatherByCriteria[list_List, crit_] := 
 With[{gathered = 
    Gather[{#, crit[#1]} & /@ list, #1[[2]] == #2[[2]] &]},
        (Identity @@ Union[#[[2]]] -> #[[1]] &)[Transpose[#]] & /@ gathered]

times2[x__] := Module[{a, b, y = List[x]}, 
    Times @@ (gatherByCriteria[y, commutingQ] //.
      {True -> Times, False -> NonCommutativeMultiply, 
       HoldPattern[a_ -> b_] :> a @@ b})]

withCommutativeSensitivity[code_] := With @@ Append[
    Hold[{Times = times2, NonCommutativeMultiply = times2}], 
    Unevaluated[code]]