Graphical Methods for Sharp Prediction: from theories entirely formal to the utterly effective

Lectures given in Amplitudes 2017 Summer School.

John Joseph M. Carrasco,

These were live-typed while giving lectures, then cleaned up prior to final posting. Video should be available at the summer school site: https://indico.ph.ed.ac.uk/event/30/

I grayed out the input cells to be less distracting when projecting as I typed.  (I wanted people to focus on the output.)  Feel free to edit the style sheet to make the input cells stand out as much as you like.

If you’re reading this as a pdf, I suspect you probably want to go after the Mathematica version -- it will be more readable.  You can look at it, manipulate graphs etc w/ Wolfram Reader (free to download).  If you have mathematica -- you can of course use it as a source to start calculating  questions of your own interest.

I will be typing a more thorough version that actually contains more of what I said while giving the lectures as well as providing additional background.  Please feel free to send me any questions!

Referencing:

J.J.M.Carrasco, Graphical Methods for Sharp Prediction: from theories entirely formal to the utterly effective, Amplitdues 2017 Summer School,  doi:10.6084/m9.figshare.5197213

Please see github repository ( https://github.com/drjjmc/ampGraphTools_mma ) , or email me, for appropriate references given use of the ampGraphTools library.

Initialization Cells (Load code library etc)

This is a recent version of a library I’ve built up over time.  Repository: https://github.com/drjjmc/ampGraphTools_mma

Import["https://raw.githubusercontent.com/drjjmc/ampGraphTools_mma/master/ampGraphTools/ampGraphTools.m"]

Feel free to play with / fork /use however you like (check out the license on the github repo.).  If your use of this code results in publication please look to the repository to find requested citation practice or send me an email. -JJ

fancyFormatOn  (* pretty prints dot products etc. 
 use fancyFormatOff to get straight InputForm output -- much better for terminal usage etc. *)

Lecture 1: Graphs and Ordered Amplitudes

Graphs...

Graphs are fantastic -- their purpose in life is to establish relationships, and their grammar is flexible.  At the core we need vertices (or nodes), or edges ( or legs).

Edges connect nodes, and nodes connect edges.   I’m happy using the word relate instead of connect. The nodes can be anything, as can the edges.

Graph3D[{
Labeled[fish\[DirectedEdge]water,"like"],
Labeled[dolphins \[DirectedEdge]water,"like"],
Labeled[dolphins \[DirectedEdge]fish,"like to eat"]},VertexLabels->Automatic, EdgeLabels->Automatic]

You can talk about whatever you want with graphs -- nodes can be any part of speech, as can edges.  As you go through life you might find yourself wanting to logically explore more and more complicated tasks. Graphs can help organize and establish such exploration.

We’ll discuss 2 types of graphs here:

1. Scaffolding graphs: Momentum-flow,  (color-flow) or charge-flow graphs.   These are the core-atoms of our predictions.  Individually they’ll have no physical meaning other than a stab at locality.  When dressed with various data, and combined they can form gauge invariant objects which we’ll recognize as physical predictions.

treesToLoops[{Atree[k/@Range[7]]}]//First//doOrderedPlot[#,k/@Range[7]]&//cutDisplayRule

2. Graph of Graphs:  Every vertex is a Scaffolding Graph.  Can be every graph that contributes to an ordered or full amplitude.  The edges relate edges of individual graphs.  These represent entire predictions (at least classically) -- can sum over every scaffolding graph weighted appropriately to arrive at a gauge invariant prediction. I’ll speak more of this in a little bit when I introduce Color-Kinematics.

Four Point Example:

All the cubic graphs that combine to make a 4-pt scattering amplitude.

The resulting graph of graphs (GoG):

{tGraph,uGraph,sGraph}=doOrderedPlot[#,k/@Range[4]]&/@ treesToLoopsUO[{Atree[k/@Range[4]]}]//cutDisplayRule;

Graph[{Labeled[s\[UndirectedEdge]t,Style["(OverHat[t])",{Bold,Large,Blue}]],Labeled[t\[UndirectedEdge]u,Style["(OverHat[t])∘(OverHat[u])",{Bold,Large,Blue}]],Labeled[s\[UndirectedEdge]u,Style["(OverHat[u])",{Bold,Large,Blue}]]},VertexLabels->({s->sGraph,t->tGraph,u->uGraph}/.Rule[a_,b_]:>a->Placed[b,{1/2,1/2}])]

Five point example

We can build this graph of graph automatically for five points, using thee very same operations (tHat and uHat), starting from an initial five-point-tree:

fivePointInitial=First[treesToLoops[{Atree[k/@Range[5]]}]]//priveledgeLegs;

{perm5,graphs5}=opTillClosure[fivePointInitial,uHat];

Timing[{tot5,graphs5tot}=opTillClosureOnCollection[graphs5,{tHat,uHat}];]

fivePlots=doOrderedPlotRand[stripPriveledge[#],k/@Range[5]]&/@(graphs5tot/@Range[15])/.in[___]:>""/.Tooltip[___]:>{}//cutDisplayRule;

Graph3D[tot5,VertexLabels->(MapIndexed[ #2[[1]]->#&,fivePlots]/.Rule[a_,b_]:>a->Placed[b,Tooltip])]

A few words: each triangle represents a possible Jacobi identity (s, t, and u around a given edge of a vertex).   To build the graphs contributing to full tree-level amplitude, operate with both (OverHat[t]) and (OverHat[u]) until closure.  To build just the graphs contributing to the color-ordered amplitude, can start with a graph of the appropriate color order and operate with (OverHat[t]) until closure.

Ordered Amplitudes.

Familiar to many in the audience: tree-level color-ordered (color-stripped) amplitudes in gauge theories  (see: e.g. Elvang and Huang).

A modern double-copy understanding is ordered/stripped amplitudes aren’t specific to particular theories, indeed they’re generic to classes of theories. They only become physical predictions when combined with the trace of relevant information:  Specific cases of ordered-amplitudes:

Double copy amplitudes (gravity, full color-dressed YM, CP dressed open string) can be written in terms of all independent cubic graphs:

𝒜=(∑_(g∈(Subscript[Γ, 3])))((n(g) (OverTilde[n])(g) )/(p(g)))=(∑_(ρ,σ)) A(ρ) (Subscript[S, 0])(ρ|σ) (OverTilde[A])(σ)=A⊗(OverTilde[A])

where (Subscript[Γ, 3]) are all cubic graphs of a given multiplicity, and ordered amplitudes A, or (OverTilde[A]) given by the sum over weights given to cubic graphs
A((Subscript[σ, 1]),...,(Subscript[σ, n]))=(∑_(g∈(Subscript[Γ, 3,σ])))((n(g))/(p(g)))
where (Subscript[Γ, 3, σ]) are all cubic graphs that share the order σ.

where:

So to talk about all of this we only need the graphs that contribute to ordered amplitudes -- although we will feel free to relabel them as necessary to discuss all independent ordered amplitudes.  The same graph will contribute to different ordered amplitudes with potential inclusion of signs as a result of vertex flips (antisymmetry around vertex order).

I’ll start by giving a recursive algorithm for building up the graphs (Subscript[σ, n+1]) if you know all the graphs for σ st |σ|=n.

Simple algorithm:
For all graphs in (Subscript[Γ, 3,σ]), add leg n+1 to every edge to the right of 1 (inclusive), and to the left of n (inclusive).

We’ll start with the cubic graph:  (Subscript[Γ, 3,{1,2,3}])={cubic(1,2,3)}

Two points :

1) We need to encode order -- and for that we’ll give structure to graph vertices -- we’ll make every vertex a necklace: an object that is identified under all cyclic permutations.  We will not identify necklaces related via acyclic permutations -- although as we recognize that all ways of dressing (color, kinematics, propagator structure) will only care up to a sign, once we make a canonical cyclic ordering choice for all necklaces associated with a topology, we will be free to identify the rest up to relevant introductions of appropriate signs.

2) We’ll use a specific labeling convention, where we label every vertex with an “outgoing” label.

color-order 3pt graph(s) (123)

threePointGraph= vertexFormGraph[{neckl[{k[1],k[2],k[3]}]}];

allThreeGraphs={threePointGraph};

doOrderedPlot/@allThreeGraphs//cutDisplayRule//Partition[#,3,3,{1,1},{}]&//GraphicsGrid//Show

color-order 4pt graph(s) (1234)

Adding an edge k[4] to edge k[3], means replacing k[3] with an internal edge in[1] on the cubic vertex (in the cubic necklace), connecting it to a new vertex (necklace) which contains k[3] and k[4] in the appropriate order:

fourPointGraphA= vertexFormGraph[{
neckl[{k[1],k[2],in[1]}],
neckl[{-in[1],k[3],k[4]}]}];

doOrderedPlot[fourPointGraphA,k/@Range[4]]//cutDisplayRule

Adding an edge k[4] to edge k[1], means replacing k[1] with an internal edge in[1] on the original cubic vertex, connecting it to a new vertex which contains k[4] and k[1] in the appropriate order:

fourPointGraphB=vertexFormGraph[
 {neckl[{in[1], k[2], k[3]}],
neckl[{-in[1], k[4], k[1]}]}];

doOrderedPlot[fourPointGraphB]//cutDisplayRule

So now we have all the color-ordered plots contributing to the 4-point ordered tree:  A(1234):

allFourPointGraphs={fourPointGraphA,fourPointGraphB};

(fourPointPlots=doOrderedPlot[#,k/@Range[4]]&/@allFourPointGraphs//cutDisplayRule)//Partition[#,2,2,{1,1},{}]&//GraphicsGrid//Show

color-order 5pt graph(s) (12345)

Three graphs from the 4pt s-graph, and two from the 4pt t:

allFive=treesToLoops[{Atree[k/@Range[5]]}];

fivePointPlots=doOrderedPlot[#,k/@Range[5]]&/@allFive//cutDisplayRule;

Graph[{t\[DirectedEdge]f1,t\[DirectedEdge]f2,s\[DirectedEdge]f3,s\[DirectedEdge]f4,s\[DirectedEdge]f5},VertexLabels->Flatten[{Thread[{s,t}->fourPointPlots],
Thread[{f1,f2,f3,f4,f5}->fivePointPlots]}/.Rule[a_,b_]:>a->Placed[b,{1/2,1/2}]]]

Side Note: conservation of momenta trivial with uniform all outgoing, or all ingoing convention:

List@@(allFourPointGraphs[[1]] /. neckl[a__]:>(Total[a]==0))//TableForm

Reduce[%//Flatten]

Lecture 2:  2-loop verification

So we’re going to learn graphical contributions cuts the way I learned graphical contributions to cuts, reproducing some results of: https://arxiv.org/abs/hep-th/9802162 .   The game is to dress the kinematic weights of YM cubic graphs

with the kinematic prefactor to the left of each graph.  Recall mandelstam s≡((((Subscript[k, 1])+(Subscript[k, 2])))^2)=((((Subscript[k, 3])+(Subscript[k, 4])))^2). This means that for graphs:

(DblBoxPlanarGraph= toGraph[{Atree[{k[1],in[5],in[6]}],
Atree[{k[2],in[7],-in[5]}],
Atree[{-in[7],in[8],in[9]}],
Atree[{-in[8],k[3],in[10]}],
Atree[{-in[10],k[4],in[11]}],
Atree[{-in[11],-in[6],-in[9]}]}]) // doOrderedPlot[#,k/@Range[4]]&

(NonPlanar2Loop=uHat[DblBoxPlanarGraph,in[7]])// doMomentaPlot

Note, this is the same as the above nonplanar graph from the paper (just move (Subscript[k, 2]) inside the graph):

They’ll both take on the dressings Mandelstam “s” == ((((Subscript[k, 1])+(Subscript[k, 2])))^2)

zviDressing[DblBoxPlanarGraph]=zviDressing[NonPlanar2Loop]=uLsq[{k[1],k[2]}]

If you’re still not seeing it note that in[6], and in[5] are drawn with a kink in the paper.

We consider two three-particle cuts.  These aren’t spanning (you need two particle cuts as well), but they are sufficiently complicated to be interesting.

I’ll be discussing something that doesn’t often appear in the literature -- how to identify and dress the graphs.  To consider the state sum in 4D, can look many places such as: http://arxiv.org/abs/arXiv:0903.5348 . I’ll carry out such a state sum later with my library, but there isn’t sufficient time in these lectures to cover it in any detail at all.

planar cut

This is the first cut, and you’ll notice because of the relative ordering of all trees that contribute to the cut, only planar graphs will be contributing.

firstCut=(∑_states)Atree({k[1],k[2],l[1],l[2],l[3]})×Atree({-l[3],-l[2],-l[1],k[3],k[4]})

firstCut={Atree[{k[1],k[2],l[3],l[2],l[1]}],Atree[{-l[1],-l[2],-l[3],k[3],k[4]}]}

doOrderedPlot[toGraph[firstCut],k/@Range[4]]//cutDisplayRule

Sewing on the “Feynman” side is a matter summing states running between the products on-shell tree-amplitudes:

firstCut=(∑_states)Atree({k[1],k[2],l[1],l[2],l[3]})×Atree({-l[3],-l[2],-l[1],k[3],k[4]})

For the maximally supersymmetric theory in 4 dimensions the cut legs can take on any of 16 states each:

pos helicity gluon (1)

pos helicity fermions (4)

real scalars (6)

neg helicity fermions (4)

neg  helicity gluon (1)

Sewing on the graphical side is a matter of identifying in-states and out-states from constituent cut graphs.

Here we build the leftTreeGraphs contributing to: ((Subscript[A, 5])^tree)((Subscript[𝓀, 1]),(Subscript[𝓀, 2]),(Subscript[ℓ, 3]),(Subscript[ℓ, 2]),(Subscript[ℓ, 1]))

First[firstCut]

leftTreeGraphs=treesToLoops[{First[firstCut]}];

doOrderedPlot[#,First[firstCut][[1]]]&/@leftTreeGraphs//cutDisplayRule//Partition[#,3,3,{1,1},{}]&//GraphicsGrid//Show

Here we build the rightTreeGraphs contributing to ((Subscript[A, 5])^tree)(-(Subscript[ℓ, 1]),-(Subscript[ℓ, 2]),-(Subscript[ℓ, 3]),(Subscript[𝓀, 3]),(Subscript[𝓀, 4]))

Last[firstCut]

rightTreeGraphs=treesToLoops[{Last[firstCut]}];

doOrderedPlot[#,Last[firstCut][[1]]]&/@rightTreeGraphs//cutDisplayRule//Partition[#,3,3,{1,1},{}]&//GraphicsGrid//Show

Sewing them together means literally taking the union of all the vertices (relabeling redundantly named internal edges as appropriate):

{MatrixForm[getVertices[leftTreeGraphs[[1]]]],doOrderedPlot[leftTreeGraphs[[1]],First[firstCut][[1]]]//cutDisplayRule}//List//GraphicsGrid//Show

{MatrixForm[getVertices[rightTreeGraphs[[1]]]],doOrderedPlot[rightTreeGraphs[[1]],
Last[firstCut][[1]]]//cutDisplayRule}//List//GraphicsGrid//Show

Note changing internal edges on the right graph so as to not conflict with left internal edge names in the union:

(sewnGraph=vertexFormGraph[Flatten[{getVertices[leftTreeGraphs[[1]]],
getVertices[rightTreeGraphs[[1]]]/.in[a_]:>in[a+2]}]])/.vertexFormGraph[a__]:>vertexFormGraph[MatrixForm[a]]

The sewn graph:

sewnGraph//doOrderedPlot[#,k/@Range[4]]&//cutDisplayRule

Here are all the sewn graphs:

Table[{{i,j},leftTreeGraphs[[i]]//doOrderedPlot[#,First[firstCut][[1]]]&//cutDisplayRule,
rightTreeGraphs[[j]]//doOrderedPlot[#,Last[firstCut][[1]]]&//cutDisplayRule,
sewGraphs[leftTreeGraphs[[i]],rightTreeGraphs[[j]]]//doOrderedPlot[#,k/@Range[4]]&//cutDisplayRule},{i,1,5},{j,1,5}]//Flatten[#,1]&//GraphicsGrid//Show

allCutGraphs=treesToLoops[firstCut]//Sort[#,OrderedQ[graphHashCode/@{#1,#2}]&]&;

Here we look at the 13 unique topologies:

GraphicsGrid[Partition[mathematicaGraph[#]&/@Union[allCutGraphs,SameTest->isIsomorphic]/.in[__]:>""/.l[__]:>""/.k[a_]:>"",3,3,{1,1},{}]]//Show

Some will show up more than once in the 25 potentially contributing cut-graphs:

GraphicsGrid[Partition[MapIndexed[(doOrderedPlot[#,k/@Range[4],PlotLabel->#2[[1]]]//cutDisplayRule)&,allCutGraphs],3,3,{1,1},{}]]//Show

nonVanishing=allCutGraphs[[{1,2,3}]];

MapIndexed[(doOrderedPlot[#,k/@Range[4],PlotLabel->#2[[1]]]//cutDisplayRule)&,nonVanishing]//List//GraphicsGrid//Show

first graph contribution ( t contribution)

MapIndexed[(doOrderedPlot[#,k/@Range[4],PlotLabel->#2[[1]]]//cutDisplayRule)&,nonVanishing[[{1}]]]

firstContributionWeight=uLsq[{k[1],k[4]}]

firstContributionDenominator= (uLsq[{k[2],l[3]}] uLsq[{k[1],l[1]}] uLsq[{k[4],-l[1]}]  uLsq[{k[3],-l[3]}])

(firstContribution =firstContributionWeight/firstContributionDenominator)//Style[#,Large]&

second graph contribution (first s contribution)

MapIndexed[(doOrderedPlot[#,k/@Range[4],PlotLabel->#2[[1]]]//cutDisplayRule)&,nonVanishing[[{2}]]]//Show

secondContributionWeight=uLsq[{k[1],k[2]}]

secondContributionDenominator= (uLsq[{l[1],l[2]}] uLsq[{k[2],l[3]}] uLsq[{l[2],l[3]}]  uLsq[{k[4],-l[1]}])

(secondContribution=secondContributionWeight/secondContributionDenominator)//Style[#,Large]&

third graph contribution (second s contribution)

MapIndexed[(doOrderedPlot[#,k/@Range[4],PlotLabel->#2[[1]]]//cutDisplayRule)&,nonVanishing[[{3}]]]//Show

thirdContributionWeight=uLsq[{k[1],k[2]}]

thirdContributionDenominator= (uLsq[{l[1],k[1]}] uLsq[{l[1],l[2]}] uLsq[{l[2],l[3]}]  uLsq[{k[3],-l[3]}])

(thirdContribution=thirdContributionWeight/thirdContributionDenominator)//Style[#,Large]&

automatic isomorphic dressing compared w/ what we did by hand

Recall we elided the s t Atree[1234] as it’s invariant under all permutations:

allTwoLoop = {DblBoxPlanarGraph, NonPlanar2Loop};

autoContribution= Total[ getCOGraphContribution[#,allTwoLoop,zviDressing]&/@nonVanishing]

You should be able to squint and see that these are identical under conservation of momenta to the above. We explicitly compare by converting the Lorentz squares (uLsq[a__]:=(a[[1]]+a[[2]]+a[[3]])^2) to momentum invariants (Lorentz products) (ulp[a_,b_]:=a·b), using conservation of momentum on the cuts.

0==autoContribution-firstContribution-secondContribution-thirdContribution/.uLsq[a__]:>ulp[Plus@@a,Plus@@a]/.getIndepRules[firstCut]/.ulp[a_,a_]:>0

Together[%]

Note getIndepRules[cut__] gives set of momentum invariant rules to some randomly chosen bases satisfying conservation of momenta identities:

getIndepRules[firstCut]//Sort[#,OrderedQ[ByteCount[TeXForm[#]]&/@{#1,#2}]&]&//TableForm

non-planar cut

secondCut={Atree[{k[1],l[1],l[3],k[2],l[2]}],
Atree[{k[3],-l[3],-l[1],k[4],-l[2]}]}

Thread[Rule[k/@Range[5], {k[1],l[1],l[3],k[2],l[2]}]]

Again we generate trees by taking our canonical five point plots and relabeling.
We start with:

(doOrderedPlot[#,k/@Range[5]]//cutDisplayRule)&/@allFive//Partition[#,3,3,{1,1},{}]&//GraphicsGrid//Show

For the left guys we map to this:

(doOrderedPlot[#,First[secondCut][[1]]]//cutDisplayRule)&/@(leftNpGraphs=treesToLoops[{First[secondCut]}])//Partition[#,3,3,{1,1},{}]&//GraphicsGrid//Show

For the right guys we map to this :

(doOrderedPlot[#,Last[secondCut][[1]]]//cutDisplayRule)&/@(rightNpGraphs=treesToLoops[{Last[secondCut]}])//Partition[#,3,3,{1,1},{}]&//GraphicsGrid//Show

allSecondCutGraphs=treesToLoops[secondCut];

Table[{Text[{i,j}],leftNpGraphs[[i]]//doOrderedPlot[#,First[secondCut][[1]]]&//cutDisplayRule,
rightNpGraphs[[j]]//doOrderedPlot[#,Last[secondCut][[1]]]&//cutDisplayRule,
(manualConst[(i-1)*5+j]=sewGraphs[leftNpGraphs[[i]],rightNpGraphs[[j]]])//doOrderedPlot[#,k/@Range[4]]&//cutDisplayRule,Text[(i-1)*5+j]},{i,1,5},{j,1,5}]//Flatten[#,1]&//GraphicsGrid//Show

let’s dress the cut

NonPlanar2Loop//doOrderedPlot[#,k/@Range[4]]&

This graph topology first shows up as graph 5 in the cut (look at the last column, 5 down in the above table)

allSecondCutGraphs[[5]] // doOrderedPlot[#, k /@ Range[4]] &

isIsomorphic[allSecondCutGraphs[[5]],NonPlanar2Loop]

To find out the graph’s contribution we need to find the isomorphism mapping kinematic weight from the canonical dressed graph to the cut graph:

(someIsomorphism=isomorphicEdgeRule[NonPlanar2Loop, allSecondCutGraphs[[5]]])//TableForm[Select[#,Head[#[[1]]]=!=Times&]]&

zviDressing[NonPlanar2Loop]/.someIsomorphism

But recall we need to track structure of permutation order around vertices:

Here is the canonical form mapped to the cut graph:

canonVertexList[NonPlanar2Loop/.someIsomorphism]//Sort//MatrixForm

Here is the cut graph:

canonVertexList[allSecondCutGraphs[[5]]]//Sort//MatrixForm

Note an ODD number of acyclic distinctions => the color trace would pick up a minus sign.

colorForm[NonPlanar2Loop/.someIsomorphism]

colorForm[NonPlanar2Loop /. someIsomorphism]=== -1 * colorForm[allSecondCutGraphs[[5]]]

Bose invariance means that the kinematic contribution to this  ordered cut needs to pick up a minus sign relative to the canonical contribution giving us the kinematic weight:

(zviDressing[NonPlanar2Loop] /. someIsomorphism) * Simplify[colorForm[allSecondCutGraphs[[5]]]/colorForm[NonPlanar2Loop /. someIsomorphism]]

Note of course it’s inefficient to calculate the color-trace just to count the number of acyclic permutations.  Here is a quicker way to get the relevant ordered sign

((allSecondCutGraphs[[5]])/((NonPlanar2Loop /. someIsomorphism)))/. neckl:>Signature/. vertexFormGraph[a__]:>Times@@a

NoDressing[___]:=0

Here are all the contributions to this cut:

(ourCalculation=getCOGraphContribution[#,allTwoLoop,zviDressing]&/@allSecondCutGraphs)//Total

You can convince yourself that these line up  (recall we have modded out a universal factor of  phase and s t Atree)

Just for our own peace of mind, lets compare in 4 dimensions with an actual state sum.  Let’s take the external states to be gluons with the helicities: (-1,-1,1,1)

Note setting SUSY to 4.  If we were doing 𝒩=1, we would set SUSY=1, etc.

SUSY=4; LOOPS=2; LEGS=4;extRule={k[1]->-1,k[2]->-1,k[3]->1,k[4]->1}

pluginNumbers[expr_, mom_] := (expr /. Atree[a__] :> Atree[a, a /. extRule] //. mhvTreeRules//FermionArrowSign) /. complexSpinorProd[mom] /. evaluateLpRule[mom] /. evaluateLsqRule[mom]

Here we actually calculate the state sum in 4D:

(susyStateSum=getTheProcessedCut[secondCut])//Short[#,4]&

Notice the flattened momenta? What is this?

It’s a way of taking massive momenta to something massless using a null reference momenta ζ:

(P^♭)≡P-ζ (P^2)/(2 ζ·P)

(take 1/3 of a second to convince yourself that this is always null)

We need to generate some 4 momenta, and an arbitrary null reference ζ vector to flatten massive momenta in spinor products:

PREC=45;
{myCutMom}=buildAllMomenta[secondCut,1];refreshHLP;
ζ=getRandomNullVector[PREC,4];
flattenMomenta[myCutMom,ζ]

Let’s see that the momenta satisfies the cut conditions:

secondCut[[1]] /. Atree[a__]:>Total[myCutMom/@a]//TraditionalForm

secondCut[[2]] /. Atree[a__]:>Total[myCutMom/@a]//TraditionalForm

Conservation of momenta works. Let’s check on-shell:

ulp[#,#]&/@(secondCut/.Atree:>List//Flatten)

%/.evaluateLpRule[myCutMom]//TraditionalForm

Ok, well let’s evaluate and check.

Total[ourCalculation ] * (-I*uLsq[{k[1],k[2]}]uLsq[{k[2],k[3]}] Atree[k/@Range[4]]) //pluginNumbers[#,myCutMom]&

compare with the explicit SUSY state sum (the sum over products of trees on all N=4 SUSY states):

susyStateSum//pluginNumbers[#,myCutMom]&

%-%%//TraditionalForm

Not enough precision to convince?

PREC=825;
{myCutMom}=buildAllMomenta[secondCut,1];refreshHLP;
ζ=getRandomNullVector[PREC,4];
flattenMomenta[myCutMom,ζ]

Total[ourCalculation ] * (-I*uLsq[{k[1], k[2]}] uLsq[{k[2], k[3]}] Atree[k /@ Range[4]])-susyStateSum // pluginNumbers[#, myCutMom] &//TraditionalForm

I don’t have time in these lectures to cover the physics behind the SUSY state sum, but there are literally multitudes of papers written on this.  The one behind the above calculation is:

https://arxiv.org/abs/0903.5348

Lecture 3: Our Goal 2-loop Cut construction....

So we discovered how to verify on cuts using integrands organized into graphs. Now we’re going to see how easy it is to construct an answer.  The key will be two things:

1) Our dressings must obey the symmetries of the graphs.

2) Our dressings must satisfy the cuts.  Since cuts span  we need only consider a few cuts to completely fix the amplitude:  3-particle cuts, and 2-particle cuts.

We will use the previous dressing (Zvi’s dressing) as encoding the d-dimensional  truth of the cuts and see if we can find any other representation that satisfies all the same cuts as:

(Subscript[n, ZVI])[#//doOrderedPlot[#,k/@Range[4]]&]==zviDressing[#]&/@{DblBoxPlanarGraph,NonPlanar2Loop}//TableForm//TraditionalForm//Style[#,{Large,Bold}]&

DblBoxPlanarGraph//doOrderedPlot[#,k/@Range[4]]&

fancyFormatOn

We’re going to come up with an ansatz in terms of all the independent inner products.  There are only three independent external momenta and two independent internal momenta at 4-pts 2-loops. This gives the following independent momentum invariants for off-shell internal legs:

planarDots=Map[#[[2]]&,getOffShellDots[DblBoxPlanarGraph]]/.ulp[a_,b_]:>Sow[ulp[a,b]]//Reap//Last//Flatten//Union

zviDressing[DblBoxPlanarGraph]

Power counting constraints means any local dressing can only have one power of any of these inner products:

newDressing[DblBoxPlanarGraph]=Total[MapIndexed[a[1,#2[[1]]]*#1&,planarDots]]

NonPlanar2Loop//doOrderedPlot[#,k/@Range[4]]&

fancyFormatOn

Here are the unique invariants for this graph.

nonplanarDots=Map[#[[2]]&,getOffShellDots[NonPlanar2Loop]]/.ulp[a_,b_]:>Sow[ulp[a,b]]//Reap//Last//Flatten//Union

Power counting constraints means any local dressing can only have one power of any of these inner products:

newDressing[NonPlanar2Loop]=Total[MapIndexed[a[2,#2[[1]]]*#1&,nonplanarDots]]

Let’s impose symmetry

Planar Double Box symmetry

Get all the different ways the numerator could be applied through graph isomorphism:

symDressingsP=getAllNumerators[DblBoxPlanarGraph,allTwoLoop,newDressing];

constraints=Table[ symDressingsP[[1]]-symDressingsP[[i]]/.getOffShellDots[DblBoxPlanarGraph],{i,2,Length[symDressingsP]}];
StylePrint[#==0]&/@constraints

(indepDots=constraints/.ulp[a_,b_]:>Sow[ulp[a,b]]//Reap//Last//Flatten//Union)//Partition[#,4,4,{1,1},{}]&//TableForm

As the momenta invariants are independent after being placed in a basis, it is their coefficients which must vanish.  In other words:

List@@And@@(allConstraints=Map[#==0&,Table[Map[#[[2]]&,CoefficientArrays[constraints[[i]],indepDots]//Last//ArrayRules],{i,1,Length[constraints]}]//Flatten]//Union)//Union//TableForm

Which is solved by:

(symRulesPlanar=allConstraints//Reduce//ToRules)//TableForm

So this is what things look like:

newDressing[DblBoxPlanarGraph]/.symRulesPlanar//Collect[#,a[___],FullSimplify]&

Non-planar symmetry

Get all the different ways the numerator could be applied through graph isomorphism:

symDressingsnP=getAllNumerators[NonPlanar2Loop,allTwoLoop,newDressing];

constraintsNP=Select[List@@And@@(Table[ symDressingsnP[[1]]-symDressingsnP/.getOffShellDots[NonPlanar2Loop],{i,2,Length[symDressingsnP]}]//Flatten//Union),#=!=0&];

Map[(0==#//StylePrint)&,constraintsNP];

(indepDots=constraintsNP/.ulp[a_,b_]:>Sow[ulp[a,b]]//Reap//Last//Flatten//Union)//Partition[#,4,4,{1,1},{}]&//TableForm

This yields the following equations:

(symNonPlanarConst=List@@And@@(Map[#==0&,Table[Map[#[[2]]&,CoefficientArrays[constraintsNP[[i]],indepDots]//Last//ArrayRules],{i,1,Length[constraintsNP]}]//Flatten]//Flatten//Union))//Sort[#,OrderedQ[ByteCount[TeXForm[#]]&/@{#1,#2}]&]&//TableForm

Which has the following solution:

(symRulesNonPlanar=symNonPlanarConst//Reduce//ToRules)//TableForm

newDressing[NonPlanar2Loop]/.symRulesNonPlanar

So the following values ensure our dressing satisfies automorphic symmetry:

(symRules={symRulesPlanar,symRulesNonPlanar}//Flatten)//Partition[#,2,2,{1,1},{}]&//TableForm

Let’s impose cuts

fix to the non-planar cut from earlier:

secondCut

We store in truth the difference between our new dressing and Zvi’s old dressing on the cuts:

truth=(getCOGraphContribution[#,allTwoLoop,zviDressing]&/@treesToLoops[secondCut]//Total)==
((getCOGraphContribution[#,allTwoLoop,newDressing]&/@treesToLoops[secondCut]//Total)//.symRules);

getIndepRules[secondCut]//TableForm

expr=truth/.uLsq[a__]:>ulp[Plus@@a,Plus@@a]/.getIndepRules[secondCut]/.ulp[a_,a_]:>0;

goodDots=expr/.uLsq[a__]:>ulp[Plus@@a,Plus@@a]/.ulp[a_,b_]:>Sow[ulp[a,b]]//Reap//Last//Flatten//Union

expr/.a[i_,j_]:>Sow[a[i,j]]//Reap//Last//Flatten//Union//Length

nDots=Map[#->RandomPrime[{10^3,10^9}]&,goodDots]

expr/.nDots//Expand//Collect[#,a[___]]&

myRules2=Table[nDots=Map[#->RandomPrime[{10^3,10^9}]&,goodDots];
expr/.nDots,{24}]//Reduce//ToRules

newDressing[DblBoxPlanarGraph]//.symRules/.myRules2//Collect[#,a[___],FullSimplify]&

newDressing[NonPlanar2Loop]//.symRules/.myRules2//Collect[#,a[___],FullSimplify]&

fix to iterated 2 particle cuts

fixMyCut2pc[myCut_]:=Module[{truth,expr,indepDots,goodDots,nDots},truth=(getCOGraphContribution[#,allTwoLoop,zviDressing]&/@treesToLoops[myCut]//Total)==
((getCOGraphContribution[#,allTwoLoop,newDressing]&/@treesToLoops[myCut]//Total)//.symRules//.myRules2);
expr=truth/.uLsq[a__]:>ulp[Plus@@a,Plus@@a]/.getIndepRules[myCut]/.ulp[a_,a_]:>0;
goodDots=expr/.uLsq[a__]:>ulp[Plus@@a,Plus@@a]/.ulp[a_,b_]:>Sow[ulp[a,b]]//Reap//Last//Flatten//Union;
nDots=Map[#->RandomPrime[{10^3,10^9}]&,goodDots];
If[(expr/.nDots//ExpandAll)=!=True,
numAs=Length[expr[[2]]/.nDots//ExpandAll];
myRules1=Table[nDots=Map[#->RandomPrime[{10^3,10^9}]&,goodDots];
expr/.nDots,{Ceiling[numAs*1.05]}]//Reduce//ToRules,
{}]]

twoPartRules=Table[{ Atree[{k[1],k[2],permA}//Flatten],
 Atree[{-l[1],-l[2],permB}//Flatten],
 Atree[{-l[3],-l[4],permC}//Flatten]}//fixMyCut2pc,
{permA,Permutations[{l[1],l[2]}]},
{permB,Permutations[{l[3],l[4]}]},
{permC,Permutations[{k[3],k[4]}]}]//Flatten//Union

(myRules={myRules2,symRules,twoPartRules}/.Rule:>Equal//Flatten//Union//Reduce//ToRules)//Partition[#,3,3,{1,1},{}]&//TableForm

newDressing[DblBoxPlanarGraph]/.myRules

newDressing[NonPlanar2Loop]/.myRules

This in an arbitrary off-shell basis.  Let’s clean it up a bit.

%/.getOffShellDotsBetter[NonPlanar2Loop]//Simplify

Turns our remaining freedom is pure gauge freedom that is fixed by no cuts, and affects no physical amplitude -- go on and integrate it!

Turns out there is a principle that can constrain it---and it intimately relates to the point I made early first lecture, and ensures that this information can be used not just for maximally supersymmetric theory at 2-loops by dressing with color, but all sorts of theories.

Lecture 4: Color-Kinematics

Recall that our single-copy YM kinematic weights had to obey the sign of the adjoint color weight on cuts:

colorForm[allSecondCutGraphs[[5]]]

colorForm[ NonPlanar2Loop /. someIsomorphism]

%+%%

Besides obeying antisymmetry on each vertex, adjoint color weights must obey Jacobi relations, defined as follows:

opJacobi[graph_,leg_,op_]:= (
 (graph//op)==(tHat[graph,leg]//op) + (uHat[graph,leg]//op)
)

Here’s a graphical depiction:

doOrderedPlot[DblBoxPlanarGraph,k/@Range[4]]

opJacobi[DblBoxPlanarGraph,in[8],((#/.in[8]:>highlight[in[8]]//doOrderedPlot[#,k/@Range[4]]&))&]/.in[a_]:>If[a===8,in[8],""]//Style[#,{Bold,Large}]&//TraditionalForm

Here’s the operation on color weights:

opJacobi[DblBoxPlanarGraph,in[8],(Module[{expr},expr=#//colorForm;StylePrint[expr];expr])&]

It turns out at tree level, and conjecturally at loop level, for all double-copy theories we can find a representation where both copies obey the same algebra.  This may be surprising to you, given we saw that the triangle diagram on the right didn’t contribute.

Here’s the operation on the newDressing:

jacEqn=opJacobi[DblBoxPlanarGraph, in[8], (getNumerator[#,allTwoLoop,newDressing]/.myRules/.getKRules2[consistentGraphToTrees[DblBoxPlanarGraph],{in[6],in[11]}]/.ulp[k[a_],k[a_]]:>0)&]/.getIndepRules[{Atree[k/@Range[4]]}]//ExpandAll

uniqEqns=Map[#[[2]]==0&,Last[CoefficientArrays[jacEqn,jacEqn/.ulp[a_,b_]:>Sow[ulp[a,b]]//Reap//Last//Flatten//Union]]//ArrayRules]

jacSoln=Reduce[uniqEqns]//ToRules

This completely constrains the solution -- so let’s verify that all Jacobi’s are satisfied, due to symmetry, we only need to check a few addl legs:

jacEqn9 = opJacobi[DblBoxPlanarGraph, in[9], (getNumerator[#, allTwoLoop, newDressing] /. myRules /.jacSoln/. getKRules2[consistentGraphToTrees[DblBoxPlanarGraph], {in[6], in[11]}] /. ulp[k[a_], k[a_]] :> 0) &] /. getIndepRules[{Atree[k /@ Range[4]]}] // ExpandAll

jacEqn10 = opJacobi[DblBoxPlanarGraph, in[10], (getNumerator[#, allTwoLoop, newDressing] /. myRules /.jacSoln/. getKRules2[consistentGraphToTrees[DblBoxPlanarGraph], {in[6], in[11]}] /. ulp[k[a_], k[a_]] :> 0) &] /. getIndepRules[{Atree[k /@ Range[4]]}] // ExpandAll

Checking we now satisfy gravity cuts with the double copy against KLT:

gravDressing[DblBoxPlanarGraph]=newDressing[DblBoxPlanarGraph]^2/. myRules /.jacSoln/. getIndepRules[{Atree[k /@ Range[4]]}] //FullSimplify

gravDressing[NonPlanar2Loop] = newDressing[NonPlanar2Loop]^2 /. myRules /.jacSoln/. getIndepRules[{Atree[k /@ Range[4]]}] //FullSimplify

Gravity cuts, like full color-dressed YM cuts, are orderless.  Let’s look at the 3-particle cut which is entirely spanning

threePartCut=firstCut  (* it doesn't matter since grav cuts are unordered *)

(ourCalc=Total[getGravGraphContribution[#,allTwoLoop,gravDressing]&/@treesToLoopsUO[threePartCut]])//Short[#,4]&

(Note use of treesToLoopsUO which generates all cubic graphs, not caring about an ordering)

We can compare by using KLT on the cut given the YM graph dressings in zviDressing:

someKLTCut=evaluateKltCut[threePartCut,zviDressing,allTwoLoop];

(fromKLT=someKLTCut["evaluate"][{}])//Short[#,7]&

This looks non-local, but if you shake it, you’ll find the same as the above:

fromKLT-ourCalc /.uLsq[a__]:>ulp[Plus@@a,Plus@@a]/.getIndepRules[threePartCut]/.ulp[a_,a_]:>0//Together

Of course we could’ve just compared numerically which would’ve been faster and indeed is much more tractable at higher loops:

indepDots= Map[#[[2]]&,getIndepRules[threePartCut]]/.ulp[a_,b_]:>Sow[ulp[a,b]]//Reap//Last//Flatten//Union

nIndepDots=Map[#->RandomPrime[{10^3,10^9}]&,indepDots]

someKLTCut["evaluate"][nIndepDots]

ourCalc /. uLsq[a__] :> ulp[Plus @@ a, Plus @@ a] /. getIndepRules[threePartCut] /. ulp[a_, a_] :> 0/.nIndepDots

%-%%==0