The other day, I brought a notebook of a library on a related issue.
http://library.wolfram.com/infocenter/Conferences/325/
How to extend the arithmetic of differential operators in mathematics
I will take the appropriate code. First I mention (again) that I will define and work with my own non-commutative operator to avoid matching headaches with the built-in NonCommutativeMultiply. In addition, I will use [...] instead of the subscript [a, ...] to simplify the writing of ascii and cutting out the Mathematica insert / exit.
We will classify certain βbasicβ entities as scalars or variables, the latter are things that have commutation restrictions. I do not take it almost as far as it would be possible, and I only define scalars to be obvious enough "unchangeable".
variableQ[x_] := MemberQ[{a, b, c, d}, Head[x]] scalarQ[x_?NumericQ] := True scalarQ[x_[a_]^n_. /; !variableQ[x[a]]] := True scalarQ[_] := False ncTimes[] := 1 ncTimes[a_] := a ncTimes[a___, ncTimes[b___, c___], d___] := ncTimes[a, b, c, d] ncTimes[a___, x_ + y_, b___] := ncTimes[a, x, b] + ncTimes[a, y, b] ncTimes[a___, n_?scalarQ*c_, b___] := n*ncTimes[a, c, b] ncTimes[a___, n_?scalarQ, b___] := n*ncTimes[a, b] ncTimes[a___, x_[i_Integer]^m_., x_[i_]^n_., b___] /; variableQ[x[i]] := ncTimes[a, x[i]^(m + n), b] ncTimes[a___, x_[i_Integer]^m_., y_[j_Integer]^n_., b___] /; variableQ[x[i]] && ! OrderedQ[{x, y}] := (* !!! *) ncTimes[a, y[j]^n, x[i]^m, b]
I will use your input form, only slightly modified, so we will convert ** expressions to use ncTimes.
Unprotect[NonCommutativeMultiply]; NonCommutativeMultiply[a___] := ncTimes[a]
Here is your example.
In[124]:= a[-4] ** b[1] ** a[-4] ** b[-4] ** a[1] ** c[-4] ** c[1] ** c[5] Out[124]= ncTimes[a[-4]^2, a[1], b[1], b[-4], c[-4], c[1], c[5]]
The advantage of this seemingly time-consuming method is that you can easily identify switches. For example, we have already (implicitly) applied this when formulating the above rules.
commutator[x_[a_], y_[b_]] /; x =!= y || !VariableQ[x[a] := 0
In general, if you have switching rules, such as
ncTimes[a[j],a[i]] == ncTimes[a[i],a[i]]+(ji)*a[i]
whenever j> i, you can canonicalize, say, by putting [i] before [j] in all expressions. To do this, you will need to change the rule marked (!!!) to account for such switches.
I must add that in no sense have I fully tested the above code.
Daniel Lichtblow Wolfram Research