5

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

Dr Joe
  • 718
  • 5
  • 19
  • Ok, so one part is easy. With application of a simple rule: `RowBox[{"e","f","*",RowBox[{"g","**","h"}],"*","i","j"}] /. "*" -> "**"`, I can transform explicitly stated multiplation. But, I'm puzzling on how to insert a '**' between two non-operator strings. How can I detect the difference between an operator and a symbol, I wonder. – Dr Joe Dec 03 '11 at 13:07
  • I think that it's best to not try to make (implicit) `Times` parse as `NonCommutativeMultiply`, but rather have the `NonCommutativeMultiply` stay explicit. You can give it a nicer `StandardForm` etc, by having it print and interpret as something like `CenterDot`. – Simon Dec 04 '11 at 03:27
  • Hi @Simon, it's definitely the input side I'm concerned about here, not the output side. I'll mop that up later if necessary. – Dr Joe Dec 04 '11 at 10:33
  • `CenterDot` can be entered using `⁝.⁝`, which is a minimal effort. I made heavy use of this in [arXiv:1102.3043](http://arxiv.org/abs/1102.3043) (Note, my implementation of anticommuting objects was slow and clumsy, but got the job done. ie I'm not proud of the code, but the results were nice!) – Simon Dec 04 '11 at 10:39
  • Dr Joe, I was going to welcome you to StackOverflow, but I see you have been here for seven months, just quietly. A belated welcome, in that case. – Mr.Wizard Dec 04 '11 at 19:28
  • @Simon, of course you could just use ** for NonCommutativeMultiply instead of CentreDot ;). – Dr Joe Dec 04 '11 at 21:04
  • @Mr.Wizard, thanks! Yes, I signed up a little while ago, but this is my first post :). – Dr Joe Dec 04 '11 at 21:05

4 Answers4

4

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.

Community
  • 1
  • 1
Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • Leonid, I argue that conceptually (rather than technically) `a - b` should remain `a - b` when following the rule: "... only those instances of Times which are explicitly present in the code would be replaced." – Mr.Wizard Dec 03 '11 at 18:50
  • @Mr.Wizard Well, `a-b` parses into `Plus[a,Times[-1,b]]`, this is not evaluation artifact. I think, this is ok - there is no primitive in the language for "-b". What is not ok is that the above code transforms *all* instances of `Times`, but this is why I said that the above functions are just illustrating the principle. In practice, one will have to write more specific transformation rules, but my point is that these may be easier written once the code has been parsed, even given cases like the one you just pointed out. – Leonid Shifrin Dec 03 '11 at 18:57
  • I understand *why* it happens, but I think it is not transparent, since there is no **explicit** `Times` in `a - b`. As you know there is `Minus[b]` but it is not parsed that way. (BTW, +1) – Mr.Wizard Dec 03 '11 at 20:08
  • @Mr.Wizard Ok, you have a point. But, here's the thing: programming in M differs from programming in most other languages (except Lisp-family) in that we program directly in the parse trees (code already parsed), and this is what is called symbolic programming. Now, for certain things, shortcuts are taken, to make them easier for us, and therefore M does have non-trivial syntax. Corner cases like this are anavoidable. One can either switch directly to "think in parse trees" mode which I advocate here, and then take care of these corner cases (or even, always program with normal M ... – Leonid Shifrin Dec 03 '11 at 20:43
  • 1
    @Mr.Wizard ... expressions and forget about extra convenient syntax), or write some sort of string preprocessors or use intermediate box representation, and change things on that level. My point is that whenever possible, the higher symbolic level is IMO preferred, since it is easier to work with and less error prone. Particularly, for the case at hand, I think it will still be easier than the lower-level method based on boxes. – Leonid Shifrin Dec 03 '11 at 20:46
  • @LeonidShifrin Thanks for your suggestions. I wasn't conscious of $Pre, and would have probably tried this approach first had I known. There is one downside to it though, which is that we have to reply on no-one else overloading $Pre in their code. This makes it a difficult implementation prospect for a module writer, as opposed to a user of the system. Or, is there a clean way around that too, that I'm unaware of? – Dr Joe Dec 03 '11 at 22:10
  • 1
    @Dr.Joe Within the approach I described, you could either wrap all your code in `withNoncommutativeMultiply` (if it's in one piece) and then not redefine `$Pre`, or write something like `If[$Pre=!=Unevaluated[$Pre],$Pre = With[{oldPre = $Pre},Function[code,withNoncommutativeMultiply[oldPre[code]],HoldAll],$Pre = withNoncommutativeMultiply]`, which would check `$Pre`, and if older definition is found, replace if by composition of the older one and `withNoncommutativeMultiply`. Although, I agree that this is still an intrusive operation. – Leonid Shifrin Dec 04 '11 at 16:41
  • @LeonidShifrin, right! It's a shame that there's no generalised plugin implementation for $Pre; it's a shame that modules can't use this transparently. – Dr Joe Dec 05 '11 at 21:08
  • @Dr Joe Agree. I hope this will be addressed in some not too distant future. – Leonid Shifrin Dec 06 '11 at 07:25
1

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.

magma
  • 626
  • 5
  • 15
  • Hi @magma, it looks to me that Notation is just a wrapper around MakeExpression (for the parsing direction) and output format for the other direction. Fundamentally it doesn't change much; my question is still relevant to using the Notation package, but more fundamental. What I'm actually wanting is to define some symbols as being non-commutative (commutesQ[x_]:=...), and to rebuild the parser that input expressions automatically use Times or NonCommutativeTimes in the right way depending upon the quality of the items being multiplied. – Dr Joe Dec 04 '11 at 19:14
  • The other thing that I should add is that to do anything interesting with Notation you need to make pattern matchers which act on boxes, and then it reduces to MakeExpression directly. It seems to me that the later is actually cleaner, as it doesn't involve embedded palette patterns to work properly. – Dr Joe Dec 04 '11 at 21:09
1

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]]
Dr Joe
  • 718
  • 5
  • 19
0

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.

Timo
  • 4,246
  • 6
  • 29
  • 42
  • Hi @Timo, I used to think that Mathematica was useless too when dealing with non-commuting objects. Somehow though I missed that it was possible to change the underlying parsing rules to change this. Today I found that I could teach mathematica that 1+1 == 3! :) It looks like it does have the necessary flexibility to make non-commuting algebra a first class citizen. (As an aside, I'm working on a general purpose geometric algebra package, and so clifford/grassman is at the heart of my enquiry). – Dr Joe Dec 03 '11 at 21:36
  • 1
    @Dr.Joe, well then you should have asked just that :-) here is a package that does grassmann/clifford algebras http://sites.google.com/site/grassmannalgebra/ – Timo Dec 03 '11 at 22:28
  • A long time ago I did add some non-commutative features to FeynCalc: http://www.feyncalc.org/FeynCalcBook/Commutator http://www.feyncalc.org/FeynCalcBook/DotSimplify etc. – Rolf Mertig Dec 03 '11 at 22:58
  • Hi @Timo, thanks for that. John Browne's package looks very interesting, and there's definitely a lot of implementation there. Personally I don't like depending upon palettes so heavily though. – Dr Joe Dec 04 '11 at 21:11