A simple implementation of higher-spin calculus (explanation given)?

I'm a physicist studying higher-spin theory. In my research, we work with fully symmetric tensors using a notation which is implicit both in the dimension of space and the order of the tensor, i.e. an order-$s$ fully symmetric tensor is simply written as $$\phi_{\mu_1 \cdots \mu_s} \equiv \phi.$$ The $n$-th gradient of $\phi$ is written as $\partial^n \phi$, the $n$-th divergence, as $\partial^n \cdot \phi$ and the $n$-th trace as $\phi^{[n]}$. Lower traces are simply written with a prime, e.g. $\phi''$ for the second trace. All indices are implicitly symmetrized, without weight factors, using the minimal number of terms.
For example, if $s=2$, $$\partial^2 \phi \equiv \partial_\mu \partial_\nu \phi_{\sigma \rho} + \partial_\mu \partial_\sigma \phi_{\nu \rho} + \partial_\mu \partial_\rho \phi_{\sigma \nu} + \partial_\nu \partial_\sigma \phi_{\mu \rho} + \partial_\nu \partial_\rho \phi_{\sigma \mu} + \partial_\sigma \partial_\rho \phi_{\mu \nu}$$ $$\partial (\partial \cdot \phi) \equiv \partial_\nu (\partial^\lambda \phi_{\mu \lambda}) + \partial_\mu (\partial^\lambda \phi_{\lambda \nu})$$ $$\eta \partial^2 \cdot \phi \equiv \eta_{\mu\nu} \partial^\rho \partial^\sigma \phi_{\rho \sigma}$$ The formalism implies the following set of rules $$( \partial^p \phi )' = \Box \partial^{p-2} \phi + 2 \partial^{p-1} \left( \partial \cdot \phi \right) + \partial^p \phi'$$ $$\partial \cdot (\partial^p \phi) = \Box \partial^{p-1} \phi + \partial^p \left( \partial \cdot \phi \right)$$ $$\partial^p \partial^q = {{p+q}\choose{q}} \partial^{p+q}$$ which you can simply take as axioms of the game. By the way, $\Box = \partial \cdot \partial$, which we are allowed to invert to $\frac{1}{\Box}$, but don't worry about what this means, you can just use the three rules written above.

PROBLEM

As you can see, these are very simple objects and I was able to write a C++ code implementing these objects. Basically I just created a data type specifying a symbol for the tensor, its rank, the number of traces, the number of divergences, the number of gradients, the number of $\Box$-operators and the numerical multiplicative factor, along with some obvious algebraic constraints (for example, 3-divergence of a 2-tensor is trivially zero).

I also implemented functions on these objects, which take gradients, divergences or traces, along with the multiplication by numbers and addition of such objects.

I would really like to implement this in Mathematica, since I can't get everything I need out of C++, but I'm not sure where to start with defining these objects and functions on them. I don't want to use a pre-existing package, I'd like to build it from scratch, with as little unnecessary baggage as possible for full functionality. Any ideas? All tips are greatly appreciated.

Please do tell me if you want me to clarify anything.

EDIT 1

By lower traces I simply mean a number of traces low enough to actually write out the primes explicitly, so, for example, $\phi''''$ could just be written as $\phi^{[4]}$.

EDIT 2

I provide here the explicit form of first two $\mathcal{F}_n$ after $n=0$.

$$\mathcal{F}_1 = \Box \phi - \partial (\partial \cdot \phi) + \partial^2 \phi'$$

$$\mathcal{F}_2 = \Box \phi - \partial (\partial \cdot \phi) + \frac{2}{3 \Box} \partial^2 (\partial^2 \cdot \phi) + \frac{1}{3} \partial^2 \phi' - \frac{1}{\Box} \partial^3 (\partial \cdot \phi') + \frac{1}{\Box} \partial^4 \phi''$$

EDIT 3

Note that explicit indices are not required in this formulation, I only wrote the explicit forms to show where the notation comes from. I do not need to implement indices in the code.

• Perhaps the LHS of the $\partial^2\phi$ equation should read something like $(\partial^2\phi)_{\mu\nu\sigma\rho}$? Or perhaps $\partial^2\phi \to (\partial^2\phi)_{\mu\nu\sigma\rho} \equiv ...$? – jjc385 Oct 24 '17 at 20:42
• It's just notation, to promote it to a map seems like an overkill to me. – PhysSE is Cancer Oct 24 '17 at 21:50

This isn't a complete answer, but it should be enough to point you in one possible direction. Forgive me for the rambling nature -- I don't have terribly much time, but I thought this question deserved an answer.

Dealing with semi-explicit indices is hard

Most of what you seek is fairly straightforward, with the exception of dealing with indices. I've dealt with indices both in gauge theories (Standard Model calculations with unbroken gauge symmetry) and in general relativity, in both cases writing all the code myself. In my experience, attempting to deal with indices -- especially dummy indices -- leads to

• a lot of complications
• a lot of choices that need to be made in representing them
• a lot of additional structures/ syntactical sugar that needs to be added to deal with them more naturally
• a lot of frustration that your initial choices didn't better facilitate some extension you later wish to implementation-of-higher-spin-calculus-explanation-given

The point is that finding a nice way to deal with indices is tough, which I'm guessing is why this question hasn't received much attention.

How I handle (Lorentz) indicies

I'll mention some functionality I've written for dealing with (Lorentz) indices. This was developed with GR in mind, though it should also work well in flat space. A decent amount of the structure probably wouldn't be needed for gauge indices, but luckily you're interested in Lorentz indices.

The following is a sketch of my full GR/index 'package' (I put 'package' in quotes because it's really just a notebook I run at the start of my calculations. I've saved the notebook as a package file and included it in full in this github repository. As noted there, I've made no effort to make it more readable, though I hope to do so someday.

I have a head inds which both declares the size of a tensor and gives its explicit indices:

inds[ ϕ, {μ,ν}, "l" ]


Where the last argument is always a single string containing "u" and "l", for upper and lower indices. If the string is shorter than the index list, the unspecified indices are assumed to be in the same position as the last character in the string. (This is made explicit in the DownValues of inds -- inds[ ϕ, {μ,ν}, "ll" ] would actually evaluate to the above expression.)

I also have a head indexDeriv :

indexDeriv[ inds[ ... ], x, ρ, "l" ] &


(I have syntax sugar pdl and pdu which apply as pdl[ρ]@inds[...] and evaluate to a full indexDeriv expression.) I have nearly identical structures for the covariant derivative covarDeriv, which I think is identified with the partial derivative in your case.

All of these heads format nicely (albeit in a way that can't be copied and pasted as input).

I have a function wrapInds which takes nested index functions (for instance, indexDeriv[indexDeriv[inds[ ... ]]]) and wraps them in a single inds head, with all indices accessible at the top level.

I also have a metric function (which formats as g or η, depending on the context), then some rules that explicitly raise and lower indices.

Finally, I have functions that find both free and dummy indices in an expression, allowing one to manipulate them and perform transformations.

I prefer to implement transformations using rules rather than DownValues (i.e., but making function definitions). I originally used the opposite philosophy, but then I found I had insufficient control over everything that happens. For example, consider the product rule for derivatives. I recommend doing something like

(* I do this *)
productRule = indexDeriv[ A_ B_, args__ ]
:> With[ {f=indexDeriv[#,args]&}, f[A]*B + B*f[A] ]


rather than

(* I don't do this *)
indexDeriv[ A_ B_, args__ ] := With[ {f=indexDeriv[#,args]&}, f[A]*B + B*f[A] ]


Beyond indices

Once you have your framework for dealing with indices, you can relate it to an index-free formalism. For instance, you could use the following heads :

symTensor[ϕ, 2]
% // toExplicitIndices @ {μ, ν, ρ, σ}
`

I could write some code for these functions, but I think you're interested in writing such things yourself. I'm happy to clarify or point you in the right direction.

Postscript

As mentioned above, my full package for dealing with indices is located in this github repository.

Again, I'm happy to clarify anything, and provide more example code here at request.

• Thanks, I'll see what I can do with this :) But the point of that notation was to get rid of indices in the first place and never worry about them, the formalism is fully consistent as long as you're working with fully symmetric tensors (which is the case here). – PhysSE is Cancer Oct 24 '17 at 21:53
• @IvanV. Well darn. I interpreted your question differently. It seemed to me that you wanted to move between index-free and index-full notation. I'm guessing from the lack of activity that others interpreted the question similarly, because this questions is much easier to answer if one needs not deal with indicies. (Rereading your question, it now seems more clear, but perhaps its worth emphasizing in the question that you're interesting in staying index-free.) – jjc385 Oct 24 '17 at 22:00
• I don't have time to write anything now, but if nobody else answers in the next day or so, I'll try to write something up that better answers your actual question. Note that you're free to self-answer with an attempt (even a partial attempt), even if it borrows from my answer. – jjc385 Oct 24 '17 at 22:03
• Sleepy time :) I will definitely edit it tomorrow to make it more clear, thanks for the suggestion! Your answer is still valuable, though, I appreciate the contribution. – PhysSE is Cancer Oct 24 '17 at 22:08