Overview¶
Creation of models¶
Note
This section assumes that the classes and functions
from matchingtools.operators
that it uses are in the namespace.
To import all the classes and functions that appear here do:
from matchingtools.operators import (
Tensor, Operator, OperatorSum
TensorBuilder, FieldBuilder, D, Op, OpSum,
number_op, power_op)
The construction of a model is done in two steps: the creation of the tensors and fields and the definition of the interaction Lagrangian.
The basic building block for a Lagrangian is tensor, an object of the
class Tensor
. Direct usage of the Tensor
constructor obscures the code. There are two classes defined to make the
process of creating tensors easier and cleaner,
TensorBuilder
and
FieldBuilder
.
When a field has covariant derivatives applied to it, that is
represented internally in the attributes of its representative
Tensor
object. For aesthetical reasons and easeness of usage,
instead of manually modifying the attributes, it’s better to use
the function D
to create covariant
derivatives of fields.
MatchingTools handles Lagrangians that are polynomials of the fields.
They are thus a sum of terms that are products of tensors. They are
represented as OperatorSum
objects, with only one
attribute: a list of its terms. Each term should be an operator, that is,
a product of tensors represented by an Operator
object with only one attribute: a list of its factors.
Instead of using the constructors for both classes, the functions
OpSum
and Op
are
available to make the definitions clearer.
Minus signs are defined in the library for operators (
Operator.__minus__()
) and operator sums
(OperatorSum.__minus__()
). Multiplication *
is defined for operators too (as the
concatenation of the tensors they contain,
see Operator.__mul__()
).
MatchingTools treats in a special way tensors whose name starts and
ends with square or curly brakets. A tensor name enclosed in square
brakets is understood as a (complex) number to be read from the name
using float(name[1:-1])
. The function number_op()
is to be used to create operators with such tensors inside.
When the name of a tensor starts and ends with curly brakets it’s
it represents some symbolic constant that appears exponentiated.
The name should be of the form "{base^exponent}"
. Curly brakets
allow for the summation of the exponents of tensors that appear in
the same tensor and have the same base and indices. This is used
mainly to produce more readable results. The function designed to
create operators containing such tensors is power_op()
.
Creation of tensors¶
Create a tensor as:
my_tensor = TensorBuilder("my_tensor")
and then use it inside an operator:
Op(..., my_tensor(ind1, ind2, ...), ...)
with ind1
, ind2
, … being integers.
Creation of fields¶
Create a field as:
my_field = FieldBuilder("my_field", dimension, statistics)
where dimension (float) represents the energy dimensions of the field
and statistics is either equal to matchingtools.algebra.boson
or
matchingtools.algebra.fermion
. Then use it inside an operator:
Op(..., my_field(ind1, ind2, ...), ...)
with ind1
, ind2
, … being integers.
Definition of the Lagrangian¶
Define the interaction Lagrangian as an operator sum:
int_lag = OpSum(op1, op2, ...)
Each argument to the function matchingtools.operators.OpSum()
should
be an operator defined as:
op1 = Op(tens1(ind1, ind2, ...), field1(ind3, ind4, ...), ...)
The arguments of the function matchingtools.operators.Op()
are
tensors (and fields). Their indices are integer numbers. Negative
integers are reserved for free indices. Free indices are not meant
to be used in the operators appearing in the Lagrangian, but later
in the definition of their transformations.
Non-negative integers represent contracted indices. Contraction is expressed by repetition of indices.
To introduce the covariant derivative with index ind
of a tensor
tens
inside an operator, use the function
matchingtools.operators.D()
in the following way:
D(ind, tens(ind1, ind2, ...))
If a numeric coefficient num
is needed for some operator op
it can be introduced as:
number_op(num) * op
A symbolic constant s
to some power p
can multiply an operator as:
power_op("s", p) * op
Integration¶
Note
This section assumes that the classes and functions
from matchingtools.integration
that it uses are in the namespace.
To import all the classes and functions that appear here do:
from matchingtools.integration import (
RealScalar, ComplexScalar, RealVector, ComplexVector,
VectorLikeFermion, MajoranaFermion, integrate)
To integrate some heavy fields out of a previously constructed Lagrangian the heavy fields should be specified first. The heavy fields should be objects of any of the following classes:
Create a heavy field using the constructors of these classes.
heavy_field = HeavyFieldClass("field_name", ...)
Then collect the heavy fields in a list and use the function
integrate()
to perform the integration:
heavy_fields = [heavy_field_1, heavy_field_2, ...]
eff_lag = integrate(heavy_fields, int_lag, max_dim=...)
Transformations of the effective Lagrangian¶
Note
This section assumes that the functions
from matchingtools.transformations
and
matchingtools.transformations
that it uses are in the namespace.
To import all the functions that appear here do:
from matchingtools.operators import tensor_op, flavor_tensor_op
from matchingtools.transformations import (
simplify, apply_rules)
An effective Lagrangian obtained from integration of heavy fields usually contains operators that aren’t independent. Several transformations can be applied to them to write the Lagrangian in terms of a set of operators (a basis) that spans the space of effective operators.
These transformations are such as Fierz identities or substitutions of the equations of motion of the light particles. All of them consist of the subsitution of operators or parts of them by sums of other operators. The operations described in this section applied to effective Lagrangians or to any other kind of operators sum.
The first step to simplify an effective Lagrangian is to collect and multiply numeric coefficients and constant tensors that appear several times inside the same operator. To do this, use:
simplified_lag_1 = simplify(effective_lagrangian)
Then we can define a set of rules as a list of pairs
(pattern, replacement)
where pattern
is an operator
and replacement
is an operator sum:
rules = [(Op(...), OpSum(...)), (Op(...), OpSum(...)), ...]
pattern
may contain tensors with negative indices corresponding
to indices that are not contracted inside pattern
. In that
case, the operators in replacement should also contain the same
negative indices. When pattern
is substituted inside an operator
op
, the indices in op
outside pattern
that are contracted
with indices inside pattern appear as contracted with the
corresponding ones in the operators of replacement
. For example,
to replace \(t_{ij}r_{ik}\) by \(-s_{mnk}u_{nmj}\) we would
write the rule:
(Op(t(1, -1), r(1, -2)), OpSum(-Op(s(1, 2, -2), u(2, 1, -1))))
The operators of the basis should be represented by tensor with a name
identifing the operator. They can be defined using
matchingtools.operators.tensor_op()
when they don’t have
free indices and matchingtools.operators.flavor_tensor_op()
when they do. So we usually define:
Op1 = tensor_op("Op1")
Op2 = tensor_op("Op2")
...
Opf1 = flavor_tensor_op("Opf1")
Opf2 = flavor_tensor_op("Opf1")
...
and then specify how to identify them using rules:
op_def_rules = [(Op(...), OpSum(Op1)),
(Op(...), OpSum(Op2)),
...
(Op(...), OpSum(Opf1(i1, i2, ...))),
(Op(...), OpSum(Opf2(i1, i2, ...)))
...]
Then we are ready to apply the rules using apply_rules()
:
simplified_lag_2 = apply_rules_until(
simplified_lag_1, rules + op_def_rule, max_iter)
where max_iter
is the maximum number of applications of all the
rules to each operator.
Output of the results¶
Note
This section assumes that the class
matchingtools.output.Writer
that it uses is in the namespace.
To import it, do:
from matchingtools.output import Writer
It’s usually convenient to organize the final results by presenting
the coefficient to each operator of the effective Lagrangian.
When a set of rules has been applied to the effective Lagrangian so
that it is written as an matchingtools.operators.OperatorSum
whose
elements are matchingtools.operators.Operator
objects each of which
contains one tensor representing the actual operator in the basis and
other tensors representing the coefficient the operator has.
To output the results in this form in a human readable format, the
Writer
is provided. If op_names
is a list of the names
of the tensors representing the operators in the basis and lag
is
the Lagrangian that we want to write, we do:
lag_writer = Writer(eff_lag, op_names)
To write the results to a file in plain text, just use:
lag_writer.write_text_file("filename")
To write it in LaTeX two python dictionaries expressing how the tensors that appear in the coefficients and how the name of the coefficients for the operators should be written in LaTeX:
tensors_latex = {"tensor1": r"latexrep", "tensor2": ..., ...}
ops_latex = {"op1": r"latexrep", ...}
The values of the dictionary should be code to be written inside some
LaTeX equation environment. It is recommended to use r"..."
instead
of "..."
to easily write instructions as \instr
instead of
the form \\instr
that would be needed for the case with just
"..."
. The placeholders for the indices should be written in
python’s str.format
style "{}"
. This implies that whenever
curly braces are needed for the LaTeX code, double braces {{...}}
should be used.
The symbols to de used to represent indices should be given also as a list of strings containing the LaTeX code representing them:
indices_latex = ["i", "j", ...]
Finally we can write the LaTeX document using:
lag_writer.write_latex("filename", tensors_latex, ops_latex, indices_latex)
Or we can instead use Writer.show_latex()
to write it, compile it
and show it all in method:
lag_writer.show_latex("filename", pdf_viewer, tensors_latex,
ops_latex, indices_latex)