Gradient Flow
Gauge loops, the flow equation, and the code that integrates it.
This tutorial covers the gradient flow code available in Qlua version 20170804 and later. The code is based on the original routines by Evan Weinberg. The paper by Martin Lüscher, arXiv:1006.4518, may be instructive too.
Definitions
Let us begin with definitions of gauge loops
\[\begin{aligned}
P^{(1)}_{\mu,\nu}(x) & = U_\mu(x) U_\nu(x+\hat\mu) U^\dagger_\mu(x+\hat\nu) U^\dagger_\nu(x)\\
R^{(1)}_{\mu,\nu}(x) & = U_\mu(x) U_\mu(x+\hat\mu)
U_\nu(x+2\hat\mu)
U^\dagger_\mu(x+\hat\mu+\hat\nu) U^\dagger_\mu(x+\hat\nu)
U^\dagger_\nu(x)\\
C^{(1)}_{\mu,\nu,\lambda}(x) & = U_\mu(x) U_\nu(x+\hat\mu)
U_\lambda(x+\hat\mu+\hat\nu)
U^\dagger_\nu(x+\hat\mu+\hat\lambda) U^\dagger_\mu(x+\hat\lambda)
U^\dagger_\lambda(x)\\
T^{(1)}_{\mu,\nu,\lambda}(x) & = U_\mu(x) U_\nu(x+\hat\mu)
U_\lambda(x+\hat\mu+\hat\nu)
U^\dagger_\mu(x+\hat\nu+\hat\lambda) U^\dagger_\nu(x+\hat\lambda)
U^\dagger_\lambda(x)
\end{aligned}\]
also, double sized loops are
\[\begin{aligned}
P^{(2)}_{\mu,\nu}(x) & = U_\mu(x) U_\mu(x+\hat\mu)
U_\nu(x+2\hat\mu) U_\nu(x+2\hat\mu+\hat\nu)
U^\dagger_\mu(x+\hat\mu+2\hat\nu)\\
&\qquad U^\dagger_\mu(x+2\hat\nu)
U^\dagger_\nu(x+\hat\nu) U^\dagger_\nu(x) \\
R^{(2)}_{\mu,\nu}(x) & = U_\mu(x) U_\mu(x+\hat\mu) U_\mu(x+2\hat\mu) U_\mu(x+3\hat\mu)
U_\nu(x+4\hat\mu) U_\nu(x+4\hat\mu+\hat\nu) \\
&\qquad
U^\dagger_\mu(x+3\hat\mu+2\hat\nu) U^\dagger_\mu(x+2\hat\mu+2\hat\nu)
U^\dagger_\mu(x+\hat\mu+2\hat\nu)\\
&\qquad U^\dagger_\mu(x+2\hat\nu)
U^\dagger_\nu(x+\hat\nu) U^\dagger_\nu(x)\\
C^{(2)}_{\mu,\nu,\lambda}(x) & = U_\mu(x)
U_\mu(x+\hat\mu)
U_\nu(x+2\hat\mu)
U_\nu(x+2\hat\mu+\hat\nu)
U_\lambda(x+2\hat\mu+2\hat\nu)\\
&\qquad U_\lambda(x+2\hat\mu+2\hat\nu+\hat\lambda)
U^\dagger_\nu(x+2\hat\mu+\hat\nu+2\hat\lambda)
U^\dagger_\nu(x+2\hat\mu+2\hat\lambda)\\
&\qquad U^\dagger_\mu(x+\hat\mu+2\hat\lambda)
U^\dagger_\mu(x+2\hat\lambda)
U^\dagger_\lambda(x+\hat\lambda)
U^\dagger_\lambda(x)\\
T^{(2)}_{\mu,\nu,\lambda}(x) & = U_\mu(x)
U_\mu(x+\hat\mu)
U_\nu(x+2\hat\mu)
U_\nu(x+2\hat\mu+\hat\nu)
U_\lambda(x+2\hat\mu+2\hat\nu)\\
&\qquad U_\lambda(x+2\hat\mu+2\hat\nu+\hat\lambda)
U^\dagger_\mu(x+\hat\mu+2\hat\nu+2\hat\lambda)
U^\dagger_\mu(x+2\hat\nu+2\hat\lambda)\\
&\qquad U^\dagger_\nu(x+\hat\nu+2\hat\lambda)
U^\dagger_\nu(x+2\hat\lambda)
U^\dagger_\lambda(x+\hat\lambda)
U^\dagger_\lambda(x)
\end{aligned}\]
These are of course, the plaquette, the rectangle, the chair and the twisted chair of sizes one and two.
The gauge action pieces are
\[\begin{aligned}
S^{(n)}_P & = \sum_{x,\mu,\nu > \mu}
\biggl(1-\frac{1}{N_c}{\rm Re}\,{\rm Tr}\Bigl(P^{(n)}_{\mu,\nu}(x)\Bigr)\biggr) \\
S^{(n)}_R & = \sum_{x,\mu,\nu}
\biggl(1-\frac{1}{N_c}{\rm Re}\,{\rm Tr}\Bigl(R^{(n)}_{\mu,\nu}(x)\Bigr)\biggr) \\
S^{(n)}_C & = \sum_{x,\mu,\nu > \mu ,\lambda \ne \mu, \lambda\ne\nu}
\biggl(1-\frac{1}{N_c}{\rm Re}\,{\rm Tr}\Bigl(
C^{(n)}_{\mu,\nu,\lambda}(x) +
C^{(n)}_{\nu,\mu,\lambda}(x) +
C^{(n)}_{-\nu\,\mu,\lambda}(x) +
C^{(n)}_{\nu,-\mu,\lambda}(x)
\Bigr)\biggr) \\
S^{(n)}_T & = \sum_{x,\mu,\nu >\mu ,\lambda > \nu}
\biggl(1-\frac{1}{N_c}{\rm Re}\,{\rm Tr}\Bigl(
T^{(n)}_{\mu,\nu,\lambda}(x) +
T^{(n)}_{-\mu,-\nu,\lambda}(x) +
T^{(n)}_{\mu\,-\nu,\lambda}(x) +
T^{(n)}_{-\nu,\mu,\lambda}(x)
\Bigr)\biggr) \\
S^{(n)}_{{\rm adj} P} & = \sum_{x,\mu,\nu > \mu}
\biggl(1-\frac{1}{N^2_c}
{\rm Tr}\Bigl(P^{(n)}_{\mu,\nu}(x)\Bigr)
{\rm Tr}\Bigl(P^{(n)}_{\mu,\nu}(x)\Bigr)^*
\biggr)
\end{aligned}\]
Available gauge loops
For each of the gauge loops above, one can use the corresponding action and the gauge force which is defined as
\[F_\mu[S](x) = \frac{\delta S}{\delta U_\mu(x)}.\]
Note that the force definitions we use differ by normalization from what is usually used in computing the gradient flow.
The table below gives the names of the gauge loop objects available. It might look a bit cryptic, but hopefully things will become clear in the next section.
| name | module | action |
|---|
adj-plaq1 | qcdlib/gauge-loops/adj-plaq1 | \(S^{(1)}_{{\rm adj} P}\) | plaq1 | qcdlib/gauge-loops/plaq1 | \(S^{(1)}_P\) | plaq2 | qcdlib/gauge-loops/plaq2 | \(S^{(2)}_P\) | rect1 | qcdlib/gauge-loops/rect1 | \(S^{(1)}_R\) | rect2 | qcdlib/gauge-loops/rect2 | \(S^{(2)}_R\) | chair1 | qcdlib/gauge-loops/chair1 | \(S^{(1)}_C\) | chair2 | qcdlib/gauge-loops/chair2 | \(S^{(2)}_C\) | twist1 | qcdlib/gauge-loops/twist1 | \(S^{(1)}_T\) | twist2 | qcdlib/gauge-loops/twist2 | \(S^{(2)}_T\) |
Gradient flow routine
Let us start with an example. Suppose we want to compute gradient flow using the flow action
\[S = 0.534 S^{(1)}_P + 0.123 S^{(1)}_T - 0.951 S^{(2)}_R\]
First, we need to request the modules. stdlib has to come first: the qcdlib modules extend a table it declares, and without it they fail with variable 'qcdLib' is not declared.
require "stdlib";
require "qcdlib/gradient-flow";
require "qcdlib/gauge-loops/plaq1";
require "qcdlib/gauge-loops/twist1";
require "qcdlib/gauge-loops/rect2";
Next, we construct the flow force operator
force = qcdLib.GaugeLoops.flow_force { plaq1 = 0.534,
twist1 = 0.123,
rect2 = -0.951 };
Now, let us apply the flow equation to a gauge field U for time 0.0076:
V = qcdLib.GradientFlow.flow(force, U, 0.0076);
The result of the flow is now bound to name V.
Advanced gradient flow features
The flow routine, qcdLib.GradientFlow.flow can be used in different ways. The above example demonstrated a simple single step flow. There are several convenience features available:
- One can pass a table of time steps to run them in sequence
V = qcdLib.GradientFlow.flow(force,
U,
{0.001, 0.002, 0.003, 0.005 });
- Alternatively, one can provide a function to compute the next integration step:
function step_size(i)
return (0.001 + 0.07 * i * i) / (1 + i * i);
end
V = qcdLib.GradientFlow.flow(force, U,
step_size,
{low = 0, high = 100 });
This will run the flow for 101 steps, with each step size computed by step_size().
In addition, one can provide a monitor function, which will be called after each step of the integrator:
function track_gflow(iter, step, time, V)
printf("flow iteration %5d, step %7.5f, flow time %8.6f\n",
iter, step, time);
end
V = qcdLib.GradientFlow.flow(force, U,
step_size,
{low = 0,
high = 100,
monitor = track_gflow });
The monitor feature is especially useful if one runs gradient flow to set the scale, as we will see in the next section.
E and Q measurements routine
When setting the lattice scale, one needs to compute an expectation value of some gauge invariant quantity as a function of flow time. One such observable is \(\langle E\rangle\) as defined by Lüscher, or its perturbative improvement. Another useful quantity is the topological charge \(Q\), which costs very little extra once \(\langle E\rangle\) has been constructed. The module qcdlib/symmetric-EQ contains code that calculates the clover definition of \(\langle E\rangle\) and \(Q\), in addition, it can be used to compute the third-order perturbatively improved \(\langle E\rangle\) and \(Q\) as well.
First, one requests the module:
require "qcdlib/symmetric-EQ";
Then, calling the following routine
EQ_simple = qcdLib.symmetricEQ(V);
will return a table with fields symmetricE and symmetricQ.
If one needs the improved expectation values, passing the following option
EQ_improved = qcdLib.symmetricEQ(V, {improved = true});
will produce in addition to symmetricE and symmetricQ, improvedE and improvedQ as well.
Tests of the gradient flow code
In the Qlua source tree the tests are at tests/gradient-flow. There are tests of correctness against Evan's code, and an extensive collection of scripts to exercise the gradient flow modules.
|