Arithmetic

+, -, *, / and unary - are overloaded across the whole type system. Unary - works on every type that has any arithmetic at all. The tables below are the exact sets the interpreter accepts, measured by trying every ordered pair of types against every operator; a combination not listed raises.

One rule covers most of it: a value may be combined with another of the same type, or with a narrower one, and the result is the wider of the two. The widening order is number ⊂ complex for scalars and L:Int ⊂ L:Real ⊂ L:Complex for the scalar fields, with a plain number or complex usable anywhere a scalar field is. Division is where the rule stops: the divisor can never be wider than the dividend.

Addition and subtraction

+ and - accept exactly the same pairs, and both are symmetric in their types, so each row below stands for both orders. Everything else raises — in particular there is no mixing of color types, of two different numbers of colors, or of fields on different lattices.

One sideThe otherResult
numbernumbernumber
numbercomplexcomplex
numbergammagamma
numberL:IntL:Int
numberL:RealL:Real
numberL:ComplexL:Complex
complexcomplexcomplex
complexgammagamma
complexL:RealL:Complex
complexL:ComplexL:Complex
gammagammagamma
matrix.realmatrix.realmatrix.real
matrix.complexmatrix.complexmatrix.complex
ColorVectorColorVectorColorVector
ColorMatrixColorMatrixColorMatrix
DiracFermionDiracFermionDiracFermion
DiracPropagatorDiracPropagatorDiracPropagator
L:IntL:IntL:Int
L:RealL:RealL:Real
L:RealL:ComplexL:Complex
L:ComplexL:ComplexL:Complex
L:ColorVectorL:ColorVectorL:ColorVector
L:ColorMatrixL:ColorMatrixL:ColorMatrix
L:DiracFermionL:DiracFermionL:DiracFermion
L:DiracPropagatorL:DiracPropagatorL:DiracPropagator

Multiplication

Multiplication of two scalars — number, complex, gamma, L:Int, L:Real, L:Complex — accepts exactly the pairs the addition table lists and gives the same result type. Beyond that it does two further things.

Scaling. A scalar multiplies an object of any type, from either side, and the object's type comes back unchanged.

ObjectMay be scaled byResult
gammanumber, complexgamma
matrix.realnumbermatrix.real
matrix.complexnumber, complexmatrix.complex
ColorVectornumber, complexColorVector
ColorMatrixnumber, complexColorMatrix
DiracFermionnumber, complexDiracFermion
DiracPropagatornumber, complexDiracPropagator
L:IntnumberL:Int
L:ColorVectornumber, complex, L:Real, L:ComplexL:ColorVector
L:ColorMatrixnumber, complex, L:Real, L:ComplexL:ColorMatrix
L:DiracFermionnumber, complex, L:Real, L:ComplexL:DiracFermion
L:DiracPropagatornumber, complex, L:Real, L:ComplexL:DiracPropagator

Products. The rest are genuine compositions, and these are not symmetric: the left operand acts on the right one. M * V is a matrix applied to a vector; V * M raises.

LeftRightResult
gammagammagamma
gammaDiracFermionDiracFermion
gammaDiracPropagatorDiracPropagator
gammaL:DiracFermionL:DiracFermion
gammaL:DiracPropagatorL:DiracPropagator
matrix.realmatrix.realmatrix.real
matrix.complexmatrix.complexmatrix.complex
ColorMatrixColorVectorColorVector
ColorMatrixColorMatrixColorMatrix
ColorMatrixDiracFermionDiracFermion
ColorMatrixDiracPropagatorDiracPropagator
DiracPropagatorgammaDiracPropagator
DiracPropagatorColorMatrixDiracPropagator
DiracPropagatorDiracPropagatorDiracPropagator
L:IntL:IntL:Int
L:ColorMatrixL:ColorVectorL:ColorVector
L:ColorMatrixL:ColorMatrixL:ColorMatrix
L:ColorMatrixL:DiracFermionL:DiracFermion
L:ColorMatrixL:DiracPropagatorL:DiracPropagator
L:DiracPropagatorgammaL:DiracPropagator
L:DiracPropagatorL:ColorMatrixL:DiracPropagator
L:DiracPropagatorL:DiracPropagatorL:DiracPropagator

A gamma acts on a Dirac object from the left, and on a propagator from either side: g * P and P * g are both defined and are different matrices. g * D is defined but D * g is not — a spinor has no index for a gamma to contract on the right.

Division

Division is where the widening rule is least uniform, so the table is worth reading rather than guessed at. It is not symmetric.

DividendDivisorResult
numbernumbernumber
numbercomplexcomplex
numberL:IntL:Int
numberL:RealL:Real
numberL:ComplexL:Complex
complexnumbercomplex
complexcomplexcomplex
complexL:RealL:Complex
complexL:ComplexL:Complex
gammanumbergamma
gammacomplexgamma
matrix.realnumbermatrix.real
matrix.complexnumbermatrix.complex
matrix.complexcomplexmatrix.complex
ColorVectornumberColorVector
ColorVectorcomplexColorVector
ColorMatrixnumberColorMatrix
ColorMatrixcomplexColorMatrix
DiracFermionnumberDiracFermion
DiracFermioncomplexDiracFermion
DiracPropagatornumberDiracPropagator
DiracPropagatorcomplexDiracPropagator
L:IntnumberL:Int
L:IntL:IntL:Int
L:RealnumberL:Real
L:RealcomplexL:Complex
L:RealL:RealL:Real
L:RealL:ComplexL:Complex
L:ComplexnumberL:Complex
L:ComplexcomplexL:Complex
L:ComplexL:RealL:Complex
L:ComplexL:ComplexL:Complex
L:ColorVectornumberL:ColorVector
L:ColorVectorcomplexL:ColorVector
L:ColorMatrixnumberL:ColorMatrix
L:ColorMatrixcomplexL:ColorMatrix
L:DiracFermionnumberL:DiracFermion
L:DiracFermioncomplexL:DiracFermion
L:DiracPropagatornumberL:DiracPropagator
L:DiracPropagatorcomplexL:DiracPropagator

Three things to take from it.

A color object — ColorVector, ColorMatrix, DiracFermion, DiracPropagator, lattice or not — divides only by a number or a complex. L:Real * L:ColorVector is defined and gives an L:ColorVector, but L:ColorVector / L:Real raises. There is no division of one color object by another in any form.

L:Int keeps to the integers: it divides by a number or another L:Int and nothing else, so L:Int / complex and L:Int / L:Real both raise.

L:Real and L:Complex divide by each other freely, and the result is the wider of the two: L:Real / L:Complex is an L:Complex.

Modulus

% is defined on lattice integers only: L:Int % L:Int, L:Int % number and number % L:Int all give an L:Int. L:Real % L:Real raises; use math.fmod on the values, or build the remainder from :floor().

Comparisons and reductions

Lua's own <, <=, == and so on compare scalars and give a boolean. A comparison of fields has to give an answer per site, so it cannot be an operator: qlua provides the six as named functions, each returning an L:Int that is 1 where the comparison holds and 0 where it does not. That is a mask, ready for L:Subset, for multiplying, or for :sum() to count.

All eight below take two arguments, each a lattice Int, a lattice Real or a plain number, with at least one of them a field. A number is treated as that number at every site.

qcd.eq

qcd.eq(a, b)                                      --> L:Int

1 at each site where a == b, 0 where it does not.

qcd.ne

qcd.ne(a, b)                                      --> L:Int

1 at each site where a ~= b, 0 where they are equal.

qcd.lt

qcd.lt(a, b)                                      --> L:Int

1 at each site where a < b, 0 elsewhere.

qcd.le

qcd.le(a, b)                                      --> L:Int

1 at each site where a <= b, 0 elsewhere.

qcd.gt

qcd.gt(a, b)                                      --> L:Int

1 at each site where a > b, 0 elsewhere.

qcd.ge

qcd.ge(a, b)                                      --> L:Int

1 at each site where a >= b, 0 elsewhere.

local t      = L:pcoord(3)
local early  = L:Subset(qcd.lt(t, 8))             -- the first eight slices
local n_even = qcd.eq(t % 2, 0):sum()             -- how many sites have even t

qcd.min

qcd.min(a, b)                                     --> L:Int or L:Real

At each site, whichever of a and b is smaller. The result is an L:Real if either argument is real, an L:Int otherwise.

qcd.max

qcd.max(a, b)                                     --> L:Int or L:Real

At each site, whichever of a and b is larger. The result follows the same rule.

Neither reduces: both give a field the size of the lattice, one value per site. To reduce a Real field to one number use R:vmin() and R:vmax().

qcd.dot

qcd.dot(a, b)                                     --> complex or L:Complex

The inner product a† · b, contracted over every index the type has except the lattice: for lattice arguments the result is a lattice Complex, one number per site, which :sum() then reduces. Defined for complex, the four sequential color types, and the lattice Real, Complex, ColorVector, ColorMatrix, DiracFermion and DiracPropagator.

local norm = qcd.dot(psi, psi):sum():real()       -- the same as psi:norm2()