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 side | The other | Result |
|---|
number | number | number | number | complex | complex | number | gamma | gamma | number | L:Int | L:Int | number | L:Real | L:Real | number | L:Complex | L:Complex | complex | complex | complex | complex | gamma | gamma | complex | L:Real | L:Complex | complex | L:Complex | L:Complex | gamma | gamma | gamma | matrix.real | matrix.real | matrix.real | matrix.complex | matrix.complex | matrix.complex | ColorVector | ColorVector | ColorVector | ColorMatrix | ColorMatrix | ColorMatrix | DiracFermion | DiracFermion | DiracFermion | DiracPropagator | DiracPropagator | DiracPropagator | L:Int | L:Int | L:Int | L:Real | L:Real | L:Real | L:Real | L:Complex | L:Complex | L:Complex | L:Complex | L:Complex | L:ColorVector | L:ColorVector | L:ColorVector | L:ColorMatrix | L:ColorMatrix | L:ColorMatrix | L:DiracFermion | L:DiracFermion | L:DiracFermion | L:DiracPropagator | L:DiracPropagator | L: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.
| Object | May be scaled by | Result |
|---|
gamma | number, complex | gamma | matrix.real | number | matrix.real | matrix.complex | number, complex | matrix.complex | ColorVector | number, complex | ColorVector | ColorMatrix | number, complex | ColorMatrix | DiracFermion | number, complex | DiracFermion | DiracPropagator | number, complex | DiracPropagator | L:Int | number | L:Int | L:ColorVector | number, complex, L:Real, L:Complex | L:ColorVector | L:ColorMatrix | number, complex, L:Real, L:Complex | L:ColorMatrix | L:DiracFermion | number, complex, L:Real, L:Complex | L:DiracFermion | L:DiracPropagator | number, complex, L:Real, L:Complex | L: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.
| Left | Right | Result |
|---|
gamma | gamma | gamma | gamma | DiracFermion | DiracFermion | gamma | DiracPropagator | DiracPropagator | gamma | L:DiracFermion | L:DiracFermion | gamma | L:DiracPropagator | L:DiracPropagator | matrix.real | matrix.real | matrix.real | matrix.complex | matrix.complex | matrix.complex | ColorMatrix | ColorVector | ColorVector | ColorMatrix | ColorMatrix | ColorMatrix | ColorMatrix | DiracFermion | DiracFermion | ColorMatrix | DiracPropagator | DiracPropagator | DiracPropagator | gamma | DiracPropagator | DiracPropagator | ColorMatrix | DiracPropagator | DiracPropagator | DiracPropagator | DiracPropagator | L:Int | L:Int | L:Int | L:ColorMatrix | L:ColorVector | L:ColorVector | L:ColorMatrix | L:ColorMatrix | L:ColorMatrix | L:ColorMatrix | L:DiracFermion | L:DiracFermion | L:ColorMatrix | L:DiracPropagator | L:DiracPropagator | L:DiracPropagator | gamma | L:DiracPropagator | L:DiracPropagator | L:ColorMatrix | L:DiracPropagator | L:DiracPropagator | L:DiracPropagator | L: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.
| Dividend | Divisor | Result |
|---|
number | number | number | number | complex | complex | number | L:Int | L:Int | number | L:Real | L:Real | number | L:Complex | L:Complex | complex | number | complex | complex | complex | complex | complex | L:Real | L:Complex | complex | L:Complex | L:Complex | gamma | number | gamma | gamma | complex | gamma | matrix.real | number | matrix.real | matrix.complex | number | matrix.complex | matrix.complex | complex | matrix.complex | ColorVector | number | ColorVector | ColorVector | complex | ColorVector | ColorMatrix | number | ColorMatrix | ColorMatrix | complex | ColorMatrix | DiracFermion | number | DiracFermion | DiracFermion | complex | DiracFermion | DiracPropagator | number | DiracPropagator | DiracPropagator | complex | DiracPropagator | L:Int | number | L:Int | L:Int | L:Int | L:Int | L:Real | number | L:Real | L:Real | complex | L:Complex | L:Real | L:Real | L:Real | L:Real | L:Complex | L:Complex | L:Complex | number | L:Complex | L:Complex | complex | L:Complex | L:Complex | L:Real | L:Complex | L:Complex | L:Complex | L:Complex | L:ColorVector | number | L:ColorVector | L:ColorVector | complex | L:ColorVector | L:ColorMatrix | number | L:ColorMatrix | L:ColorMatrix | complex | L:ColorMatrix | L:DiracFermion | number | L:DiracFermion | L:DiracFermion | complex | L:DiracFermion | L:DiracPropagator | number | L:DiracPropagator | L:DiracPropagator | complex | L: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()
|