Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F11222344
fusion.ml
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
129 KB
Subscribers
None
fusion.ml
View Options
(* fusion.ml --
Copyright (C) 1999-2023 by
Wolfgang Kilian <kilian@physik.uni-siegen.de>
Thorsten Ohl <ohl@physik.uni-wuerzburg.de>
Juergen Reuter <juergen.reuter@desy.de>
with contributions from
Christian Speckner <cnspeckn@googlemail.com>
Marco Sekulla <marco.sekulla@kit.edu>
WHIZARD is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
WHIZARD is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *)
(* Avoid refering to [Pervasives.compare], because [Pervasives] will
become [Stdlib.Pervasives] in O'Caml 4.07 and [Stdlib] in O'Caml 4.08. *)
let
pcompare
=
compare
module
type
T
=
sig
val
options
:
Options
.
t
val
vintage
:
bool
type
wf
val
conjugate
:
wf
->
wf
type
flavor
type
flavor_sans_color
val
flavor
:
wf
->
flavor
val
flavor_sans_color
:
wf
->
flavor_sans_color
type
p
val
momentum
:
wf
->
p
val
momentum_list
:
wf
->
int
list
val
wf_tag
:
wf
->
string
option
type
constant
type
coupling
type
rhs
type
'
a
children
val
sign
:
rhs
->
int
val
coupling
:
rhs
->
constant
Coupling
.
t
val
coupling_tag
:
rhs
->
string
option
type
exclusions
val
no_exclusions
:
exclusions
val
children
:
rhs
->
wf
list
type
fusion
val
lhs
:
fusion
->
wf
val
rhs
:
fusion
->
rhs
list
type
braket
val
bra
:
braket
->
wf
val
ket
:
braket
->
rhs
list
type
amplitude
type
amplitude_sans_color
type
selectors
val
amplitudes
:
bool
->
exclusions
->
selectors
->
flavor_sans_color
list
->
flavor_sans_color
list
->
amplitude
list
val
amplitude_sans_color
:
bool
->
exclusions
->
selectors
->
flavor_sans_color
list
->
flavor_sans_color
list
->
amplitude_sans_color
val
dependencies
:
amplitude
->
wf
->
(
wf
,
coupling
)
Tree2
.
t
val
incoming
:
amplitude
->
flavor
list
val
outgoing
:
amplitude
->
flavor
list
val
externals
:
amplitude
->
wf
list
val
variables
:
amplitude
->
wf
list
val
fusions
:
amplitude
->
fusion
list
val
brakets
:
amplitude
->
braket
list
val
on_shell
:
amplitude
->
(
wf
->
bool
)
val
is_gauss
:
amplitude
->
(
wf
->
bool
)
val
constraints
:
amplitude
->
string
option
val
symmetry
:
amplitude
->
int
val
allowed
:
amplitude
->
bool
(*i
val initialize_cache : string -> unit
val set_cache_name : string -> unit
i*)
val
check_charges
:
unit
->
flavor_sans_color
list
list
val
count_fusions
:
amplitude
->
int
val
count_propagators
:
amplitude
->
int
val
count_diagrams
:
amplitude
->
int
val
forest
:
wf
->
amplitude
->
((
wf
*
coupling
option
,
wf
)
Tree
.
t
)
list
val
poles
:
amplitude
->
wf
list
list
val
s_channel
:
amplitude
->
wf
list
val
tower_to_dot
:
out_channel
->
amplitude
->
unit
val
amplitude_to_dot
:
out_channel
->
amplitude
->
unit
val
phase_space_channels
:
out_channel
->
amplitude_sans_color
->
unit
val
phase_space_channels_flipped
:
out_channel
->
amplitude_sans_color
->
unit
end
module
type
Maker
=
functor
(
P
:
Momentum
.
T
)
->
functor
(
M
:
Model
.
T
)
->
T
with
type
p
=
P
.
t
and
type
flavor
=
Colorize
.
It
(
M
).
flavor
and
type
flavor_sans_color
=
M
.
flavor
and
type
constant
=
M
.
constant
and
type
selectors
=
Cascade
.
Make
(
M
)(
P
).
selectors
(* \thocwmodulesection{Fermi Statistics} *)
module
type
Stat
=
sig
(* This will be [Model.T.flavor]. *)
type
flavor
(* A record of the fermion lines in the 1POW. *)
type
stat
(* Vertices with an odd number of fermion fields. *)
exception
Impossible
(* External lines. *)
val
stat
:
flavor
->
int
->
stat
(* [stat_fuse (Some flines) slist f] combines the fermion lines
in the elements of [slist] according to the connections listed
in [flines].
On the other hand, [stat_fuse None slist f] corresponds to
the legacy mode with \emph{at most} two fermions.
The resulting flavor [f] of the 1POW can be ignored for models
with only Dirac fermions, except for debugging, since
the direction of the arrows is unambiguous.
However, in the case of Majorana fermions and/or fermion number
violating interactions, the flavor [f] must be used. *)
val
stat_fuse
:
Coupling
.
fermion_lines
option
->
stat
list
->
flavor
->
stat
(* Analogous to [stat_fuse], but for the finalizing keystone
instead of the 1POW. *)
val
stat_keystone
:
Coupling
.
fermion_lines
option
->
stat
list
->
flavor
->
stat
(* Compute the sign corresponding to the fermion lines in
a 1POW or keystone. *)
val
stat_sign
:
stat
->
int
(* Debugging and consistency checks \ldots *)
val
stat_to_string
:
stat
->
string
val
equal
:
stat
->
stat
->
bool
val
saturated
:
stat
->
bool
end
module
type
Stat_Maker
=
functor
(
M
:
Model
.
T
)
->
Stat
with
type
flavor
=
M
.
flavor
(* \thocwmodulesection{Dirac Fermions} *)
let
dirac_log
silent
logging
=
logging
let
dirac_log
silent
logging
=
silent
exception
Majorana
module
Stat_Dirac
(
M
:
Model
.
T
)
:
(
Stat
with
type
flavor
=
M
.
flavor
)
=
struct
type
flavor
=
M
.
flavor
(* \begin{equation}
\gamma_\mu\psi(1)\,G^{\mu\nu}\,\bar\psi(2)\gamma_\nu\psi(3)
- \gamma_\mu\psi(3)\,G^{\mu\nu}\,\bar\psi(2)\gamma_\nu\psi(1)
\end{equation} *)
type
stat
=
|
Fermion
of
int
*
(
int
option
*
int
option
)
list
|
AntiFermion
of
int
*
(
int
option
*
int
option
)
list
|
Boson
of
(
int
option
*
int
option
)
list
let
lines_to_string
lines
=
ThoList
.
to_string
(
function
|
Some
i
,
Some
j
->
Printf
.
sprintf
"%d>%d"
i
j
|
Some
i
,
None
->
Printf
.
sprintf
"%d>*"
i
|
None
,
Some
j
->
Printf
.
sprintf
"*>%d"
j
|
None
,
None
->
"*>*"
)
lines
let
stat_to_string
=
function
|
Boson
lines
->
Printf
.
sprintf
"Boson %s"
(
lines_to_string
lines
)
|
Fermion
(
p
,
lines
)
->
Printf
.
sprintf
"Fermion (%d, %s)"
p
(
lines_to_string
lines
)
|
AntiFermion
(
p
,
lines
)
->
Printf
.
sprintf
"AntiFermion (%d, %s)"
p
(
lines_to_string
lines
)
let
equal
s1
s2
=
match
s1
,
s2
with
|
Boson
l1
,
Boson
l2
->
List
.
sort
compare
l1
=
List
.
sort
compare
l2
|
Fermion
(
p1
,
l1
),
Fermion
(
p2
,
l2
)
|
AntiFermion
(
p1
,
l1
),
AntiFermion
(
p2
,
l2
)
->
p1
=
p2
&&
List
.
sort
compare
l1
=
List
.
sort
compare
l2
|
_
->
false
let
saturated
=
function
|
Boson
_
->
true
|
_
->
false
let
stat
f
p
=
match
M
.
fermion
f
with
|
0
->
Boson
[]
|
1
->
Fermion
(
p
,
[]
)
|
-
1
->
AntiFermion
(
p
,
[]
)
|
2
->
raise
Majorana
|
_
->
invalid_arg
"Fusion.Stat_Dirac: invalid fermion number"
exception
Impossible
let
stat_fuse_pair_legacy
f
s1
s2
=
match
s1
,
s2
with
|
Boson
l1
,
Boson
l2
->
Boson
(
l1
@
l2
)
|
Boson
l1
,
Fermion
(
p
,
l2
)
->
Fermion
(
p
,
l1
@
l2
)
|
Boson
l1
,
AntiFermion
(
p
,
l2
)
->
AntiFermion
(
p
,
l1
@
l2
)
|
Fermion
(
p
,
l1
),
Boson
l2
->
Fermion
(
p
,
l1
@
l2
)
|
AntiFermion
(
p
,
l1
),
Boson
l2
->
AntiFermion
(
p
,
l1
@
l2
)
|
AntiFermion
(
pbar
,
l1
),
Fermion
(
p
,
l2
)
->
Boson
((
Some
pbar
,
Some
p
)
::
l1
@
l2
)
|
Fermion
(
p
,
l1
),
AntiFermion
(
pbar
,
l2
)
->
Boson
((
Some
pbar
,
Some
p
)
::
l1
@
l2
)
|
Fermion
_,
Fermion
_
|
AntiFermion
_,
AntiFermion
_
->
raise
Impossible
let
stat_fuse_legacy
s1
s23__n
f
=
List
.
fold_right
(
stat_fuse_pair_legacy
f
)
s23__n
s1
let
stat_fuse_legacy_logging
s1
s23__n
f
=
let
s
=
stat_fuse_legacy
s1
s23__n
f
in
Printf
.
eprintf
"stat_fuse_legacy: %s <- %s -> %s
\n
"
(
M
.
flavor_to_string
f
)
(
ThoList
.
to_string
stat_to_string
(
s1
::
s23__n
))
(
stat_to_string
s
);
s
let
stat_fuse_legacy
=
dirac_log
stat_fuse_legacy
stat_fuse_legacy_logging
module
IMap
=
Map
.
Make
(
struct
type
t
=
int
let
compare
=
compare
end
)
type
partial
=
{
stat
:
stat
(* the [stat] accumulated so far *)
;
fermions
:
int
IMap
.
t
(* a map from the indices in the vertex to open fermion lines *)
;
antifermions
:
int
IMap
.
t
(* a map from the indices in the vertex to open antifermion lines *)
;
n
:
int
(* the number of incoming propagators *)
}
let
partial_to_string
p
=
Printf
.
sprintf
"{ fermions=%s, antifermions=%s, state=%s, #=%d }"
(
ThoList
.
to_string
(
fun
(
i
,
f
)
->
Printf
.
sprintf
"%d@%d"
f
i
)
(
IMap
.
bindings
p
.
fermions
))
(
ThoList
.
to_string
(
fun
(
i
,
f
)
->
Printf
.
sprintf
"%d@%d"
f
i
)
(
IMap
.
bindings
p
.
antifermions
))
(
stat_to_string
p
.
stat
)
p
.
n
let
add_lines
l
=
function
|
Boson
l'
->
Boson
(
List
.
rev_append
l
l'
)
|
Fermion
(
n
,
l'
)
->
Fermion
(
n
,
List
.
rev_append
l
l'
)
|
AntiFermion
(
n
,
l'
)
->
AntiFermion
(
n
,
List
.
rev_append
l
l'
)
let
partial_of_slist
slist
=
List
.
fold_left
(
fun
acc
s
->
let
n
=
succ
acc
.
n
in
match
s
with
|
Boson
l
->
{
acc
with
stat
=
add_lines
l
acc
.
stat
;
n
}
|
Fermion
(
p
,
l
)
->
{
acc
with
fermions
=
IMap
.
add
n
p
acc
.
fermions
;
stat
=
add_lines
l
acc
.
stat
;
n
}
|
AntiFermion
(
p
,
l
)
->
{
acc
with
antifermions
=
IMap
.
add
n
p
acc
.
antifermions
;
stat
=
add_lines
l
acc
.
stat
;
n
}
)
{
stat
=
Boson
[]
;
fermions
=
IMap
.
empty
;
antifermions
=
IMap
.
empty
;
n
=
0
}
slist
let
find_opt
p
map
=
try
Some
(
IMap
.
find
p
map
)
with
Not_found
->
None
let
match_fermion_line
p
(
i
,
j
)
=
if
i
<=
p
.
n
&&
j
<=
p
.
n
then
match
find_opt
i
p
.
fermions
,
find_opt
j
p
.
antifermions
with
|
(
Some
_
as
f
),
(
Some
_
as
fbar
)
->
{
p
with
stat
=
add_lines
[
fbar
,
f
]
p
.
stat
;
fermions
=
IMap
.
remove
i
p
.
fermions
;
antifermions
=
IMap
.
remove
j
p
.
antifermions
}
|
_
->
invalid_arg
"match_fermion_line: mismatched boson"
else
if
i
<=
p
.
n
then
match
find_opt
i
p
.
fermions
,
p
.
stat
with
|
Some
f
,
Boson
l
->
{
p
with
stat
=
Fermion
(
f
,
l
);
fermions
=
IMap
.
remove
i
p
.
fermions
}
|
_
->
invalid_arg
"match_fermion_line: mismatched fermion"
else
if
j
<=
p
.
n
then
match
find_opt
j
p
.
antifermions
,
p
.
stat
with
|
Some
fbar
,
Boson
l
->
{
p
with
stat
=
AntiFermion
(
fbar
,
l
);
antifermions
=
IMap
.
remove
j
p
.
antifermions
}
|
_
->
invalid_arg
"match_fermion_line: mismatched antifermion"
else
failwith
"match_fermion_line: impossible"
let
match_fermion_line_logging
p
(
i
,
j
)
=
Printf
.
eprintf
"match_fermion_line %s (%d, %d)"
(
partial_to_string
p
)
i
j
;
let
p'
=
match_fermion_line
p
(
i
,
j
)
in
Printf
.
eprintf
" >> %s
\n
"
(
partial_to_string
p'
);
p'
let
match_fermion_line
=
dirac_log
match_fermion_line
match_fermion_line_logging
let
match_fermion_lines
flines
s1
s23__n
=
let
p
=
partial_of_slist
(
s1
::
s23__n
)
in
List
.
fold_left
match_fermion_line
p
flines
let
stat_fuse_new
flines
s1
s23__n
f
=
(
match_fermion_lines
flines
s1
s23__n
).
stat
let
stat_fuse_new_checking
flines
s1
s23__n
f
=
let
stat
=
stat_fuse_new
flines
s1
s23__n
f
in
if
List
.
length
flines
<
2
then
begin
let
legacy
=
stat_fuse_legacy
s1
s23__n
f
in
if
not
(
equal
stat
legacy
)
then
failwith
(
Printf
.
sprintf
"Fusion.Stat_Dirac.stat_fuse_new: %s <> %s!"
(
stat_to_string
stat
)
(
stat_to_string
legacy
))
end
;
stat
let
stat_fuse_new_logging
flines
s1
s23__n
f
=
Printf
.
eprintf
"stat_fuse_new: connecting fermion lines %s in %s <- %s
\n
"
(
UFO_Lorentz
.
fermion_lines_to_string
flines
)
(
M
.
flavor_to_string
f
)
(
ThoList
.
to_string
stat_to_string
(
s1
::
s23__n
));
stat_fuse_new_checking
flines
s1
s23__n
f
let
stat_fuse_new
=
dirac_log
stat_fuse_new
stat_fuse_new_logging
let
stat_fuse
flines_opt
slist
f
=
match
slist
with
|
[]
->
invalid_arg
"Fusion.Stat_Dirac.stat_fuse: empty"
|
s1
::
s23__n
->
begin
match
flines_opt
with
|
Some
flines
->
stat_fuse_new
flines
s1
s23__n
f
|
None
->
stat_fuse_legacy
s1
s23__n
f
end
let
stat_fuse_logging
flines_opt
slist
f
=
Printf
.
eprintf
"stat_fuse: %s <- %s
\n
"
(
M
.
flavor_to_string
f
)
(
ThoList
.
to_string
stat_to_string
slist
);
stat_fuse
flines_opt
slist
f
let
stat_fuse
=
dirac_log
stat_fuse
stat_fuse_logging
let
stat_keystone_legacy
s1
s23__n
f
=
let
s2
=
List
.
hd
s23__n
and
s34__n
=
List
.
tl
s23__n
in
stat_fuse_legacy
s1
[
stat_fuse_legacy
s2
s34__n
(
M
.
conjugate
f
)]
f
let
stat_keystone_legacy_logging
s1
s23__n
f
=
let
s
=
stat_keystone_legacy
s1
s23__n
f
in
Printf
.
eprintf
"stat_keystone_legacy: %s (%s) %s -> %s
\n
"
(
stat_to_string
s1
)
(
M
.
flavor_to_string
f
)
(
ThoList
.
to_string
stat_to_string
s23__n
)
(
stat_to_string
s
);
s
let
stat_keystone_legacy
=
dirac_log
stat_keystone_legacy
stat_keystone_legacy_logging
let
stat_keystone
flines_opt
slist
f
=
match
slist
with
|
[]
->
invalid_arg
"Fusion.Stat_Dirac.stat_keystone: empty"
|
[
s
]
->
invalid_arg
"Fusion.Stat_Dirac.stat_keystone: singleton"
|
s1
::
(
s2
::
s34__n
as
s23__n
)
->
begin
match
flines_opt
with
|
None
->
stat_keystone_legacy
s1
s23__n
f
|
Some
flines
->
(* The fermion line indices in [flines] must match
the lines on one side of the keystone. *)
let
stat
=
stat_fuse_legacy
s1
[
stat_fuse_new
flines
s2
s34__n
f
]
f
in
if
saturated
stat
then
stat
else
failwith
(
Printf
.
sprintf
"Fusion.Stat_Dirac.stat_keystone: incomplete %s!"
(
stat_to_string
stat
))
end
let
stat_keystone_logging
flines_opt
slist
f
=
let
s
=
stat_keystone
flines_opt
slist
f
in
Printf
.
eprintf
"stat_keystone: %s (%s) %s -> %s
\n
"
(
stat_to_string
(
List
.
hd
slist
))
(
M
.
flavor_to_string
f
)
(
ThoList
.
to_string
stat_to_string
(
List
.
tl
slist
))
(
stat_to_string
s
);
s
let
stat_keystone
=
dirac_log
stat_keystone
stat_keystone_logging
(* \begin{figure}
\begin{displaymath}
\parbox{26\unitlength}{%
\begin{fmfgraph*}(25,15)
\fmfstraight
\fmfleft{f}
\fmfright{f1,f2,f3}
\fmflabel{$\psi(1)$}{f1}
\fmflabel{$\bar\psi(2)$}{f2}
\fmflabel{$\psi(3)$}{f3}
\fmflabel{$0$}{f}
\fmf{fermion}{f1,v1,f}
\fmffreeze
\fmf{fermion,tension=0.5}{f3,v2,f2}
\fmf{photon}{v1,v2}
\fmfdot{v1,v2}
\end{fmfgraph*}}
\qquad\qquad-\qquad
\parbox{26\unitlength}{%
\begin{fmfgraph*}(25,15)
\fmfstraight
\fmfleft{f}
\fmfright{f1,f2,f3}
\fmflabel{$\psi(1)$}{f1}
\fmflabel{$\bar\psi(2)$}{f2}
\fmflabel{$\psi(3)$}{f3}
\fmflabel{$0$}{f}
\fmf{fermion}{f3,v1,f}
\fmffreeze
\fmf{fermion,tension=0.5}{f1,v2,f2}
\fmf{photon}{v1,v2}
\fmfdot{v1,v2}
\end{fmfgraph*}}
\end{displaymath}
\caption{\label{fig:stat_fuse} Relative sign from Fermi statistics.}
\end{figure} *)
(* \begin{equation}
\epsilon \left(\left\{ (0,1), (2,3) \right\}\right)
= - \epsilon \left(\left\{ (0,3), (2,1) \right\}\right)
\end{equation} *)
let
permutation
lines
=
let
fout
,
fin
=
List
.
split
lines
in
let
eps_in
,
_
=
Combinatorics
.
sort_signed
fin
and
eps_out
,
_
=
Combinatorics
.
sort_signed
fout
in
(
eps_in
*
eps_out
)
(* \begin{dubious}
This comparing of permutations of fermion lines is a bit tedious
and takes a macroscopic fraction of time. However, it's less than
20\,\%, so we don't focus on improving on it yet.
\end{dubious} *)
let
stat_sign
=
function
|
Boson
lines
->
permutation
lines
|
Fermion
(
p
,
lines
)
->
permutation
((
None
,
Some
p
)
::
lines
)
|
AntiFermion
(
pbar
,
lines
)
->
permutation
((
Some
pbar
,
None
)
::
lines
)
end
(* \thocwmodulesection{Tags} *)
module
type
Tags
=
sig
type
wf
type
coupling
type
'
a
children
val
null_wf
:
wf
val
null_coupling
:
coupling
val
fuse
:
coupling
->
wf
children
->
wf
val
wf_to_string
:
wf
->
string
option
val
coupling_to_string
:
coupling
->
string
option
end
module
type
Tagger
=
functor
(
PT
:
Tuple
.
Poly
)
->
Tags
with
type
'
a
children
=
'
a
PT
.
t
module
type
Tagged_Maker
=
functor
(
Tagger
:
Tagger
)
->
functor
(
P
:
Momentum
.
T
)
->
functor
(
M
:
Model
.
T
)
->
T
with
type
p
=
P
.
t
and
type
flavor
=
Colorize
.
It
(
M
).
flavor
and
type
flavor_sans_color
=
M
.
flavor
and
type
constant
=
M
.
constant
(* No tags is one option for good tags \ldots *)
module
No_Tags
(
PT
:
Tuple
.
Poly
)
=
struct
type
wf
=
unit
type
coupling
=
unit
type
'
a
children
=
'
a
PT
.
t
let
null_wf
=
()
let
null_coupling
=
()
let
fuse
()
_
=
()
let
wf_to_string
()
=
None
let
coupling_to_string
()
=
None
end
(* \begin{dubious}
Here's a simple additive tag that can grow into something useful
for loop calculations.
\end{dubious} *)
module
Loop_Tags
(
PT
:
Tuple
.
Poly
)
=
struct
type
wf
=
int
type
coupling
=
int
type
'
a
children
=
'
a
PT
.
t
let
null_wf
=
0
let
null_coupling
=
0
let
fuse
c
wfs
=
PT
.
fold_left
(+)
c
wfs
let
wf_to_string
n
=
Some
(
string_of_int
n
)
let
coupling_to_string
n
=
Some
(
string_of_int
n
)
end
module
Order_Tags
(
PT
:
Tuple
.
Poly
)
=
struct
type
wf
=
int
type
coupling
=
int
type
'
a
children
=
'
a
PT
.
t
let
null_wf
=
0
let
null_coupling
=
0
let
fuse
c
wfs
=
PT
.
fold_left
(+)
c
wfs
let
wf_to_string
n
=
Some
(
string_of_int
n
)
let
coupling_to_string
n
=
Some
(
string_of_int
n
)
end
(* \thocwmodulesection{[Tagged], the [Fusion.Make] Functor} *)
module
Tagged
(
Tagger
:
Tagger
)
(
PT
:
Tuple
.
Poly
)
(
Stat
:
Stat_Maker
)
(
T
:
Topology
.
T
with
type
'
a
children
=
'
a
PT
.
t
)
(
P
:
Momentum
.
T
)
(
M
:
Model
.
T
)
=
struct
let
vintage
=
false
type
cache_mode
=
Cache_Use
|
Cache_Ignore
|
Cache_Overwrite
let
cache_option
=
ref
Cache_Ignore
type
qcd_order
=
|
QCD_order
of
int
type
ew_order
=
|
EW_order
of
int
let
qcd_order
=
ref
(
QCD_order
99
)
let
ew_order
=
ref
(
EW_order
99
)
let
options
=
Options
.
create
[
(*i
"ignore-cache", Arg.Unit (fun () -> cache_option := Cache_Ignore),
" ignore cached model tables (default)";
"use-cache", Arg.Unit (fun () -> cache_option := Cache_Use),
" use cached model tables";
"overwrite-cache", Arg.Unit (fun () -> cache_option := Cache_Overwrite),
" overwrite cached model tables";
i*)
"qcd"
,
Arg
.
Int
(
fun
n
->
qcd_order
:=
QCD_order
n
),
" set QCD order n [>= 0, default = 99] (ignored)"
;
"ew"
,
Arg
.
Int
(
fun
n
->
ew_order
:=
EW_order
n
),
" set QCD order n [>=0, default = 99] (ignored)"
]
exception
Negative_QCD_order
exception
Negative_EW_order
exception
Vanishing_couplings
exception
Negative_QCD_EW_orders
let
int_orders
=
match
!
qcd_order
,
!
ew_order
with
|
QCD_order
n
,
EW_order
n'
when
n
<
0
&&
n'
>=
0
->
raise
Negative_QCD_order
|
QCD_order
n
,
EW_order
n'
when
n
>=
0
&&
n'
<
0
->
raise
Negative_EW_order
|
QCD_order
n
,
EW_order
n'
when
n
<
0
&&
n'
<
0
->
raise
Negative_QCD_EW_orders
|
QCD_order
n
,
EW_order
n'
->
(
n
,
n'
)
open
Coupling
module
S
=
Stat
(
M
)
type
stat
=
S
.
stat
let
stat
=
S
.
stat
let
stat_sign
=
S
.
stat_sign
(* \begin{dubious}
This will do \emph{something} for 4-, 6-, \ldots fermion vertices,
but not necessarily the right thing \ldots
\end{dubious} *)
(* \begin{dubious}
This is copied from [Colorize] and should be factored!
\end{dubious} *)
(* \begin{dubious}
In the long run, it will probably be beneficial to apply
the permutations in [Modeltools.add_vertexn]!
\end{dubious} *)
module
PosMap
=
Partial
.
Make
(
struct
type
t
=
int
let
compare
=
compare
end
)
let
partial_map_undoing_permutation
l
l'
=
let
module
P
=
Permutation
.
Default
in
let
p
=
P
.
of_list
(
List
.
map
pred
l'
)
in
PosMap
.
of_lists
l
(
P
.
list
p
l
)
let
partial_map_undoing_fuse
fuse
=
partial_map_undoing_permutation
(
ThoList
.
range
1
(
List
.
length
fuse
))
fuse
let
undo_permutation_of_fuse
fuse
=
PosMap
.
apply_with_fallback
(
fun
_
->
invalid_arg
"permutation_of_fuse"
)
(
partial_map_undoing_fuse
fuse
)
let
fermion_lines
=
function
|
Coupling
.
V3
_
|
Coupling
.
V4
_
->
None
|
Coupling
.
Vn
(
Coupling
.
UFO
(_,
_,
_,
fl
,
_),
fuse
,
_)
->
Some
(
UFO_Lorentz
.
map_fermion_lines
(
undo_permutation_of_fuse
fuse
)
fl
)
type
constant
=
M
.
constant
(* \thocwmodulesubsection{Wave Functions} *)
(* \begin{dubious}
The code below is not yet functional. Too often, we assign to
[Tags.null_wf] instead of calling [Tags.fuse].
\end{dubious} *)
(* We will need two types of amplitudes: with color and without color. Since
we can build them using the same types with only [flavor] replaced, it pays
to use a functor to set up the scaffolding. *)
module
Tags
=
Tagger
(
PT
)
(* In the future, we might want to have [Coupling] among the functor
arguments. However, for the moment, [Coupling] is assumed to be
comprehensive. *)
module
type
Tagged_Coupling
=
sig
type
sign
=
int
type
t
=
{
sign
:
sign
;
coupling
:
constant
Coupling
.
t
;
coupling_tag
:
Tags
.
coupling
}
val
sign
:
t
->
sign
val
coupling
:
t
->
constant
Coupling
.
t
val
coupling_tag
:
t
->
string
option
end
module
Tagged_Coupling
:
Tagged_Coupling
=
struct
type
sign
=
int
type
t
=
{
sign
:
sign
;
coupling
:
constant
Coupling
.
t
;
coupling_tag
:
Tags
.
coupling
}
let
sign
c
=
c
.
sign
let
coupling
c
=
c
.
coupling
let
coupling_tag_raw
c
=
c
.
coupling_tag
let
coupling_tag
rhs
=
Tags
.
coupling_to_string
(
coupling_tag_raw
rhs
)
end
(* \thocwmodulesubsection{Amplitudes: Monochrome and Colored} *)
module
type
Amplitude
=
sig
module
Tags
:
Tags
type
flavor
type
p
type
wf
=
{
flavor
:
flavor
;
momentum
:
p
;
wf_tag
:
Tags
.
wf
}
val
flavor
:
wf
->
flavor
val
conjugate
:
wf
->
wf
val
momentum
:
wf
->
p
val
momentum_list
:
wf
->
int
list
val
wf_tag
:
wf
->
string
option
val
wf_tag_raw
:
wf
->
Tags
.
wf
val
order_wf
:
wf
->
wf
->
int
val
external_wfs
:
int
->
(
flavor
*
int
)
list
->
wf
list
type
'
a
children
type
coupling
=
Tagged_Coupling
.
t
type
rhs
=
coupling
*
wf
children
val
sign
:
rhs
->
int
val
coupling
:
rhs
->
constant
Coupling
.
t
val
coupling_tag
:
rhs
->
string
option
type
exclusions
val
no_exclusions
:
exclusions
val
children
:
rhs
->
wf
list
type
fusion
=
wf
*
rhs
list
val
lhs
:
fusion
->
wf
val
rhs
:
fusion
->
rhs
list
type
braket
=
wf
*
rhs
list
val
bra
:
braket
->
wf
val
ket
:
braket
->
rhs
list
module
D
:
DAG
.
T
with
type
node
=
wf
and
type
edge
=
coupling
and
type
children
=
wf
children
val
wavefunctions
:
braket
list
->
wf
list
type
amplitude
=
{
fusions
:
fusion
list
;
brakets
:
braket
list
;
on_shell
:
(
wf
->
bool
);
is_gauss
:
(
wf
->
bool
);
constraints
:
string
option
;
incoming
:
flavor
list
;
outgoing
:
flavor
list
;
externals
:
wf
list
;
symmetry
:
int
;
dependencies
:
(
wf
->
(
wf
,
coupling
)
Tree2
.
t
);
fusion_tower
:
D
.
t
;
fusion_dag
:
D
.
t
}
val
incoming
:
amplitude
->
flavor
list
val
outgoing
:
amplitude
->
flavor
list
val
externals
:
amplitude
->
wf
list
val
variables
:
amplitude
->
wf
list
val
fusions
:
amplitude
->
fusion
list
val
brakets
:
amplitude
->
braket
list
val
on_shell
:
amplitude
->
(
wf
->
bool
)
val
is_gauss
:
amplitude
->
(
wf
->
bool
)
val
constraints
:
amplitude
->
string
option
val
symmetry
:
amplitude
->
int
val
dependencies
:
amplitude
->
wf
->
(
wf
,
coupling
)
Tree2
.
t
val
fusion_dag
:
amplitude
->
D
.
t
end
module
Amplitude
(
PT
:
Tuple
.
Poly
)
(
P
:
Momentum
.
T
)
(
M
:
Model
.
T
)
:
Amplitude
with
type
p
=
P
.
t
and
type
flavor
=
M
.
flavor
and
type
'
a
children
=
'
a
PT
.
t
and
module
Tags
=
Tags
=
struct
type
flavor
=
M
.
flavor
type
p
=
P
.
t
module
Tags
=
Tags
type
wf
=
{
flavor
:
flavor
;
momentum
:
p
;
wf_tag
:
Tags
.
wf
}
let
flavor
wf
=
wf
.
flavor
let
conjugate
wf
=
{
wf
with
flavor
=
M
.
conjugate
wf
.
flavor
}
let
momentum
wf
=
wf
.
momentum
let
momentum_list
wf
=
P
.
to_ints
wf
.
momentum
let
wf_tag
wf
=
Tags
.
wf_to_string
wf
.
wf_tag
let
wf_tag_raw
wf
=
wf
.
wf_tag
let
external_wfs
rank
particles
=
List
.
map
(
fun
(
f
,
p
)
->
{
flavor
=
f
;
momentum
=
P
.
singleton
rank
p
;
wf_tag
=
Tags
.
null_wf
})
particles
(* Order wavefunctions so that the external come first, then the pairs, etc.
Also put possible Goldstone bosons \emph{before} their gauge bosons. *)
let
lorentz_ordering
f
=
match
M
.
lorentz
f
with
|
Coupling
.
Scalar
->
0
|
Coupling
.
Spinor
->
1
|
Coupling
.
ConjSpinor
->
2
|
Coupling
.
Majorana
->
3
|
Coupling
.
Vector
->
4
|
Coupling
.
Massive_Vector
->
5
|
Coupling
.
Tensor_2
->
6
|
Coupling
.
Tensor_1
->
7
|
Coupling
.
Vectorspinor
->
8
|
Coupling
.
BRS
Coupling
.
Scalar
->
9
|
Coupling
.
BRS
Coupling
.
Spinor
->
10
|
Coupling
.
BRS
Coupling
.
ConjSpinor
->
11
|
Coupling
.
BRS
Coupling
.
Majorana
->
12
|
Coupling
.
BRS
Coupling
.
Vector
->
13
|
Coupling
.
BRS
Coupling
.
Massive_Vector
->
14
|
Coupling
.
BRS
Coupling
.
Tensor_2
->
15
|
Coupling
.
BRS
Coupling
.
Tensor_1
->
16
|
Coupling
.
BRS
Coupling
.
Vectorspinor
->
17
|
Coupling
.
BRS
_
->
invalid_arg
"Fusion.lorentz_ordering: not needed"
|
Coupling
.
Maj_Ghost
->
18
(*i | Coupling.Ward_Vector -> 19 i*)
let
order_flavor
f1
f2
=
let
c
=
compare
(
lorentz_ordering
f1
)
(
lorentz_ordering
f2
)
in
if
c
<>
0
then
c
else
compare
f1
f2
(* Note that [Momentum().compare] guarantees that wavefunctions will be
ordered according to \emph{increasing} [Momentum().rank] of their
momenta. *)
let
order_wf
wf1
wf2
=
let
c
=
P
.
compare
wf1
.
momentum
wf2
.
momentum
in
if
c
<>
0
then
c
else
let
c
=
order_flavor
wf1
.
flavor
wf2
.
flavor
in
if
c
<>
0
then
c
else
compare
wf1
.
wf_tag
wf2
.
wf_tag
(* This \emph{must} be a pair matching the [edge * node children] pairs of
[DAG.Forest]! *)
type
coupling
=
Tagged_Coupling
.
t
type
'
a
children
=
'
a
PT
.
t
type
rhs
=
coupling
*
wf
children
let
sign
(
c
,
_)
=
Tagged_Coupling
.
sign
c
let
coupling
(
c
,
_)
=
Tagged_Coupling
.
coupling
c
let
coupling_tag
(
c
,
_)
=
Tagged_Coupling
.
coupling_tag
c
type
exclusions
=
{
x_flavors
:
flavor
list
;
x_couplings
:
coupling
list
}
let
no_exclusions
=
{
x_flavors
=
[]
;
x_couplings
=
[]
}
let
children
(_,
wfs
)
=
PT
.
to_list
wfs
type
fusion
=
wf
*
rhs
list
let
lhs
(
l
,
_)
=
l
let
rhs
(_,
r
)
=
r
type
braket
=
wf
*
rhs
list
let
bra
(
b
,
_)
=
b
let
ket
(_,
k
)
=
k
module
D
=
DAG
.
Make
(
DAG
.
Forest
(
PT
)
(
struct
type
t
=
wf
let
compare
=
order_wf
end
)
(
struct
type
t
=
coupling
let
compare
=
compare
end
))
module
WFSet
=
Set
.
Make
(
struct
type
t
=
wf
let
compare
=
order_wf
end
)
let
wavefunctions
brakets
=
WFSet
.
elements
(
List
.
fold_left
(
fun
set
(
wf1
,
wf23
)
->
WFSet
.
add
wf1
(
List
.
fold_left
(
fun
set'
(_,
wfs
)
->
PT
.
fold_right
WFSet
.
add
wfs
set'
)
set
wf23
))
WFSet
.
empty
brakets
)
type
amplitude
=
{
fusions
:
fusion
list
;
brakets
:
braket
list
;
on_shell
:
(
wf
->
bool
);
is_gauss
:
(
wf
->
bool
);
constraints
:
string
option
;
incoming
:
flavor
list
;
outgoing
:
flavor
list
;
externals
:
wf
list
;
symmetry
:
int
;
dependencies
:
(
wf
->
(
wf
,
coupling
)
Tree2
.
t
);
fusion_tower
:
D
.
t
;
fusion_dag
:
D
.
t
}
let
incoming
a
=
a
.
incoming
let
outgoing
a
=
a
.
outgoing
let
externals
a
=
a
.
externals
let
fusions
a
=
a
.
fusions
let
brakets
a
=
a
.
brakets
let
symmetry
a
=
a
.
symmetry
let
on_shell
a
=
a
.
on_shell
let
is_gauss
a
=
a
.
is_gauss
let
constraints
a
=
a
.
constraints
let
variables
a
=
List
.
map
lhs
a
.
fusions
let
dependencies
a
=
a
.
dependencies
let
fusion_dag
a
=
a
.
fusion_dag
end
module
A
=
Amplitude
(
PT
)(
P
)(
M
)
(* Operator insertions can be fused only if they are external. *)
let
is_source
wf
=
match
M
.
propagator
wf
.
A
.
flavor
with
|
Only_Insertion
->
P
.
rank
wf
.
A
.
momentum
=
1
|
_
->
true
(* [is_goldstone_of g v] is [true] if and only if [g] is the Goldstone boson
corresponding to the gauge particle [v]. *)
let
is_goldstone_of
g
v
=
match
M
.
goldstone
v
with
|
None
->
false
|
Some
(
g'
,
_)
->
g
=
g'
(* \begin{dubious}
In the end, [PT.to_list] should become redudant!
\end{dubious} *)
let
fuse_rhs
rhs
=
M
.
fuse
(
PT
.
to_list
rhs
)
(* \thocwmodulesubsection{Vertices} *)
(* Compute the set of all vertices in the model from the allowed
fusions and the set of all flavors:
\begin{dubious}
One could think of using [M.vertices] instead of [M.fuse2],
[M.fuse3] and [M.fuse] \ldots
\end{dubious} *)
module
VSet
=
Map
.
Make
(
struct
type
t
=
A
.
flavor
let
compare
=
compare
end
)
let
add_vertices
f
rhs
m
=
VSet
.
add
f
(
try
rhs
::
VSet
.
find
f
m
with
Not_found
->
[
rhs
])
m
let
collect_vertices
rhs
=
List
.
fold_right
(
fun
(
f1
,
c
)
->
add_vertices
(
M
.
conjugate
f1
)
(
c
,
rhs
))
(
fuse_rhs
rhs
)
(* The set of all vertices with common left fields factored. *)
(* I used to think that constant initializers are a good idea to allow
compile time optimizations. The down side turned out to be that the
constant initializers will be evaluated \emph{every time} the functor
is applied. \emph{Relying on the fact that the functor will be
called only once is not a good idea!} *)
type
vertices
=
(
A
.
flavor
*
(
constant
Coupling
.
t
*
A
.
flavor
PT
.
t
)
list
)
list
(* \begin{dubious}
This is \emph{very} inefficient for [max_degree > 6]. Find a better
approach that avoids precomputing the huge lookup table!
\end{dubious}
\begin{dubious}
I should revive the above Idea to use [M.vertices] instead directly,
instead of rebuilding it from [M.fuse2],
[M.fuse3] and [M.fuse]!
\end{dubious} *)
let
vertices_nocache
max_degree
flavors
:
vertices
=
VSet
.
fold
(
fun
f
rhs
v
->
(
f
,
rhs
)
::
v
)
(
PT
.
power_fold
~
truncate
:(
pred
max_degree
)
collect_vertices
flavors
VSet
.
empty
)
[]
(* Performance hack: *)
type
vertex_table
=
((
A
.
flavor
*
A
.
flavor
*
A
.
flavor
)
*
constant
Coupling
.
vertex3
*
constant
)
list
*
((
A
.
flavor
*
A
.
flavor
*
A
.
flavor
*
A
.
flavor
)
*
constant
Coupling
.
vertex4
*
constant
)
list
*
(
A
.
flavor
list
*
constant
Coupling
.
vertexn
*
constant
)
list
(*i
module VCache =
Cache.Make (struct type t = vertex_table end) (struct type t = vertices end)
let vertices_cache = ref None
let hash () = VCache.hash (M.vertices ())
(* \begin{dubious}
Can we do better than the executable name provided by [Config.cache_prefix]???
We need a better way to avoid collisions among the caches for different models
in the same program.
\end{dubious} *)
let cache_name =
ref (Config.cache_prefix ^ "." ^ Config.cache_suffix)
let set_cache_name name =
cache_name := name
let initialize_cache dir =
Printf.eprintf
" >>> Initializing vertex table %s. This may take some time ... "
!cache_name;
flush stderr;
VCache.write_dir (hash ()) dir !cache_name
(vertices_nocache (M.max_degree ()) (M.flavors()));
Printf.eprintf "done. <<< \n"
let vertices max_degree flavors : vertices =
match !vertices_cache with
| None ->
begin match !cache_option with
| Cache_Use ->
begin match VCache.maybe_read (hash ()) !cache_name with
| VCache.Hit result -> result
| VCache.Miss ->
Printf.eprintf
" >>> Initializing vertex table %s. This may take some time ... "
!cache_name;
flush stderr;
let result = vertices_nocache max_degree flavors in
VCache.write (hash ()) !cache_name (result);
vertices_cache := Some result;
Printf.eprintf "done. <<< \n";
flush stderr;
result
| VCache.Stale file ->
Printf.eprintf
" >>> Re-initializing stale vertex table %s in file %s. "
!cache_name file;
Printf.eprintf "This may take some time ... ";
flush stderr;
let result = vertices_nocache max_degree flavors in
VCache.write (hash ()) !cache_name (result);
vertices_cache := Some result;
Printf.eprintf "done. <<< \n";
flush stderr;
result
end
| Cache_Overwrite ->
Printf.eprintf
" >>> Overwriting vertex table %s. This may take some time ... "
!cache_name;
flush stderr;
let result = vertices_nocache max_degree flavors in
VCache.write (hash ()) !cache_name (result);
vertices_cache := Some result;
Printf.eprintf "done. <<< \n";
flush stderr;
result
| Cache_Ignore ->
let result = vertices_nocache max_degree flavors in
vertices_cache := Some result;
result
end
| Some result -> result
i*)
let
vertices
=
vertices_nocache
let
vertices'
max_degree
flavors
=
Printf
.
eprintf
">>> vertices %d ..."
max_degree
;
flush
stderr
;
let
v
=
vertices
max_degree
flavors
in
Printf
.
eprintf
" done.
\n
"
;
flush
stderr
;
v
(* Note that we must perform any filtering of the vertices \emph{after}
caching, because the restrictions \emph{must not} influence the
cache (unless we tag the cache with model and restrictions). *)
(*i
let unpack_constant = function
| Coupling.V3 (_, _, cs) -> cs
| Coupling.V4 (_, _, cs) -> cs
| Coupling.Vn (_, _, cs) -> cs
let coupling_and_flavors_to_string (c, fs) =
M.constant_symbol (unpack_constant c) ^ "[" ^
String.concat ", " (List.map M.flavor_to_string (PT.to_list fs)) ^ "]"
let fusions_to_string (f, cfs) =
M.flavor_to_string f ^ " <- { " ^
String.concat " | " (List.map coupling_and_flavors_to_string cfs) ^
" }"
let vertices_to_string vertices =
String.concat "; " (List.map fusions_to_string vertices)
i*)
let
filter_vertices
select_vtx
vertices
=
List
.
fold_left
(
fun
acc
(
f
,
cfs
)
->
let
f'
=
M
.
conjugate
f
in
let
cfs
=
List
.
filter
(
fun
(
c
,
fs
)
->
select_vtx
c
f'
(
PT
.
to_list
fs
))
cfs
in
match
cfs
with
|
[]
->
acc
|
cfs
->
(
f
,
cfs
)
::
acc
)
[]
vertices
(* \thocwmodulesubsection{Partitions} *)
(* Vertices that are not crossing invariant need special treatment so
that they're only generated for the correct combinations of momenta.
NB: the [crossing] checks here are a bit redundant, because [CM.fuse] below
will bring the killed vertices back to life and will have to filter once more.
Nevertheless, we keep them here, for the unlikely case that anybody ever wants
to use uncolored amplitudes directly.
NB: the analogous problem does not occur for [select_wf], because this applies
to momenta instead of vertices. *)
(* \begin{dubious}
This approach worked before the colorize, but has become \emph{futile},
because [CM.fuse] will bring the killed vertices back to life. We need
to implement the same checks there again!!!
\end{dubious} *)
(* \begin{dubious}
Using [PT.Mismatched_arity] is not really good style \ldots
Tho's approach doesn't work since he does not catch charge conjugated processes or
crossed processes. Another very strange thing is that O'Mega seems always to run in the
q2 q3 timelike case, but not in the other two. (Property of how the DAG is built?).
For the $ZZZZ$ vertex I add the same vertex again, but interchange 1 and 3 in the
[crossing] vertex
\end{dubious} *)
let
kmatrix_cuts
c
momenta
=
match
c
with
|
V4
(
Vector4_K_Matrix_tho
(
disc
,
_),
fusion
,
_)
|
V4
(
Vector4_K_Matrix_jr
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
)
|
1
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
)
|
1
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
)
->
true
|
2
,
true
,
false
,
false
,
(
F123
|
F213
|
F124
|
F214
)
|
2
,
false
,
true
,
false
,
(
F312
|
F321
|
F412
|
F421
)
|
2
,
false
,
false
,
true
,
(
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
_
->
false
end
|
V4
(
Vector4_K_Matrix_cf_t0
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
)
|
1
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
)
|
1
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
)
->
true
|
2
,
true
,
false
,
false
,
(
F123
|
F213
|
F124
|
F214
)
|
2
,
false
,
true
,
false
,
(
F312
|
F321
|
F412
|
F421
)
|
2
,
false
,
false
,
true
,
(
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
_
->
false
end
|
V4
(
Vector4_K_Matrix_cf_t1
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
)
|
1
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
)
|
1
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
)
->
true
|
2
,
true
,
false
,
false
,
(
F123
|
F213
|
F124
|
F214
)
|
2
,
false
,
true
,
false
,
(
F312
|
F321
|
F412
|
F421
)
|
2
,
false
,
false
,
true
,
(
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
_
->
false
end
|
V4
(
Vector4_K_Matrix_cf_t2
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
)
|
1
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
)
|
1
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
)
->
true
|
2
,
true
,
false
,
false
,
(
F123
|
F213
|
F124
|
F214
)
|
2
,
false
,
true
,
false
,
(
F312
|
F321
|
F412
|
F421
)
|
2
,
false
,
false
,
true
,
(
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
_
->
false
end
|
V4
(
Vector4_K_Matrix_cf_t_rsi
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
)
|
1
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
)
|
1
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
)
->
true
|
2
,
true
,
false
,
false
,
(
F123
|
F213
|
F124
|
F214
)
|
2
,
false
,
true
,
false
,
(
F312
|
F321
|
F412
|
F421
)
|
2
,
false
,
false
,
true
,
(
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
_
->
false
end
|
V4
(
Vector4_K_Matrix_cf_m0
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
)
|
1
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
)
|
1
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
)
->
true
|
2
,
true
,
false
,
false
,
(
F123
|
F213
|
F124
|
F214
)
|
2
,
false
,
true
,
false
,
(
F312
|
F321
|
F412
|
F421
)
|
2
,
false
,
false
,
true
,
(
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
_
->
false
end
|
V4
(
Vector4_K_Matrix_cf_m1
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
)
|
1
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
)
|
1
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
)
->
true
|
2
,
true
,
false
,
false
,
(
F123
|
F213
|
F124
|
F214
)
|
2
,
false
,
true
,
false
,
(
F312
|
F321
|
F412
|
F421
)
|
2
,
false
,
false
,
true
,
(
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
_
->
false
end
|
V4
(
Vector4_K_Matrix_cf_m7
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
)
|
1
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
)
|
1
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
)
->
true
|
2
,
true
,
false
,
false
,
(
F123
|
F213
|
F124
|
F214
)
|
2
,
false
,
true
,
false
,
(
F312
|
F321
|
F412
|
F421
)
|
2
,
false
,
false
,
true
,
(
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
_
->
false
end
|
V4
(
DScalar2_Vector2_K_Matrix_ms
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F432
|
F123
|
F214
)
|
1
,
false
,
true
,
false
,
(
F134
|
F243
|
F312
|
F421
)
|
1
,
false
,
false
,
true
,
(
F314
|
F423
|
F132
|
F241
)
->
true
|
2
,
true
,
false
,
false
,
(
F431
|
F342
|
F213
|
F124
)
|
2
,
false
,
true
,
false
,
(
F143
|
F234
|
F321
|
F412
)
|
2
,
false
,
false
,
true
,
(
F413
|
F324
|
F231
|
F142
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
4
,
true
,
false
,
false
,
(
F142
|
F413
|
F231
|
F324
)
|
4
,
false
,
true
,
false
,
(
F214
|
F341
|
F123
|
F432
)
|
4
,
false
,
false
,
true
,
(
F124
|
F431
|
F213
|
F342
)
->
true
|
5
,
true
,
false
,
false
,
(
F143
|
F412
|
F321
|
F234
)
|
5
,
false
,
true
,
false
,
(
F314
|
F241
|
F132
|
F423
)
|
5
,
false
,
false
,
true
,
(
F134
|
F421
|
F312
|
F243
)
->
true
|
6
,
true
,
false
,
false
,
(
F134
|
F132
|
F314
|
F312
|
F241
|
F243
|
F421
|
F423
)
|
6
,
false
,
true
,
false
,
(
F213
|
F413
|
F231
|
F431
|
F124
|
F324
|
F142
|
F342
)
|
6
,
false
,
false
,
true
,
(
F143
|
F123
|
F341
|
F321
|
F412
|
F214
|
F432
|
F234
)
->
true
|
7
,
true
,
false
,
false
,
(
F134
|
F312
|
F421
|
F243
)
|
7
,
false
,
true
,
false
,
(
F413
|
F231
|
F142
|
F324
)
|
7
,
false
,
false
,
true
,
(
F143
|
F321
|
F412
|
F432
)
->
true
|
8
,
true
,
false
,
false
,
(
F132
|
F314
|
F241
|
F423
)
|
8
,
false
,
true
,
false
,
(
F213
|
F431
|
F124
|
F342
)
|
8
,
false
,
false
,
true
,
(
F123
|
F341
|
F214
|
F234
)
->
true
|
_
->
false
end
|
V4
(
DScalar2_Vector2_m_0_K_Matrix_cf
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F432
|
F123
|
F214
)
|
1
,
false
,
true
,
false
,
(
F134
|
F243
|
F312
|
F421
)
|
1
,
false
,
false
,
true
,
(
F314
|
F423
|
F132
|
F241
)
->
true
|
2
,
true
,
false
,
false
,
(
F431
|
F342
|
F213
|
F124
)
|
2
,
false
,
true
,
false
,
(
F143
|
F234
|
F321
|
F412
)
|
2
,
false
,
false
,
true
,
(
F413
|
F324
|
F231
|
F142
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
4
,
true
,
false
,
false
,
(
F142
|
F413
|
F231
|
F324
)
|
4
,
false
,
true
,
false
,
(
F214
|
F341
|
F123
|
F432
)
|
4
,
false
,
false
,
true
,
(
F124
|
F431
|
F213
|
F342
)
->
true
|
5
,
true
,
false
,
false
,
(
F143
|
F412
|
F321
|
F234
)
|
5
,
false
,
true
,
false
,
(
F314
|
F241
|
F132
|
F423
)
|
5
,
false
,
false
,
true
,
(
F134
|
F421
|
F312
|
F243
)
->
true
|
6
,
true
,
false
,
false
,
(
F134
|
F132
|
F314
|
F312
|
F241
|
F243
|
F421
|
F423
)
|
6
,
false
,
true
,
false
,
(
F213
|
F413
|
F231
|
F431
|
F124
|
F324
|
F142
|
F342
)
|
6
,
false
,
false
,
true
,
(
F143
|
F123
|
F341
|
F321
|
F412
|
F214
|
F432
|
F234
)
->
true
|
7
,
true
,
false
,
false
,
(
F134
|
F312
|
F421
|
F243
)
|
7
,
false
,
true
,
false
,
(
F413
|
F231
|
F142
|
F324
)
|
7
,
false
,
false
,
true
,
(
F143
|
F321
|
F412
|
F432
)
->
true
|
8
,
true
,
false
,
false
,
(
F132
|
F314
|
F241
|
F423
)
|
8
,
false
,
true
,
false
,
(
F213
|
F431
|
F124
|
F342
)
|
8
,
false
,
false
,
true
,
(
F123
|
F341
|
F214
|
F234
)
->
true
|
_
->
false
end
|
V4
(
DScalar2_Vector2_m_1_K_Matrix_cf
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F432
|
F123
|
F214
)
|
1
,
false
,
true
,
false
,
(
F134
|
F243
|
F312
|
F421
)
|
1
,
false
,
false
,
true
,
(
F314
|
F423
|
F132
|
F241
)
->
true
|
2
,
true
,
false
,
false
,
(
F431
|
F342
|
F213
|
F124
)
|
2
,
false
,
true
,
false
,
(
F143
|
F234
|
F321
|
F412
)
|
2
,
false
,
false
,
true
,
(
F413
|
F324
|
F231
|
F142
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
4
,
true
,
false
,
false
,
(
F142
|
F413
|
F231
|
F324
)
|
4
,
false
,
true
,
false
,
(
F214
|
F341
|
F123
|
F432
)
|
4
,
false
,
false
,
true
,
(
F124
|
F431
|
F213
|
F342
)
->
true
|
5
,
true
,
false
,
false
,
(
F143
|
F412
|
F321
|
F234
)
|
5
,
false
,
true
,
false
,
(
F314
|
F241
|
F132
|
F423
)
|
5
,
false
,
false
,
true
,
(
F134
|
F421
|
F312
|
F243
)
->
true
|
6
,
true
,
false
,
false
,
(
F134
|
F132
|
F314
|
F312
|
F241
|
F243
|
F421
|
F423
)
|
6
,
false
,
true
,
false
,
(
F213
|
F413
|
F231
|
F431
|
F124
|
F324
|
F142
|
F342
)
|
6
,
false
,
false
,
true
,
(
F143
|
F123
|
F341
|
F321
|
F412
|
F214
|
F432
|
F234
)
->
true
|
7
,
true
,
false
,
false
,
(
F134
|
F312
|
F421
|
F243
)
|
7
,
false
,
true
,
false
,
(
F413
|
F231
|
F142
|
F324
)
|
7
,
false
,
false
,
true
,
(
F143
|
F321
|
F412
|
F432
)
->
true
|
8
,
true
,
false
,
false
,
(
F132
|
F314
|
F241
|
F423
)
|
8
,
false
,
true
,
false
,
(
F213
|
F431
|
F124
|
F342
)
|
8
,
false
,
false
,
true
,
(
F123
|
F341
|
F214
|
F234
)
->
true
|
_
->
false
end
|
V4
(
DScalar2_Vector2_m_7_K_Matrix_cf
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
1
,
true
,
false
,
false
,
(
F341
|
F432
|
F123
|
F214
)
|
1
,
false
,
true
,
false
,
(
F134
|
F243
|
F312
|
F421
)
|
1
,
false
,
false
,
true
,
(
F314
|
F423
|
F132
|
F241
)
->
true
|
2
,
true
,
false
,
false
,
(
F431
|
F342
|
F213
|
F124
)
|
2
,
false
,
true
,
false
,
(
F143
|
F234
|
F321
|
F412
)
|
2
,
false
,
false
,
true
,
(
F413
|
F324
|
F231
|
F142
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
4
,
true
,
false
,
false
,
(
F142
|
F413
|
F231
|
F324
)
|
4
,
false
,
true
,
false
,
(
F214
|
F341
|
F123
|
F432
)
|
4
,
false
,
false
,
true
,
(
F124
|
F431
|
F213
|
F342
)
->
true
|
5
,
true
,
false
,
false
,
(
F143
|
F412
|
F321
|
F234
)
|
5
,
false
,
true
,
false
,
(
F314
|
F241
|
F132
|
F423
)
|
5
,
false
,
false
,
true
,
(
F134
|
F421
|
F312
|
F243
)
->
true
|
6
,
true
,
false
,
false
,
(
F134
|
F132
|
F314
|
F312
|
F241
|
F243
|
F421
|
F423
)
|
6
,
false
,
true
,
false
,
(
F213
|
F413
|
F231
|
F431
|
F124
|
F324
|
F142
|
F342
)
|
6
,
false
,
false
,
true
,
(
F143
|
F123
|
F341
|
F321
|
F412
|
F214
|
F432
|
F234
)
->
true
|
7
,
true
,
false
,
false
,
(
F134
|
F312
|
F421
|
F243
)
|
7
,
false
,
true
,
false
,
(
F413
|
F231
|
F142
|
F324
)
|
7
,
false
,
false
,
true
,
(
F143
|
F321
|
F412
|
F432
)
->
true
|
8
,
true
,
false
,
false
,
(
F132
|
F314
|
F241
|
F423
)
|
8
,
false
,
true
,
false
,
(
F213
|
F431
|
F124
|
F342
)
|
8
,
false
,
false
,
true
,
(
F123
|
F341
|
F214
|
F234
)
->
true
|
_
->
false
end
|
V4
(
DScalar4_K_Matrix_ms
(
disc
,
_),
fusion
,
_)
->
let
s12
,
s23
,
s13
=
begin
match
PT
.
to_list
momenta
with
|
[
q1
;
q2
;
q3
]
->
(
P
.
Scattering
.
timelike
(
P
.
add
q1
q2
),
P
.
Scattering
.
timelike
(
P
.
add
q2
q3
),
P
.
Scattering
.
timelike
(
P
.
add
q1
q3
))
|
_
->
raise
PT
.
Mismatched_arity
end
in
begin
match
disc
,
s12
,
s23
,
s13
,
fusion
with
|
0
,
true
,
false
,
false
,
(
F341
|
F431
|
F342
|
F432
|
F123
|
F213
|
F124
|
F214
)
|
0
,
false
,
true
,
false
,
(
F134
|
F143
|
F234
|
F243
|
F312
|
F321
|
F412
|
F421
)
|
0
,
false
,
false
,
true
,
(
F314
|
F413
|
F324
|
F423
|
F132
|
F231
|
F142
|
F241
)
->
true
|
3
,
true
,
false
,
false
,
(
F143
|
F413
|
F142
|
F412
|
F321
|
F231
|
F324
|
F234
)
|
3
,
false
,
true
,
false
,
(
F314
|
F341
|
F214
|
F241
|
F132
|
F123
|
F432
|
F423
)
|
3
,
false
,
false
,
true
,
(
F134
|
F431
|
F124
|
F421
|
F312
|
F213
|
F342
|
F243
)
->
true
|
4
,
true
,
false
,
false
,
(
F142
|
F413
|
F231
|
F324
)
|
4
,
false
,
true
,
false
,
(
F214
|
F341
|
F123
|
F432
)
|
4
,
false
,
false
,
true
,
(
F124
|
F431
|
F213
|
F342
)
->
true
|
5
,
true
,
false
,
false
,
(
F143
|
F412
|
F321
|
F234
)
|
5
,
false
,
true
,
false
,
(
F314
|
F241
|
F132
|
F423
)
|
5
,
false
,
false
,
true
,
(
F134
|
F421
|
F312
|
F243
)
->
true
|
6
,
true
,
false
,
false
,
(
F134
|
F132
|
F314
|
F312
|
F241
|
F243
|
F421
|
F423
)
|
6
,
false
,
true
,
false
,
(
F213
|
F413
|
F231
|
F431
|
F124
|
F324
|
F142
|
F342
)
|
6
,
false
,
false
,
true
,
(
F143
|
F123
|
F341
|
F321
|
F412
|
F214
|
F432
|
F234
)
->
true
|
7
,
true
,
false
,
false
,
(
F134
|
F312
|
F421
|
F243
)
|
7
,
false
,
true
,
false
,
(
F413
|
F231
|
F142
|
F324
)
|
7
,
false
,
false
,
true
,
(
F143
|
F321
|
F412
|
F432
)
->
true
|
8
,
true
,
false
,
false
,
(
F132
|
F314
|
F241
|
F423
)
|
8
,
false
,
true
,
false
,
(
F213
|
F431
|
F124
|
F342
)
|
8
,
false
,
false
,
true
,
(
F123
|
F341
|
F214
|
F234
)
->
true
|
_
->
false
end
|
_
->
true
(* Counting QCD and EW orders. *)
let
qcd_ew_check
orders
=
if
fst
(
orders
)
<=
fst
(
int_orders
)
&&
snd
(
orders
)
<=
snd
(
int_orders
)
then
true
else
false
(* Match a set of flavors to a set of momenta. Form the direct product for
the lists of momenta two and three with the list of couplings and flavors
two and three. *)
let
flavor_keystone
select_p
dim
(
f1
,
f23
)
(
p1
,
p23
)
=
({
A
.
flavor
=
f1
;
A
.
momentum
=
P
.
of_ints
dim
p1
;
A
.
wf_tag
=
A
.
Tags
.
null_wf
},
Product
.
fold2
(
fun
(
c
,
f
)
p
acc
->
try
let
p'
=
PT
.
map
(
P
.
of_ints
dim
)
p
in
if
select_p
(
P
.
of_ints
dim
p1
)
(
PT
.
to_list
p'
)
&&
kmatrix_cuts
c
p'
then
(
c
,
PT
.
map2
(
fun
f''
p''
->
{
A
.
flavor
=
f''
;
A
.
momentum
=
p''
;
A
.
wf_tag
=
A
.
Tags
.
null_wf
})
f
p'
)
::
acc
else
acc
with
|
PT
.
Mismatched_arity
->
acc
)
f23
p23
[]
)
(*i
let cnt = ref 0
let gc_stat () =
let minor, promoted, major = Gc.counters () in
Printf.sprintf "(%12.0f, %12.0f, %12.0f)" minor promoted major
let flavor_keystone select_p n (f1, f23) (p1, p23) =
incr cnt;
Gc.set { (Gc.get()) with Gc.space_overhead = 20 };
Printf.eprintf "%6d@%8.1f: %s\n" !cnt (Sys.time ()) (gc_stat ());
flush stderr;
flavor_keystone select_p n (f1, f23) (p1, p23)
i*)
(* Produce all possible combinations of vertices (flavor keystones)
and momenta by forming the direct product. The semantically equivalent
[Product.list2 (flavor_keystone select_wf n) vertices keystones] with
\emph{subsequent} filtering would be a \emph{very bad} idea, because
a potentially huge intermediate list is built for large models.
E.\,g.~for the MSSM this would lead to non-termination by thrashing
for $2\to4$ processes on most PCs. *)
let
flavor_keystones
filter
select_p
dim
vertices
keystones
=
Product
.
fold2
(
fun
v
k
acc
->
filter
(
flavor_keystone
select_p
dim
v
k
)
acc
)
vertices
keystones
[]
(* Flatten the nested lists of vertices into a list of attached lines. *)
let
flatten_keystones
t
=
ThoList
.
flatmap
(
fun
(
p1
,
p23
)
->
p1
::
(
ThoList
.
flatmap
(
fun
(_,
rhs
)
->
PT
.
to_list
rhs
)
p23
))
t
(* \thocwmodulesubsection{Subtrees} *)
(* Fuse a tuple of wavefunctions, keeping track of Fermi statistics.
Record only the the sign \emph{relative} to the children.
(The type annotation is only for documentation.) *)
let
fuse
select_wf
select_vtx
wfss
:
(
A
.
wf
*
stat
*
A
.
rhs
)
list
=
if
PT
.
for_all
(
fun
(
wf
,
_)
->
is_source
wf
)
wfss
then
try
let
wfs
,
ss
=
PT
.
split
wfss
in
let
flavors
=
PT
.
map
A
.
flavor
wfs
and
momenta
=
PT
.
map
A
.
momentum
wfs
(*i and wf_tags = PT.map A.wf_tag_raw wfs i*)
in
let
p
=
PT
.
fold_left_internal
P
.
add
momenta
in
(*i let wft = PT.fold_left Tags.fuse wf_tags in i*)
List
.
fold_left
(
fun
acc
(
f
,
c
)
->
if
select_wf
f
p
(
PT
.
to_list
momenta
)
&&
select_vtx
c
f
(
PT
.
to_list
flavors
)
&&
kmatrix_cuts
c
momenta
then
(* [let _ =
Printf.eprintf
"Fusion.fuse: %s <- %s\n"
(M.flavor_to_string f)
(ThoList.to_string M.flavor_to_string (PT.to_list flavors)) in] *)
let
s
=
S
.
stat_fuse
(
fermion_lines
c
)
(
PT
.
to_list
ss
)
f
in
let
flip
=
PT
.
fold_left
(
fun
acc
s'
->
acc
*
stat_sign
s'
)
(
stat_sign
s
)
ss
in
({
A
.
flavor
=
f
;
A
.
momentum
=
p
;
A
.
wf_tag
=
A
.
Tags
.
null_wf
},
s
,
({
Tagged_Coupling
.
sign
=
flip
;
Tagged_Coupling
.
coupling
=
c
;
Tagged_Coupling
.
coupling_tag
=
A
.
Tags
.
null_coupling
},
wfs
))
::
acc
else
acc
)
[]
(
fuse_rhs
flavors
)
with
|
P
.
Duplicate
_
|
S
.
Impossible
->
[]
else
[]
(* \begin{dubious}
Eventually, the pairs of [tower] and [dag] in [fusion_tower']
below could and should be replaced by a graded [DAG]. This will
look like, but currently [tower] containts statistics information
that is missing from [dag]:
\begin{quote}
\verb+Type node = flavor * p is not compatible with type wf * stat+
\end{quote}
This should be easy to fix. However, replacing [type t = wf]
with [type t = wf * stat] is \emph{not} a good idea because the variable
[stat] makes it impossible to test for the existance of a particular
[wf] in a [DAG].
\end{dubious}
\begin{dubious}
In summary, it seems that [(wf * stat) list array * A.D.t] should be
replaced by [(wf -> stat) * A.D.t].
\end{dubious} *)
module
GF
=
struct
module
Nodes
=
struct
type
t
=
A
.
wf
module
G
=
struct
type
t
=
int
let
compare
=
compare
end
let
compare
=
A
.
order_wf
let
rank
wf
=
P
.
rank
wf
.
A
.
momentum
end
module
Edges
=
struct
type
t
=
A
.
coupling
let
compare
=
compare
end
module
F
=
DAG
.
Forest
(
PT
)(
Nodes
)(
Edges
)
type
node
=
Nodes
.
t
type
edge
=
F
.
edge
type
children
=
F
.
children
type
t
=
F
.
t
let
compare
=
F
.
compare
let
for_all
=
F
.
for_all
let
fold
=
F
.
fold
end
module
D'
=
DAG
.
Graded
(
GF
)
let
tower_of_dag
dag
=
let
_,
max_rank
=
D'
.
min_max_rank
dag
in
Array
.
init
max_rank
(
fun
n
->
D'
.
ranked
n
dag
)
(* The function [fusion_tower']
recursively builds the tower of all fusions from bottom up to a chosen
level. The argument [tower] is an array of lists, where the $i$-th sublist
(counting from 0) represents all off shell wave functions depending on
$i+1$~momenta and their Fermistatistics.
\begin{equation}
\begin{aligned}
\Bigl\lbrack
& \{ \phi_1(p_1), \phi_2(p_2), \phi_3(p_3), \ldots \}, \\
& \{ \phi_{12}(p_1+p_2), \phi'_{12}(p_1+p_2), \ldots,
\phi_{13}(p_1+p_3), \ldots, \phi_{23}(p_2+p_3), \ldots \}, \\
& \ldots \\
& \{ \phi_{1\cdots n}(p_1+\cdots+p_n),
\phi'_{1\cdots n}(p_1+\cdots+p_n), \ldots \} \Bigr\rbrack
\end{aligned}
\end{equation}
The argument [dag] is a DAG representing all the fusions calculated so far.
NB: The outer array in [tower] is always very short, so we could also
have accessed a list with [List.nth]. Appending of new members at the
end brings no loss of performance. NB: the array is supposed to be
immutable. *)
(* The towers must be sorted so that the combinatorical functions can
make consistent selections.
\begin{dubious}
Intuitively, this seems to be correct. However, one could have
expected that no element appears twice and that this ordering is
not necessary \ldots
\end{dubious} *)
let
grow
select_wf
select_vtx
tower
=
let
rank
=
succ
(
Array
.
length
tower
)
in
List
.
sort
pcompare
(
PT
.
graded_sym_power_fold
rank
(
fun
wfs
acc
->
fuse
select_wf
select_vtx
wfs
@
acc
)
tower
[]
)
let
add_offspring
dag
(
wf
,
_,
rhs
)
=
A
.
D
.
add_offspring
wf
rhs
dag
let
filter_offspring
fusions
=
List
.
map
(
fun
(
wf
,
s
,
_)
->
(
wf
,
s
))
fusions
let
rec
fusion_tower'
n_max
select_wf
select_vtx
tower
dag
:
(
A
.
wf
*
stat
)
list
array
*
A
.
D
.
t
=
if
Array
.
length
tower
>=
n_max
then
(
tower
,
dag
)
else
let
tower'
=
grow
select_wf
select_vtx
tower
in
fusion_tower'
n_max
select_wf
select_vtx
(
Array
.
append
tower
[|
filter_offspring
tower'
|])
(
List
.
fold_left
add_offspring
dag
tower'
)
(* Discard the tower and return a map from wave functions to Fermistatistics
together with the DAG. *)
let
make_external_dag
wfs
=
List
.
fold_left
(
fun
m
(
wf
,
_)
->
A
.
D
.
add_node
wf
m
)
A
.
D
.
empty
wfs
let
mixed_fold_left
f
acc
lists
=
Array
.
fold_left
(
List
.
fold_left
f
)
acc
lists
module
Stat_Map
=
Map
.
Make
(
struct
type
t
=
A
.
wf
let
compare
=
A
.
order_wf
end
)
let
fusion_tower
height
select_wf
select_vtx
wfs
:
(
A
.
wf
->
stat
)
*
A
.
D
.
t
=
let
tower
,
dag
=
fusion_tower'
height
select_wf
select_vtx
[|
wfs
|]
(
make_external_dag
wfs
)
in
let
stats
=
mixed_fold_left
(
fun
m
(
wf
,
s
)
->
Stat_Map
.
add
wf
s
m
)
Stat_Map
.
empty
tower
in
((
fun
wf
->
Stat_Map
.
find
wf
stats
),
dag
)
(* Calculate the minimal tower of fusions that suffices for calculating
the amplitude. *)
let
minimal_fusion_tower
n
select_wf
select_vtx
wfs
:
(
A
.
wf
->
stat
)
*
A
.
D
.
t
=
fusion_tower
(
T
.
max_subtree
n
)
select_wf
select_vtx
wfs
(* Calculate the complete tower of fusions. It is much larger than required,
but it allows a complete set of gauge checks. *)
let
complete_fusion_tower
select_wf
select_vtx
wfs
:
(
A
.
wf
->
stat
)
*
A
.
D
.
t
=
fusion_tower
(
List
.
length
wfs
-
1
)
select_wf
select_vtx
wfs
(* \begin{dubious}
There is a natural product of two DAGs using [fuse]. Can this be
used in a replacement for [fusion_tower]? The hard part is to avoid
double counting, of course. A straight forward solution
could do a diagonal sum (in order to reject flipped offspring representing
the same fusion) and rely on the uniqueness in [DAG] otherwise.
However, this will (probably) slow down the procedure significanty,
because most fusions (including Fermi signs!) will be calculated before
being rejected by [DAG().add_offspring].
\end{dubious} *)
(* Add to [dag] all Goldstone bosons defined in [tower] that correspond
to gauge bosons in [dag]. This is only required for checking
Slavnov-Taylor identities in unitarity gauge. Currently, it is not used,
because we use the complete tower for gauge checking. *)
let
harvest_goldstones
tower
dag
=
A
.
D
.
fold_nodes
(
fun
wf
dag'
->
match
M
.
goldstone
wf
.
A
.
flavor
with
|
Some
(
g
,
_)
->
let
wf'
=
{
wf
with
A
.
flavor
=
g
}
in
if
A
.
D
.
is_node
wf'
tower
then
begin
A
.
D
.
harvest
tower
wf'
dag'
end
else
begin
dag'
end
|
None
->
dag'
)
dag
dag
(* Calculate the sign from Fermi statistics that is not already included
in the children. *)
let
strip_fermion_lines
=
function
|
(
Coupling
.
V3
_
|
Coupling
.
V4
_
as
v
)
->
v
|
Coupling
.
Vn
(
Coupling
.
UFO
(
c
,
l
,
s
,
fl
,
col
),
f
,
x
)
->
Coupling
.
Vn
(
Coupling
.
UFO
(
c
,
l
,
s
,
[]
,
col
),
f
,
x
)
let
num_fermion_lines_v3
=
function
|
FBF
_
|
PBP
_
|
BBB
_
|
GBG
_
->
1
|
_
->
0
let
num_fermion_lines
=
function
|
Coupling
.
Vn
(
Coupling
.
UFO
(
c
,
l
,
s
,
fl
,
col
),
f
,
x
)
->
List
.
length
fl
|
Coupling
.
V3
(
v3
,
_,
_)
->
num_fermion_lines_v3
v3
|
Coupling
.
V4
_
->
0
let
stat_keystone
v
stats
wf1
wfs
=
let
wf1'
=
stats
wf1
and
wfs'
=
PT
.
map
stats
wfs
in
let
f
=
A
.
flavor
wf1
in
let
slist
=
wf1'
::
PT
.
to_list
wfs'
in
let
stat
=
S
.
stat_keystone
(
fermion_lines
v
)
slist
f
in
(* We can compare with the legacy implementation only if there
are no fermion line ambiguities possible, i.\,e.~for
at most one line. *)
if
num_fermion_lines
v
<
2
then
begin
let
legacy
=
S
.
stat_keystone
None
slist
f
in
if
not
(
S
.
equal
stat
legacy
)
then
failwith
(
Printf
.
sprintf
"Fusion.stat_keystone: %s <> %s!"
(
S
.
stat_to_string
legacy
)
(
S
.
stat_to_string
stat
));
if
not
(
S
.
saturated
legacy
)
then
failwith
(
Printf
.
sprintf
"Fusion.stat_keystone: legacy incomplete: %s!"
(
S
.
stat_to_string
legacy
))
end
;
if
not
(
S
.
saturated
stat
)
then
failwith
(
Printf
.
sprintf
"Fusion.stat_keystone: incomplete: %s!"
(
S
.
stat_to_string
stat
));
stat_sign
stat
*
PT
.
fold_left
(
fun
acc
wf
->
acc
*
stat_sign
wf
)
(
stat_sign
wf1'
)
wfs'
let
stat_keystone_logging
v
stats
wf1
wfs
=
let
sign
=
stat_keystone
v
stats
wf1
wfs
in
Printf
.
eprintf
"Fusion.stat_keystone: %s * %s -> %d
\n
"
(
M
.
flavor_to_string
(
A
.
flavor
wf1
))
(
ThoList
.
to_string
(
fun
wf
->
M
.
flavor_to_string
(
A
.
flavor
wf
))
(
PT
.
to_list
wfs
))
sign
;
sign
(* Test all members of a list of wave functions are defined by the DAG
simultaneously: *)
let
test_rhs
dag
(_,
wfs
)
=
PT
.
for_all
(
fun
wf
->
is_source
wf
&&
A
.
D
.
is_node
wf
dag
)
wfs
(* Add the keystone [(wf1,pairs)] to [acc] only if it is present in [dag]
and calculate the statistical factor depending on [stats]
\emph{en passant}: *)
let
filter_keystone
stats
dag
(
wf1
,
pairs
)
acc
=
if
is_source
wf1
&&
A
.
D
.
is_node
wf1
dag
then
match
List
.
filter
(
test_rhs
dag
)
pairs
with
|
[]
->
acc
|
pairs'
->
(
wf1
,
List
.
map
(
fun
(
c
,
wfs
)
->
({
Tagged_Coupling
.
sign
=
stat_keystone
c
stats
wf1
wfs
;
Tagged_Coupling
.
coupling
=
c
;
Tagged_Coupling
.
coupling_tag
=
A
.
Tags
.
null_coupling
},
wfs
))
pairs'
)
::
acc
else
acc
(* \begin{figure}
\begin{center}
\thocwincludegraphics{width=\textwidth}{bhabha0}\\
\hfil\\
\thocwincludegraphics{width=\textwidth}{bhabha}
\end{center}
\caption{\label{fig:bhabha}
The DAGs for Bhabha scattering before and after weeding out unused
nodes. The blatant asymmetry of these DAGs is caused by our
prescription for removing doubling counting for an even number
of external lines.}
\end{figure}
\begin{figure}
\begin{center}
\thocwincludegraphics{width=\textwidth}{epemudbarmunumubar0}\\
\hfil\\
\thocwincludegraphics{width=\textwidth}{epemudbarmunumubar}
\end{center}
\caption{\label{fig:epemudbarmunumubar}
The DAGs for $e^+e^-\to u\bar d \mu^-\bar\nu_\mu$ before and after
weeding out unused nodes.}
\end{figure}
\begin{figure}
\begin{center}
\thocwincludegraphics{width=\textwidth}{epemudbardubar0}\\
\hfil\\
\thocwincludegraphics{width=\textwidth}{epemudbardubar}
\end{center}
\caption{\label{fig:epemudbardubar}
The DAGs for $e^+e^-\to u\bar d d\bar u$ before and after weeding
out unused nodes.}
\end{figure} *)
(* \thocwmodulesubsection{Amplitudes} *)
module
C
=
Cascade
.
Make
(
M
)(
P
)
type
selectors
=
C
.
selectors
let
external_wfs
n
particles
=
List
.
map
(
fun
(
f
,
p
)
->
({
A
.
flavor
=
f
;
A
.
momentum
=
P
.
singleton
n
p
;
A
.
wf_tag
=
A
.
Tags
.
null_wf
},
stat
f
p
))
particles
(* \thocwmodulesubsection{Main Function} *)
module
WFMap
=
Map
.
Make
(
struct
type
t
=
A
.
wf
let
compare
=
compare
end
)
(* [map_amplitude_wfs f a] applies the function [f : wf -> wf] to all
wavefunctions appearing in the amplitude [a]. *)
let
map_amplitude_wfs
f
a
=
let
map_rhs
(
c
,
wfs
)
=
(
c
,
PT
.
map
f
wfs
)
in
let
map_braket
(
wf
,
rhs
)
=
(
f
wf
,
List
.
map
map_rhs
rhs
)
and
map_fusion
(
lhs
,
rhs
)
=
(
f
lhs
,
List
.
map
map_rhs
rhs
)
in
let
map_dag
=
A
.
D
.
map
f
(
fun
node
rhs
->
map_rhs
rhs
)
in
let
tower
=
map_dag
a
.
A
.
fusion_tower
and
dag
=
map_dag
a
.
A
.
fusion_dag
in
let
dependencies_map
=
A
.
D
.
fold
(
fun
wf
_
->
WFMap
.
add
wf
(
A
.
D
.
dependencies
dag
wf
))
dag
WFMap
.
empty
in
{
A
.
fusions
=
List
.
map
map_fusion
a
.
A
.
fusions
;
A
.
brakets
=
List
.
map
map_braket
a
.
A
.
brakets
;
A
.
on_shell
=
a
.
A
.
on_shell
;
A
.
is_gauss
=
a
.
A
.
is_gauss
;
A
.
constraints
=
a
.
A
.
constraints
;
A
.
incoming
=
a
.
A
.
incoming
;
A
.
outgoing
=
a
.
A
.
outgoing
;
A
.
externals
=
List
.
map
f
a
.
A
.
externals
;
A
.
symmetry
=
a
.
A
.
symmetry
;
A
.
dependencies
=
(
fun
wf
->
WFMap
.
find
wf
dependencies_map
);
A
.
fusion_tower
=
tower
;
A
.
fusion_dag
=
dag
}
(*i
(* \begin{dubious}
Just a silly little test:
\end{dubious} *)
let hack_amplitude =
map_amplitude_wfs (fun wf -> { wf with momentum = P.split 2 16 wf.momentum })
i*)
(* This is the main function that constructs the amplitude for sets
of incoming and outgoing particles and returns the results in
conveniently packaged pieces. *)
let
amplitude
goldstones
selectors
fin
fout
=
(* Set up external lines and match flavors with numbered momenta. *)
let
f
=
fin
@
List
.
map
M
.
conjugate
fout
in
let
nin
,
nout
=
List
.
length
fin
,
List
.
length
fout
in
let
n
=
nin
+
nout
in
let
externals
=
List
.
combine
f
(
ThoList
.
range
1
n
)
in
let
wfs
=
external_wfs
n
externals
in
let
select_p
=
C
.
select_p
selectors
in
let
select_wf
=
match
fin
with
|
[_]
->
C
.
select_wf
selectors
P
.
Decay
.
timelike
|
_
->
C
.
select_wf
selectors
P
.
Scattering
.
timelike
in
let
select_vtx
=
C
.
select_vtx
selectors
in
(* Build the full fusion tower (including nodes that are never
needed in the amplitude). *)
let
stats
,
tower
=
if
goldstones
then
complete_fusion_tower
select_wf
select_vtx
wfs
else
minimal_fusion_tower
n
select_wf
select_vtx
wfs
in
(* Find all vertices for which \emph{all} off shell wavefunctions
are defined by the tower. *)
let
brakets
=
flavor_keystones
(
filter_keystone
stats
tower
)
select_p
n
(
filter_vertices
select_vtx
(
vertices
(
min
n
(
M
.
max_degree
()
))
(
M
.
flavors
()
)))
(
T
.
keystones
(
ThoList
.
range
1
n
))
in
(* Remove the part of the DAG that is never needed in the amplitude. *)
let
dag
=
if
goldstones
then
tower
else
A
.
D
.
harvest_list
tower
(
A
.
wavefunctions
brakets
)
in
(* Remove the leaf nodes of the DAG, corresponding to external lines. *)
let
fusions
=
List
.
filter
(
function
(_,
[]
)
->
false
|
_
->
true
)
(
A
.
D
.
lists
dag
)
in
(* Calculate the symmetry factor for identical particles in the
final state. *)
let
symmetry
=
Combinatorics
.
symmetry
fout
in
let
dependencies_map
=
A
.
D
.
fold
(
fun
wf
_
->
WFMap
.
add
wf
(
A
.
D
.
dependencies
dag
wf
))
dag
WFMap
.
empty
in
(* Finally: package the results: *)
{
A
.
fusions
=
fusions
;
A
.
brakets
=
brakets
;
A
.
on_shell
=
(
fun
wf
->
C
.
on_shell
selectors
(
A
.
flavor
wf
)
wf
.
A
.
momentum
);
A
.
is_gauss
=
(
fun
wf
->
C
.
is_gauss
selectors
(
A
.
flavor
wf
)
wf
.
A
.
momentum
);
A
.
constraints
=
C
.
description
selectors
;
A
.
incoming
=
fin
;
A
.
outgoing
=
fout
;
A
.
externals
=
List
.
map
fst
wfs
;
A
.
symmetry
=
symmetry
;
A
.
dependencies
=
(
fun
wf
->
WFMap
.
find
wf
dependencies_map
);
A
.
fusion_tower
=
tower
;
A
.
fusion_dag
=
dag
}
(* \thocwmodulesubsection{Color} *)
module
CM
=
Colorize
.
It
(
M
)
module
CA
=
Amplitude
(
PT
)(
P
)(
CM
)
let
colorize_wf
flavor
wf
=
{
CA
.
flavor
=
flavor
;
CA
.
momentum
=
wf
.
A
.
momentum
;
CA
.
wf_tag
=
wf
.
A
.
wf_tag
}
let
uncolorize_wf
wf
=
{
A
.
flavor
=
CM
.
flavor_sans_color
wf
.
CA
.
flavor
;
A
.
momentum
=
wf
.
CA
.
momentum
;
A
.
wf_tag
=
wf
.
CA
.
wf_tag
}
(* \begin{dubious}
At the end of the day, I shall want to have some sort of
\textit{fibered DAG} as abstract data type, with a projection
of colored nodes to their uncolored counterparts.
\end{dubious} *)
module
CWFBundle
=
Bundle
.
Make
(
struct
type
elt
=
CA
.
wf
let
compare_elt
=
compare
type
base
=
A
.
wf
let
compare_base
=
compare
let
pi
wf
=
{
A
.
flavor
=
CM
.
flavor_sans_color
wf
.
CA
.
flavor
;
A
.
momentum
=
wf
.
CA
.
momentum
;
A
.
wf_tag
=
wf
.
CA
.
wf_tag
}
end
)
(* \begin{dubious}
For now, we can live with simple aggregation:
\end{dubious} *)
type
fibered_dag
=
{
dag
:
CA
.
D
.
t
;
bundle
:
CWFBundle
.
t
}
(* Not yet(?) needed: [module CS = Stat (CM)] *)
let
colorize_sterile_nodes
dag
f
wf
fibered_dag
=
if
A
.
D
.
is_sterile
wf
dag
then
let
wf'
,
wf_bundle'
=
f
wf
fibered_dag
in
{
dag
=
CA
.
D
.
add_node
wf'
fibered_dag
.
dag
;
bundle
=
wf_bundle'
}
else
fibered_dag
let
colorize_nodes
f
wf
rhs
fibered_dag
=
let
wf_rhs_list'
,
wf_bundle'
=
f
wf
rhs
fibered_dag
in
let
dag'
=
List
.
fold_right
(
fun
(
wf'
,
rhs'
)
->
CA
.
D
.
add_offspring
wf'
rhs'
)
wf_rhs_list'
fibered_dag
.
dag
in
{
dag
=
dag'
;
bundle
=
wf_bundle'
}
(* O'Caml (correctly) infers the type
[val colorize_dag : (D.node -> D.edge * D.children -> fibered_dag ->
(CA.D.node * (CA.D.edge * CA.D.children)) list * CWFBundle.t) ->
(D.node -> fibered_dag -> CA.D.node * CWFBundle.t) ->
D.t -> CWFBundle.t -> fibered_dag]. *)
let
colorize_dag
f_node
f_ext
dag
wf_bundle
=
A
.
D
.
fold
(
colorize_nodes
f_node
)
dag
(
A
.
D
.
fold_nodes
(
colorize_sterile_nodes
dag
f_ext
)
dag
{
dag
=
CA
.
D
.
empty
;
bundle
=
wf_bundle
})
let
colorize_external
wf
fibered_dag
=
match
CWFBundle
.
inv_pi
wf
fibered_dag
.
bundle
with
|
[
c_wf
]
->
(
c_wf
,
fibered_dag
.
bundle
)
|
[]
->
failwith
"colorize_external: not found"
|
_
->
failwith
"colorize_external: not unique"
let
fuse_c_wf
rhs
=
let
momenta
=
PT
.
map
(
fun
wf
->
wf
.
CA
.
momentum
)
rhs
in
List
.
filter
(
fun
(_,
c
)
->
kmatrix_cuts
c
momenta
)
(
CM
.
fuse
(
List
.
map
(
fun
wf
->
wf
.
CA
.
flavor
)
(
PT
.
to_list
rhs
)))
let
colorize_coupling
c
coupling
=
{
coupling
with
Tagged_Coupling
.
coupling
=
c
}
let
colorize_fusion
wf
(
coupling
,
children
)
fibered_dag
=
let
match_flavor
(
f
,
_)
=
(
CM
.
flavor_sans_color
f
=
A
.
flavor
wf
)
and
find_colored
wf'
=
CWFBundle
.
inv_pi
wf'
fibered_dag
.
bundle
in
let
fusions
=
ThoList
.
flatmap
(
fun
c_children
->
List
.
map
(
fun
(
f
,
c
)
->
(
colorize_wf
f
wf
,
(
colorize_coupling
c
coupling
,
c_children
)))
(
List
.
filter
match_flavor
(
fuse_c_wf
c_children
)))
(
PT
.
product
(
PT
.
map
find_colored
children
))
in
let
bundle
=
List
.
fold_right
(
fun
(
c_wf
,
_)
->
CWFBundle
.
add
c_wf
)
fusions
fibered_dag
.
bundle
in
(
fusions
,
bundle
)
let
colorize_braket1
(
wf
,
(
coupling
,
children
))
fibered_dag
=
let
find_colored
wf'
=
CWFBundle
.
inv_pi
wf'
fibered_dag
.
bundle
in
Product
.
fold2
(
fun
bra
ket
acc
->
List
.
fold_left
(
fun
brakets
(
f
,
c
)
->
if
CM
.
conjugate
bra
.
CA
.
flavor
=
f
then
(
bra
,
(
colorize_coupling
c
coupling
,
ket
))
::
brakets
else
brakets
)
acc
(
fuse_c_wf
ket
))
(
find_colored
wf
)
(
PT
.
product
(
PT
.
map
find_colored
children
))
[]
module
CWFMap
=
Map
.
Make
(
struct
type
t
=
CA
.
wf
let
compare
=
CA
.
order_wf
end
)
module
CKetSet
=
Set
.
Make
(
struct
type
t
=
CA
.
rhs
let
compare
=
compare
end
)
(* Find a set of kets in [map] that belong to [bra].
Return the empty set, if nothing is found. *)
let
lookup_ketset
bra
map
=
try
CWFMap
.
find
bra
map
with
Not_found
->
CKetSet
.
empty
(* Return the set of kets belonging to [bra] in [map],
augmented by [ket]. *)
let
addto_ketset
bra
ket
map
=
CKetSet
.
add
ket
(
lookup_ketset
bra
map
)
(* Augment or update [map] with a new [(bra, ket)] relation. *)
let
addto_ketset_map
map
(
bra
,
ket
)
=
CWFMap
.
add
bra
(
addto_ketset
bra
ket
map
)
map
(* Take a list of [(bra, ket)] pairs and group the [ket]s
according to [bra]. This is very similar to
[ThoList.factorize] on page~\pageref{ThoList.factorize},
but the latter keeps duplicate copies, while we keep
only one, with equality determined by [CA.order_wf]. *)
(* \begin{dubious}
Isn't [Bundle]~\ref{Bundle} the correct framework for this?
\end{dubious} *)
let
factorize_brakets
brakets
=
CWFMap
.
fold
(
fun
bra
ket
acc
->
(
bra
,
CKetSet
.
elements
ket
)
::
acc
)
(
List
.
fold_left
addto_ketset_map
CWFMap
.
empty
brakets
)
[]
let
colorize_braket
(
wf
,
rhs_list
)
fibered_dag
=
factorize_brakets
(
ThoList
.
flatmap
(
fun
rhs
->
(
colorize_braket1
(
wf
,
rhs
)
fibered_dag
))
rhs_list
)
let
colorize_amplitude
a
fin
fout
=
let
f
=
fin
@
List
.
map
CM
.
conjugate
fout
in
let
nin
,
nout
=
List
.
length
fin
,
List
.
length
fout
in
let
n
=
nin
+
nout
in
let
externals
=
List
.
combine
f
(
ThoList
.
range
1
n
)
in
let
external_wfs
=
CA
.
external_wfs
n
externals
in
let
wf_bundle
=
CWFBundle
.
of_list
external_wfs
in
let
fibered_dag
=
colorize_dag
colorize_fusion
colorize_external
a
.
A
.
fusion_dag
wf_bundle
in
let
brakets
=
ThoList
.
flatmap
(
fun
braket
->
colorize_braket
braket
fibered_dag
)
a
.
A
.
brakets
in
let
dag
=
CA
.
D
.
harvest_list
fibered_dag
.
dag
(
CA
.
wavefunctions
brakets
)
in
let
fusions
=
List
.
filter
(
function
(_,
[]
)
->
false
|
_
->
true
)
(
CA
.
D
.
lists
dag
)
in
let
dependencies_map
=
CA
.
D
.
fold
(
fun
wf
_
->
CWFMap
.
add
wf
(
CA
.
D
.
dependencies
dag
wf
))
dag
CWFMap
.
empty
in
{
CA
.
fusions
=
fusions
;
CA
.
brakets
=
brakets
;
CA
.
constraints
=
a
.
A
.
constraints
;
CA
.
incoming
=
fin
;
CA
.
outgoing
=
fout
;
CA
.
externals
=
external_wfs
;
CA
.
fusion_dag
=
dag
;
CA
.
fusion_tower
=
dag
;
CA
.
symmetry
=
a
.
A
.
symmetry
;
CA
.
on_shell
=
(
fun
wf
->
a
.
A
.
on_shell
(
uncolorize_wf
wf
));
CA
.
is_gauss
=
(
fun
wf
->
a
.
A
.
is_gauss
(
uncolorize_wf
wf
));
CA
.
dependencies
=
(
fun
wf
->
CWFMap
.
find
wf
dependencies_map
)
}
let
allowed
amplitude
=
match
amplitude
.
CA
.
brakets
with
|
[]
->
false
|
_
->
true
let
colorize_amplitudes
a
=
List
.
fold_left
(
fun
amps
(
fin
,
fout
)
->
let
amp
=
colorize_amplitude
a
fin
fout
in
if
allowed
amp
then
amp
::
amps
else
amps
)
[]
(
CM
.
amplitude
a
.
A
.
incoming
a
.
A
.
outgoing
)
let
amplitudes
goldstones
exclusions
selectors
fin
fout
=
colorize_amplitudes
(
amplitude
goldstones
selectors
fin
fout
)
let
amplitude_sans_color
goldstones
exclusions
selectors
fin
fout
=
amplitude
goldstones
selectors
fin
fout
type
flavor
=
CA
.
flavor
type
flavor_sans_color
=
A
.
flavor
type
p
=
A
.
p
type
wf
=
CA
.
wf
let
conjugate
=
CA
.
conjugate
let
flavor
=
CA
.
flavor
let
flavor_sans_color
wf
=
CM
.
flavor_sans_color
(
CA
.
flavor
wf
)
let
momentum
=
CA
.
momentum
let
momentum_list
=
CA
.
momentum_list
let
wf_tag
=
CA
.
wf_tag
type
coupling
=
CA
.
coupling
let
sign
=
CA
.
sign
let
coupling
=
CA
.
coupling
let
coupling_tag
=
CA
.
coupling_tag
type
exclusions
=
CA
.
exclusions
let
no_exclusions
=
CA
.
no_exclusions
type
'
a
children
=
'
a
CA
.
children
type
rhs
=
CA
.
rhs
let
children
=
CA
.
children
type
fusion
=
CA
.
fusion
let
lhs
=
CA
.
lhs
let
rhs
=
CA
.
rhs
type
braket
=
CA
.
braket
let
bra
=
CA
.
bra
let
ket
=
CA
.
ket
type
amplitude
=
CA
.
amplitude
type
amplitude_sans_color
=
A
.
amplitude
let
incoming
=
CA
.
incoming
let
outgoing
=
CA
.
outgoing
let
externals
=
CA
.
externals
let
fusions
=
CA
.
fusions
let
brakets
=
CA
.
brakets
let
symmetry
=
CA
.
symmetry
let
on_shell
=
CA
.
on_shell
let
is_gauss
=
CA
.
is_gauss
let
constraints
=
CA
.
constraints
let
variables
a
=
List
.
map
lhs
(
fusions
a
)
let
dependencies
=
CA
.
dependencies
(* \thocwmodulesubsection{Checking Conservation Laws} *)
let
check_charges
()
=
let
vlist3
,
vlist4
,
vlistn
=
M
.
vertices
()
in
List
.
filter
(
fun
flist
->
not
(
M
.
Ch
.
is_null
(
M
.
Ch
.
sum
(
List
.
map
M
.
charges
flist
))))
(
List
.
map
(
fun
((
f1
,
f2
,
f3
),
_,
_)
->
[
f1
;
f2
;
f3
])
vlist3
@
List
.
map
(
fun
((
f1
,
f2
,
f3
,
f4
),
_,
_)
->
[
f1
;
f2
;
f3
;
f4
])
vlist4
@
List
.
map
(
fun
(
flist
,
_,
_)
->
flist
)
vlistn
)
(* \thocwmodulesubsection{Diagnostics} *)
let
count_propagators
a
=
List
.
length
a
.
CA
.
fusions
let
count_fusions
a
=
List
.
fold_left
(
fun
n
(_,
a
)
->
n
+
List
.
length
a
)
0
a
.
CA
.
fusions
+
List
.
fold_left
(
fun
n
(_,
t
)
->
n
+
List
.
length
t
)
0
a
.
CA
.
brakets
+
List
.
length
a
.
CA
.
brakets
(* \begin{dubious}
This brute force approach blows up for more than ten particles.
Find a smarter algorithm.
\end{dubious} *)
let
count_diagrams
a
=
List
.
fold_left
(
fun
n
(
wf1
,
wf23
)
->
n
+
CA
.
D
.
count_trees
wf1
a
.
CA
.
fusion_dag
*
(
List
.
fold_left
(
fun
n'
(_,
wfs
)
->
n'
+
PT
.
fold_left
(
fun
n''
wf
->
n''
*
CA
.
D
.
count_trees
wf
a
.
CA
.
fusion_dag
)
1
wfs
)
0
wf23
))
0
a
.
CA
.
brakets
exception
Impossible
let
forest'
a
=
let
below
wf
=
CA
.
D
.
forest_memoized
wf
a
.
CA
.
fusion_dag
in
ThoList
.
flatmap
(
fun
(
bra
,
ket
)
->
(
Product
.
list2
(
fun
bra'
ket'
->
bra'
::
ket'
)
(
below
bra
)
(
ThoList
.
flatmap
(
fun
(_,
wfs
)
->
Product
.
list
(
fun
w
->
w
)
(
PT
.
to_list
(
PT
.
map
below
wfs
)))
ket
)))
a
.
CA
.
brakets
let
cross
wf
=
{
CA
.
flavor
=
CM
.
conjugate
wf
.
CA
.
flavor
;
CA
.
momentum
=
P
.
neg
wf
.
CA
.
momentum
;
CA
.
wf_tag
=
wf
.
CA
.
wf_tag
}
let
fuse_trees
wf
ts
=
Tree
.
fuse
(
fun
(
wf'
,
e
)
->
(
cross
wf'
,
e
))
wf
(
fun
t
->
List
.
mem
wf
(
Tree
.
leafs
t
))
ts
let
forest
wf
a
=
List
.
map
(
fuse_trees
wf
)
(
forest'
a
)
(*i
(* \begin{dubious}
The following duplication should be replaced by polymorphism
or a functor.
\end{dubious} *)
let forest_uncolored' a =
let below wf = A.D.forest_memoized wf a.A.fusion_dag in
ThoList.flatmap
(fun (bra, ket) ->
(Product.list2 (fun bra' ket' -> bra' :: ket')
(below bra)
(ThoList.flatmap
(fun (_, wfs) ->
Product.list (fun w -> w) (PT.to_list (PT.map below wfs)))
ket)))
a.A.brakets
let cross_uncolored wf =
{ A.flavor = M.conjugate wf.A.flavor;
A.momentum = P.neg wf.A.momentum;
A.wf_tag = wf.A.wf_tag }
let fuse_trees_uncolored wf ts =
Tree.fuse (fun (wf', e) -> (cross_uncolored wf', e))
wf (fun t -> List.mem wf (Tree.leafs t)) ts
let forest_sans_color wf a =
List.map (fuse_trees_uncolored wf) (forest_uncolored' a)
i*)
let
poles_beneath
wf
dag
=
CA
.
D
.
eval_memoized
(
fun
wf'
->
[
[]
])
(
fun
wf'
_
p
->
List
.
map
(
fun
p'
->
wf'
::
p'
)
p
)
(
fun
wf1
wf2
->
Product
.
fold2
(
fun
wf'
wfs'
wfs''
->
(
wf'
@
wfs'
)
::
wfs''
)
wf1
wf2
[]
)
(@)
[
[]
]
[
[]
]
wf
dag
let
poles
a
=
ThoList
.
flatmap
(
fun
(
wf1
,
wf23
)
->
let
poles_wf1
=
poles_beneath
wf1
a
.
CA
.
fusion_dag
in
(
ThoList
.
flatmap
(
fun
(_,
wfs
)
->
Product
.
list
List
.
flatten
(
PT
.
to_list
(
PT
.
map
(
fun
wf
->
poles_wf1
@
poles_beneath
wf
a
.
CA
.
fusion_dag
)
wfs
)))
wf23
))
a
.
CA
.
brakets
module
WFSet
=
Set
.
Make
(
struct
type
t
=
CA
.
wf
let
compare
=
CA
.
order_wf
end
)
let
s_channel
a
=
WFSet
.
elements
(
ThoList
.
fold_right2
(
fun
wf
wfs
->
if
P
.
Scattering
.
timelike
wf
.
CA
.
momentum
then
WFSet
.
add
wf
wfs
else
wfs
)
(
poles
a
)
WFSet
.
empty
)
(* \begin{dubious}
This should be much faster! Is it correct? Is it faster indeed?
\end{dubious} *)
let
poles'
a
=
List
.
map
CA
.
lhs
a
.
CA
.
fusions
let
s_channel
a
=
WFSet
.
elements
(
List
.
fold_right
(
fun
wf
wfs
->
if
P
.
Scattering
.
timelike
wf
.
CA
.
momentum
then
WFSet
.
add
wf
wfs
else
wfs
)
(
poles'
a
)
WFSet
.
empty
)
(* \thocwmodulesubsection{Pictures} *)
(* Export the DAG in the \texttt{dot(1)} file format so that we can
draw pretty pictures to impress audiences \ldots *)
let
p2s
p
=
if
p
>=
0
&&
p
<=
9
then
string_of_int
p
else
if
p
<=
36
then
String
.
make
1
(
Char
.
chr
(
Char
.
code
'A'
+
p
-
10
))
else
"_"
let
variable
wf
=
CM
.
flavor_symbol
wf
.
CA
.
flavor
^
String
.
concat
""
(
List
.
map
p2s
(
P
.
to_ints
wf
.
CA
.
momentum
))
module
Int
=
Map
.
Make
(
struct
type
t
=
int
let
compare
=
compare
end
)
let
add_to_list
i
n
m
=
Int
.
add
i
(
n
::
try
Int
.
find
i
m
with
Not_found
->
[]
)
m
let
classify_nodes
dag
=
Int
.
fold
(
fun
i
n
acc
->
(
i
,
n
)
::
acc
)
(
CA
.
D
.
fold_nodes
(
fun
wf
->
add_to_list
(
P
.
rank
wf
.
CA
.
momentum
)
wf
)
dag
Int
.
empty
)
[]
let
dag_to_dot
ch
brakets
dag
=
Printf
.
fprintf
ch
"digraph OMEGA {
\n
"
;
CA
.
D
.
iter_nodes
(
fun
wf
->
Printf
.
fprintf
ch
"
\"
%s
\"
[ label =
\"
%s
\"
];
\n
"
(
variable
wf
)
(
variable
wf
))
dag
;
List
.
iter
(
fun
(_,
wfs
)
->
Printf
.
fprintf
ch
" { rank = same;"
;
List
.
iter
(
fun
n
->
Printf
.
fprintf
ch
"
\"
%s
\"
;"
(
variable
n
))
wfs
;
Printf
.
fprintf
ch
" };
\n
"
)
(
classify_nodes
dag
);
List
.
iter
(
fun
n
->
Printf
.
fprintf
ch
"
\"
*
\"
->
\"
%s
\"
;
\n
"
(
variable
n
))
(
flatten_keystones
brakets
);
CA
.
D
.
iter
(
fun
n
(_,
ns
)
->
let
p
=
variable
n
in
PT
.
iter
(
fun
n'
->
Printf
.
fprintf
ch
"
\"
%s
\"
->
\"
%s
\"
;
\n
"
p
(
variable
n'
))
ns
)
dag
;
Printf
.
fprintf
ch
"}
\n
"
let
tower_to_dot
ch
a
=
dag_to_dot
ch
a
.
CA
.
brakets
a
.
CA
.
fusion_tower
let
amplitude_to_dot
ch
a
=
dag_to_dot
ch
a
.
CA
.
brakets
a
.
CA
.
fusion_dag
(* \thocwmodulesubsection{Phasespace} *)
let
variable
wf
=
M
.
flavor_to_string
wf
.
A
.
flavor
^
"["
^
String
.
concat
"/"
(
List
.
map
p2s
(
P
.
to_ints
wf
.
A
.
momentum
))
^
"]"
let
below_to_channel
transform
ch
dag
wf
=
let
n2s
wf
=
variable
(
transform
wf
)
and
e2s
c
=
""
in
Tree2
.
to_channel
ch
n2s
e2s
(
A
.
D
.
dependencies
dag
wf
)
let
bra_to_channel
transform
ch
dag
wf
=
let
tree
=
A
.
D
.
dependencies
dag
wf
in
if
Tree2
.
is_singleton
tree
then
let
n2s
wf
=
variable
(
transform
wf
)
and
e2s
c
=
""
in
Tree2
.
to_channel
ch
n2s
e2s
tree
else
failwith
"Fusion.phase_space_channels: wrong topology!"
let
ket_to_channel
transform
ch
dag
ket
=
Printf
.
fprintf
ch
"("
;
begin
match
A
.
children
ket
with
|
[]
->
()
|
[
child
]
->
below_to_channel
transform
ch
dag
child
|
child
::
children
->
below_to_channel
transform
ch
dag
child
;
List
.
iter
(
fun
child
->
Printf
.
fprintf
ch
","
;
below_to_channel
transform
ch
dag
child
)
children
end
;
Printf
.
fprintf
ch
")"
let
phase_space_braket
transform
ch
(
bra
,
ket
)
dag
=
bra_to_channel
transform
ch
dag
bra
;
Printf
.
fprintf
ch
": {"
;
begin
match
ket
with
|
[]
->
()
|
[
ket1
]
->
Printf
.
fprintf
ch
" "
;
ket_to_channel
transform
ch
dag
ket1
|
ket1
::
kets
->
Printf
.
fprintf
ch
" "
;
ket_to_channel
transform
ch
dag
ket1
;
List
.
iter
(
fun
k
->
Printf
.
fprintf
ch
"
\\\n
| "
;
ket_to_channel
transform
ch
dag
k
)
kets
end
;
Printf
.
fprintf
ch
" }
\n
"
(*i Food for thought:
let braket_to_tree2 dag (bra, ket) =
let bra' = A.D.dependencies dag bra in
if Tree2.is_singleton bra' then
Tree2.cons
[(fst ket, bra, List.map (A.D.dependencies dag) (A.children ket))]
else
failwith "Fusion.phase_space_channels: wrong topology!"
let phase_space_braket transform ch (bra, ket) dag =
let n2s wf = variable (transform wf)
and e2s c = "" in
Printf.fprintf
ch "%s\n" (Tree2.to_string n2s e2s (braket_to_tree2 dag (bra, ket)))
i*)
let
phase_space_channels_transformed
transform
ch
a
=
List
.
iter
(
fun
braket
->
phase_space_braket
transform
ch
braket
a
.
A
.
fusion_dag
)
a
.
A
.
brakets
let
phase_space_channels
ch
a
=
phase_space_channels_transformed
(
fun
wf
->
wf
)
ch
a
let
exchange_momenta_list
p1
p2
p
=
List
.
map
(
fun
pi
->
if
pi
=
p1
then
p2
else
if
pi
=
p2
then
p1
else
pi
)
p
let
exchange_momenta
p1
p2
p
=
P
.
of_ints
(
P
.
dim
p
)
(
exchange_momenta_list
p1
p2
(
P
.
to_ints
p
))
let
flip_momenta
wf
=
{
wf
with
A
.
momentum
=
exchange_momenta
1
2
wf
.
A
.
momentum
}
let
phase_space_channels_flipped
ch
a
=
phase_space_channels_transformed
flip_momenta
ch
a
end
module
Make
=
Tagged
(
No_Tags
)
module
Binary
=
Make
(
Tuple
.
Binary
)(
Stat_Dirac
)(
Topology
.
Binary
)
module
Tagged_Binary
(
T
:
Tagger
)
=
Tagged
(
T
)(
Tuple
.
Binary
)(
Stat_Dirac
)(
Topology
.
Binary
)
(* \thocwmodulesection{Fusions with Majorana Fermions} *)
let
majorana_log
silent
logging
=
logging
let
majorana_log
silent
logging
=
silent
let
force_legacy
=
true
let
force_legacy
=
false
module
Stat_Majorana
(
M
:
Model
.
T
)
:
(
Stat
with
type
flavor
=
M
.
flavor
)
=
struct
exception
Impossible
type
flavor
=
M
.
flavor
(* \thocwmodulesubsection{Keeping Track of Fermion Lines} *)
(* JRR's algorithm doesn't use lists of pairs representing
directed arrows as in [Stat_Dirac().stat] above, but a list
of integers denoting the external leg a fermion line connects
to: *)
type
stat
=
|
Fermion
of
int
*
int
list
|
AntiFermion
of
int
*
int
list
|
Boson
of
int
list
|
Majorana
of
int
*
int
list
let
sign_of_permutation
lines
=
fst
(
Combinatorics
.
sort_signed
lines
)
let
lines_equivalent
l1
l2
=
sign_of_permutation
l1
=
sign_of_permutation
l2
let
stat_to_string
s
=
let
open
Printf
in
let
l2s
=
ThoList
.
to_string
string_of_int
in
match
s
with
|
Boson
lines
->
sprintf
"B%s"
(
l2s
lines
)
|
Fermion
(
p
,
lines
)
->
sprintf
"F(%d, %s)"
p
(
l2s
lines
)
|
AntiFermion
(
p
,
lines
)
->
sprintf
"A(%d, %s)"
p
(
l2s
lines
)
|
Majorana
(
p
,
lines
)
->
sprintf
"M(%d, %s)"
p
(
l2s
lines
)
(* Writing all cases explicitely is tedious, but allows exhaustiveness
checking. *)
let
equal
s1
s2
=
match
s1
,
s2
with
|
Boson
l1
,
Boson
l2
->
lines_equivalent
l1
l2
|
Majorana
(
p1
,
l1
),
Majorana
(
p2
,
l2
)
|
Fermion
(
p1
,
l1
),
Fermion
(
p2
,
l2
)
|
AntiFermion
(
p1
,
l1
),
AntiFermion
(
p2
,
l2
)
->
p1
=
p2
&&
lines_equivalent
l1
l2
|
Boson
_,
(
Fermion
_
|
AntiFermion
_
|
Majorana
_
)
|
(
Fermion
_
|
AntiFermion
_
|
Majorana
_
),
Boson
_
|
Majorana
_,
(
Fermion
_
|
AntiFermion
_)
|
(
Fermion
_
|
AntiFermion
_),
Majorana
_
|
Fermion
_
,
AntiFermion
_
|
AntiFermion
_
,
Fermion
_
->
false
(* The final amplitude must not be fermionic! *)
let
saturated
=
function
|
Boson
_
->
true
|
Fermion
_
|
AntiFermion
_
|
Majorana
_
->
false
(* [stat f p] interprets the numeric fermion numbers of flavor [f]
at external leg [p] at creates a leaf: *)
let
stat
f
p
=
match
M
.
fermion
f
with
|
0
->
Boson
[]
|
1
->
Fermion
(
p
,
[]
)
|
-
1
->
AntiFermion
(
p
,
[]
)
|
2
->
Majorana
(
p
,
[]
)
|
_
->
invalid_arg
"Fusion.Stat_Majorana: invalid fermion number"
(* The formalism of~\cite{Denner:Majorana} does not distinguish
spinors from conjugate spinors, it is only important to know in which direction
a fermion line is calculated. So the sign is made by the calculation together
with an aditional one due to the permuation of the pairs of endpoints of
fermion lines in the direction they are calculated. We propose a
``canonical'' direction from the right to the left child at a fusion point
so we only have to keep in mind which external particle hangs at each side.
Therefore we need not to have a list of pairs of conjugate spinors and
spinors but just a list in which the pairs are right-left-right-left
and so on. Unfortunately it is unavoidable to have couplings with clashing
arrows in supersymmetric theories so we need transmutations from fermions
in antifermions and vice versa as well. *)
(* \thocwmodulesubsection{Merge Fermion Lines for Legacy Models with Implied Fermion Connections} *)
(* In the legacy case with at most one fermion line, it was straight
forward to determine the kind of outgoing line from the
corresponding flavor. In the general case, it is not
possible to maintain this constraint, when constructing
the $n$-ary fusion from binary ones. *)
(* We can break up the process into two steps however:
first perform unconstrained fusions pairwise \ldots *)
let
stat_fuse_pair_unconstrained
s1
s2
=
match
s1
,
s2
with
|
Boson
l1
,
Boson
l2
->
Boson
(
l1
@
l2
)
|
(
Majorana
(
p1
,
l1
)
|
Fermion
(
p1
,
l1
)
|
AntiFermion
(
p1
,
l1
)),
(
Majorana
(
p2
,
l2
)
|
Fermion
(
p2
,
l2
)
|
AntiFermion
(
p2
,
l2
))
->
Boson
([
p2
;
p1
]
@
l1
@
l2
)
|
Boson
l1
,
Majorana
(
p
,
l2
)
->
Majorana
(
p
,
l1
@
l2
)
|
Boson
l1
,
Fermion
(
p
,
l2
)
->
Fermion
(
p
,
l1
@
l2
)
|
Boson
l1
,
AntiFermion
(
p
,
l2
)
->
AntiFermion
(
p
,
l1
@
l2
)
|
Majorana
(
p
,
l1
),
Boson
l2
->
Majorana
(
p
,
l1
@
l2
)
|
Fermion
(
p
,
l1
),
Boson
l2
->
Fermion
(
p
,
l1
@
l2
)
|
AntiFermion
(
p
,
l1
),
Boson
l2
->
AntiFermion
(
p
,
l1
@
l2
)
(* \ldots{} and only apply the constraint to the outgoing leg. *)
let
constrain_stat_fusion
s
f
=
match
s
,
M
.
lorentz
f
with
|
(
Majorana
(
p
,
l
)
|
Fermion
(
p
,
l
)
|
AntiFermion
(
p
,
l
)),
(
Coupling
.
Majorana
|
Coupling
.
Vectorspinor
|
Coupling
.
Maj_Ghost
)
->
Majorana
(
p
,
l
)
|
(
Majorana
(
p
,
l
)
|
Fermion
(
p
,
l
)
|
AntiFermion
(
p
,
l
)),
Coupling
.
Spinor
->
Fermion
(
p
,
l
)
|
(
Majorana
(
p
,
l
)
|
Fermion
(
p
,
l
)
|
AntiFermion
(
p
,
l
)),
Coupling
.
ConjSpinor
->
AntiFermion
(
p
,
l
)
|
(
Majorana
_
|
Fermion
_
|
AntiFermion
_
as
s
),
(
Coupling
.
Scalar
|
Coupling
.
Vector
|
Coupling
.
Massive_Vector
|
Coupling
.
Tensor_1
|
Coupling
.
Tensor_2
|
Coupling
.
BRS
_)
->
invalid_arg
(
Printf
.
sprintf
"Fusion.stat_fuse_pair_constrained: expected boson, got %s"
(
stat_to_string
s
))
|
Boson
l
as
s
,
(
Coupling
.
Majorana
|
Coupling
.
Vectorspinor
|
Coupling
.
Maj_Ghost
|
Coupling
.
Spinor
|
Coupling
.
ConjSpinor
)
->
invalid_arg
(
Printf
.
sprintf
"Fusion.stat_fuse_pair_constrained: expected fermion, got %s"
(
stat_to_string
s
))
|
Boson
l
,
(
Coupling
.
Scalar
|
Coupling
.
Vector
|
Coupling
.
Massive_Vector
|
Coupling
.
Tensor_1
|
Coupling
.
Tensor_2
|
Coupling
.
BRS
_)
->
Boson
l
let
stat_fuse_pair_legacy
f
s1
s2
=
stat_fuse_pair_unconstrained
s1
s2
let
stat_fuse_pair_legacy_logging
f
s1
s2
=
let
stat
=
stat_fuse_pair_legacy
f
s1
s2
in
Printf
.
eprintf
"stat_fuse_pair_legacy: (%s, %s) -> %s = %s
\n
"
(
stat_to_string
s1
)
(
stat_to_string
s2
)
(
stat_to_string
stat
)
(
M
.
flavor_to_string
f
);
stat
let
stat_fuse_pair_legacy
=
majorana_log
stat_fuse_pair_legacy
stat_fuse_pair_legacy_logging
(* Note that we are using [List.fold_left], therefore
we perform the fusions as
$f(f(\ldots(f(s_1,s_2),s_3),\ldots),s_n)$. Had we used
[List.fold_right] instead, we would compute
$f(s_1,f(s_2,\ldots f(s_{n-1},s_n))).$ For our Dirac
algorithm, this makes no difference, but JRR's Majorana
algorithm depends on the order! *)
(* Also not that we \emph{must not} apply [constrain_stat_fusion]
here, because [stat_fuse_legacy] will be used in
[stat_keystone_legacy] again, where we always expect
[Boson _]. *)
let
stat_fuse_legacy
s1
s23__n
f
=
List
.
fold_left
(
stat_fuse_pair_legacy
f
)
s1
s23__n
(*i
let stat_fuse_legacy' s1 s23__n f =
match List.rev (s1 :: s23__n) with
| s1 :: s23__n -> List.fold_left (stat_fuse_pair_legacy f) s1 s23__n
| [] -> failwith "stat_fuse_legacy: impossible"
let stat_fuse_legacy' s1 s23__n f =
List.fold_right (stat_fuse_pair_legacy f) s23__n s1
i*)
let
stat_fuse_legacy_logging
s1
s23__n
f
=
let
stat
=
stat_fuse_legacy
s1
s23__n
f
in
Printf
.
eprintf
"stat_fuse_legacy: %s -> %s = %s
\n
"
(
ThoList
.
to_string
stat_to_string
(
s1
::
s23__n
))
(
stat_to_string
stat
)
(
M
.
flavor_to_string
f
);
stat
let
stat_fuse_legacy
=
majorana_log
stat_fuse_legacy
stat_fuse_legacy_logging
(* \thocwmodulesubsection{Merge Fermion Lines using Explicit Fermion Connections} *)
(* We need to match the fermion lines in the incoming propagators
using the connection information in the vertex. This used to
be trivial in the old omega, because there was at most one
fermion line in a vertex. *)
module
IMap
=
Map
.
Make
(
struct
type
t
=
int
let
compare
=
compare
end
)
(* From version 4.05 on, this is just [IMap.find_opt]. *)
let
imap_find_opt
p
map
=
try
Some
(
IMap
.
find
p
map
)
with
Not_found
->
None
(* Partially combined [stat]s of the incoming propagators and keeping
track of the fermion lines, while we're scanning them. *)
type
partial
=
{
stat
:
stat
(* the [stat] accumulated so far *)
;
fermions
:
int
IMap
.
t
(* a map from the indices in the vertex to open (anti)fermion lines *)
;
n
:
int
(* the number of incoming propagators *)
}
(* We will
perform two passes:
\begin{enumerate}
\item collect the saturated fermion lines in a [Boson], while
building a map from the indices in the vertex to the open
fermion lines
\item connect the open fermion lines using the [int -> int] map
[fermions].
\end{enumerate} *)
let
empty_partial
=
{
stat
=
Boson
[]
;
fermions
=
IMap
.
empty
;
n
=
0
}
(* Only for debugging: *)
let
partial_to_string
p
=
Printf
.
sprintf
"{ fermions=%s, stat=%s, #=%d }"
(
ThoList
.
to_string
(
fun
(
i
,
particle
)
->
Printf
.
sprintf
"%d@%d"
particle
i
)
(
IMap
.
bindings
p
.
fermions
))
(
stat_to_string
p
.
stat
)
p
.
n
(* Add a list of saturated fermion lines at the top of the list
of lines in a [stat]. *)
let
add_lines
l
=
function
|
Boson
l'
->
Boson
(
l
@
l'
)
|
Fermion
(
n
,
l'
)
->
Fermion
(
n
,
l
@
l'
)
|
AntiFermion
(
n
,
l'
)
->
AntiFermion
(
n
,
l
@
l'
)
|
Majorana
(
n
,
l'
)
->
Majorana
(
n
,
l
@
l'
)
(* Process one line in the first pass: add the saturated fermion lines
to the partial stat [p.stat]
and add a pointer to an open fermion line in case of a fermion. *)
let
add_lines_to_partial
p
stat
=
let
n
=
succ
p
.
n
in
match
stat
with
|
Boson
l
->
{
fermions
=
p
.
fermions
;
stat
=
add_lines
l
p
.
stat
;
n
}
|
Majorana
(
f
,
l
)
->
{
fermions
=
IMap
.
add
n
f
p
.
fermions
;
stat
=
add_lines
l
p
.
stat
;
n
}
|
Fermion
(
p
,
l
)
->
invalid_arg
"add_lines_to_partial: unexpected Fermion"
|
AntiFermion
(
p
,
l
)
->
invalid_arg
"add_lines_to_partial: unexpected AntiFermion"
(* Do it for all lines: *)
let
partial_of_slist
stat_list
=
List
.
fold_left
add_lines_to_partial
empty_partial
stat_list
let
partial_of_rev_slist
stat_list
=
List
.
fold_left
add_lines_to_partial
empty_partial
(
List
.
rev
stat_list
)
(* The building blocks for a single step of the second pass:
saturate a fermion line or pass it through. *)
(* The indices [i] and [j] refer to incoming lines: add a saturated
line to [p.stat] and remove the corresponding open lines from
the map. *)
let
saturate_fermion_line
p
i
j
=
match
imap_find_opt
i
p
.
fermions
,
imap_find_opt
j
p
.
fermions
with
|
Some
f
,
Some
f'
->
{
stat
=
add_lines
[
f'
;
f
]
p
.
stat
;
fermions
=
IMap
.
remove
i
(
IMap
.
remove
j
p
.
fermions
);
n
=
p
.
n
}
|
Some
_,
None
->
invalid_arg
"saturate_fermion_line: no open outgoing fermion line"
|
None
,
Some
_
->
invalid_arg
"saturate_fermion_line: no open incoming fermion line"
|
None
,
None
->
invalid_arg
"saturate_fermion_line: no open fermion lines"
(* The index [i] refers to an incoming line: add the open line
to [p.stat] and remove it from the map. *)
let
pass_through_fermion_line
p
i
=
match
imap_find_opt
i
p
.
fermions
,
p
.
stat
with
|
Some
f
,
Boson
l
->
{
stat
=
Majorana
(
f
,
l
);
fermions
=
IMap
.
remove
i
p
.
fermions
;
n
=
p
.
n
}
|
Some
_
,
(
Majorana
_
|
Fermion
_
|
AntiFermion
_)
->
invalid_arg
"pass_through_fermion_line: more than one open line"
|
None
,
_
->
invalid_arg
"pass_through_fermion_line: expected fermion not found"
(* Ignoring the direction of the fermion line reproduces JRR's algorithm. *)
let
sort_pair
(
i
,
j
)
=
if
i
<
j
then
(
i
,
j
)
else
(
j
,
i
)
(* The index [p.n + 1] corresponds to the outgoing line: *)
let
is_incoming
p
i
=
i
<=
p
.
n
let
match_fermion_line
p
(
i
,
j
)
=
let
i
,
j
=
sort_pair
(
i
,
j
)
in
if
is_incoming
p
i
&&
is_incoming
p
j
then
saturate_fermion_line
p
i
j
else
if
is_incoming
p
i
then
pass_through_fermion_line
p
i
else
if
is_incoming
p
j
then
pass_through_fermion_line
p
j
else
failwith
"match_fermion_line: both lines outgoing"
let
match_fermion_line_logging
p
(
i
,
j
)
=
Printf
.
eprintf
"match_fermion_line %s [%d->%d]"
(
partial_to_string
p
)
i
j
;
let
p'
=
match_fermion_line
p
(
i
,
j
)
in
Printf
.
eprintf
" >> %s
\n
"
(
partial_to_string
p'
);
p'
let
match_fermion_line
=
majorana_log
match_fermion_line
match_fermion_line_logging
(* Combine the passes \ldots *)
let
match_fermion_lines
flines
s1
s23__n
=
List
.
fold_left
match_fermion_line
(
partial_of_slist
(
s1
::
s23__n
))
flines
(* \ldots{} and keep only the [stat]. *)
let
stat_fuse_new
flines
s1
s23__n
_
=
(
match_fermion_lines
flines
s1
s23__n
).
stat
(* If there is at most a single fermion line, we can compare [stat]
against the result of [stat_fuse_legacy] for checking
[stat_fuse_new] (admittedly, this case is rather trivial) \ldots *)
let
stat_fuse_new_check
stat
flines
s1
s23__n
f
=
if
List
.
length
flines
<
2
then
begin
let
legacy
=
stat_fuse_legacy
s1
s23__n
f
in
if
not
(
equal
stat
legacy
)
then
failwith
(
Printf
.
sprintf
"stat_fuse_new: %s <> %s!"
(
stat_to_string
stat
)
(
stat_to_string
legacy
))
end
(* \ldots{} do it, but only when we are writing debugging output. *)
let
stat_fuse_new_logging
flines
s1
s23__n
f
=
let
stat
=
stat_fuse_new
flines
s1
s23__n
f
in
Printf
.
eprintf
"stat_fuse_new: %s: %s -> %s = %s
\n
"
(
UFO_Lorentz
.
fermion_lines_to_string
flines
)
(
ThoList
.
to_string
stat_to_string
(
s1
::
s23__n
))
(
stat_to_string
stat
)
(
M
.
flavor_to_string
f
);
stat_fuse_new_check
stat
flines
s1
s23__n
f
;
stat
let
stat_fuse_new
=
majorana_log
stat_fuse_new
stat_fuse_new_logging
(* Use [stat_fuse_new], whenever fermion connections are
available. NB: [Some []] is \emph{not} the same as [None]! *)
let
stat_fuse
flines_opt
slist
f
=
match
slist
with
|
[]
->
invalid_arg
"stat_fuse: empty"
|
s1
::
s23__n
->
constrain_stat_fusion
(
match
flines_opt
with
|
Some
flines
->
stat_fuse_new
flines
s1
s23__n
f
|
None
->
stat_fuse_legacy
s1
s23__n
f
)
f
let
stat_fuse_logging
flines_opt
slist
f
=
let
stat
=
stat_fuse
flines_opt
slist
f
in
Printf
.
eprintf
"stat_fuse: %s -> %s = %s
\n
"
(
ThoList
.
to_string
stat_to_string
slist
)
(
stat_to_string
stat
)
(
M
.
flavor_to_string
f
);
stat
let
stat_fuse
=
majorana_log
stat_fuse
stat_fuse_logging
(* \thocwmodulesubsection{Final Step using Implied Fermion Connections} *)
let
stat_keystone_legacy
s1
s23__n
f
=
stat_fuse_legacy
s1
s23__n
f
let
stat_keystone_legacy_logging
s1
s23__n
f
=
let
s
=
stat_keystone_legacy
s1
s23__n
f
in
Printf
.
eprintf
"stat_keystone_legacy: %s (%s) %s -> %s
\n
"
(
stat_to_string
s1
)
(
M
.
flavor_to_string
f
)
(
ThoList
.
to_string
stat_to_string
s23__n
)
(
stat_to_string
s
);
s
let
stat_keystone_legacy
=
majorana_log
stat_keystone_legacy
stat_keystone_legacy_logging
(* \thocwmodulesubsection{Final Step using Explicit Fermion Connections} *)
let
stat_keystone_new
flines
slist
f
=
match
slist
with
|
[]
->
invalid_arg
"stat_keystone: empty"
|
[
s
]
->
invalid_arg
"stat_keystone: singleton"
|
s1
::
s2
::
s34__n
->
let
stat
=
stat_fuse_pair_unconstrained
s1
(
stat_fuse_new
flines
s2
s34__n
f
)
in
if
saturated
stat
then
stat
else
failwith
(
Printf
.
sprintf
"stat_keystone: incomplete %s!"
(
stat_to_string
stat
))
let
stat_keystone_new_check
stat
slist
f
=
match
slist
with
|
[]
->
invalid_arg
"stat_keystone_check: empty"
|
s1
::
s23__n
->
let
legacy
=
stat_keystone_legacy
s1
s23__n
f
in
if
not
(
equal
stat
legacy
)
then
failwith
(
Printf
.
sprintf
"stat_keystone_check: %s <> %s!"
(
stat_to_string
stat
)
(
stat_to_string
legacy
))
let
stat_keystone
flines_opt
slist
f
=
match
flines_opt
with
|
Some
flines
->
stat_keystone_new
flines
slist
f
|
None
->
begin
match
slist
with
|
[]
->
invalid_arg
"stat_keystone: empty"
|
s1
::
s23__n
->
stat_keystone_legacy
s1
s23__n
f
end
let
stat_keystone_logging
flines_opt
slist
f
=
let
stat
=
stat_keystone
flines_opt
slist
f
in
Printf
.
eprintf
"stat_keystone: %s (%s) %s -> %s
\n
"
(
stat_to_string
(
List
.
hd
slist
))
(
M
.
flavor_to_string
f
)
(
ThoList
.
to_string
stat_to_string
(
List
.
tl
slist
))
(
stat_to_string
stat
);
stat_keystone_new_check
stat
slist
f
;
stat
let
stat_keystone
=
majorana_log
stat_keystone
stat_keystone_logging
(* Force the legacy version w/o checking against the
new implementation for comparing generated code
against the hard coded models: *)
let
stat_fuse
flines_opt
slist
f
=
if
force_legacy
then
stat_fuse_legacy
(
List
.
hd
slist
)
(
List
.
tl
slist
)
f
else
stat_fuse
flines_opt
slist
f
let
stat_keystone
flines_opt
slist
f
=
if
force_legacy
then
stat_keystone_legacy
(
List
.
hd
slist
)
(
List
.
tl
slist
)
f
else
stat_keystone
flines_opt
slist
f
(* \thocwmodulesubsection{Evaluate Signs from Fermion Permuations} *)
let
stat_sign
=
function
|
Boson
lines
->
sign_of_permutation
lines
|
Fermion
(
p
,
lines
)
->
sign_of_permutation
(
p
::
lines
)
|
AntiFermion
(
pbar
,
lines
)
->
sign_of_permutation
(
pbar
::
lines
)
|
Majorana
(
pm
,
lines
)
->
sign_of_permutation
(
pm
::
lines
)
let
stat_sign_logging
stat
=
let
sign
=
stat_sign
stat
in
Printf
.
eprintf
"stat_sign: %s -> %d
\n
"
(
stat_to_string
stat
)
sign
;
sign
let
stat_sign
=
majorana_log
stat_sign
stat_sign_logging
end
module
Binary_Majorana
=
Make
(
Tuple
.
Binary
)(
Stat_Majorana
)(
Topology
.
Binary
)
module
Nary
(
B
:
Tuple
.
Bound
)
=
Make
(
Tuple
.
Nary
(
B
))(
Stat_Dirac
)(
Topology
.
Nary
(
B
))
module
Nary_Majorana
(
B
:
Tuple
.
Bound
)
=
Make
(
Tuple
.
Nary
(
B
))(
Stat_Majorana
)(
Topology
.
Nary
(
B
))
module
Mixed23
=
Make
(
Tuple
.
Mixed23
)(
Stat_Dirac
)(
Topology
.
Mixed23
)
module
Mixed23_Majorana
=
Make
(
Tuple
.
Mixed23
)(
Stat_Majorana
)(
Topology
.
Mixed23
)
module
Helac
(
B
:
Tuple
.
Bound
)
=
Make
(
Tuple
.
Nary
(
B
))(
Stat_Dirac
)(
Topology
.
Helac
(
B
))
module
Helac_Majorana
(
B
:
Tuple
.
Bound
)
=
Make
(
Tuple
.
Nary
(
B
))(
Stat_Majorana
)(
Topology
.
Helac
(
B
))
module
B2
=
struct
let
max_arity
()
=
2
end
module
B3
=
struct
let
max_arity
()
=
3
end
module
Helac_Binary
=
Helac
(
B2
)
module
Helac_Binary_Majorana
=
Helac
(
B2
)
module
Helac_Mixed23
=
Helac
(
B3
)
module
Helac_Mixed23_Majorana
=
Helac
(
B3
)
(* \thocwmodulesection{Multiple Amplitudes} *)
module
type
Multi
=
sig
exception
Mismatch
val
options
:
Options
.
t
type
flavor
type
process
=
flavor
list
*
flavor
list
type
amplitude
type
fusion
type
wf
type
exclusions
val
no_exclusions
:
exclusions
type
selectors
type
amplitudes
val
amplitudes
:
bool
->
int
option
->
exclusions
->
selectors
->
process
list
->
amplitudes
val
empty
:
amplitudes
(*i
val initialize_cache : string -> unit
val set_cache_name : string -> unit
i*)
val
flavors
:
amplitudes
->
process
list
val
vanishing_flavors
:
amplitudes
->
process
list
val
color_flows
:
amplitudes
->
Color
.
Flow
.
t
list
val
helicities
:
amplitudes
->
(
int
list
*
int
list
)
list
val
processes
:
amplitudes
->
amplitude
list
val
process_table
:
amplitudes
->
amplitude
option
array
array
val
fusions
:
amplitudes
->
(
fusion
*
amplitude
)
list
val
multiplicity
:
amplitudes
->
wf
->
int
val
dictionary
:
amplitudes
->
amplitude
->
wf
->
int
val
color_factors
:
amplitudes
->
Color
.
Flow
.
factor
array
array
val
constraints
:
amplitudes
->
string
option
end
module
type
Multi_Maker
=
functor
(
Fusion_Maker
:
Maker
)
->
functor
(
P
:
Momentum
.
T
)
->
functor
(
M
:
Model
.
T
)
->
Multi
with
type
flavor
=
M
.
flavor
and
type
amplitude
=
Fusion_Maker
(
P
)(
M
).
amplitude
and
type
fusion
=
Fusion_Maker
(
P
)(
M
).
fusion
and
type
wf
=
Fusion_Maker
(
P
)(
M
).
wf
and
type
selectors
=
Fusion_Maker
(
P
)(
M
).
selectors
module
Multi
(
Fusion_Maker
:
Maker
)
(
P
:
Momentum
.
T
)
(
M
:
Model
.
T
)
=
struct
exception
Mismatch
type
progress_mode
=
|
Quiet
|
Channel
of
out_channel
|
File
of
string
let
progress_option
=
ref
Quiet
module
CM
=
Colorize
.
It
(
M
)
module
F
=
Fusion_Maker
(
P
)(
M
)
module
C
=
Cascade
.
Make
(
M
)(
P
)
(* \begin{dubious}
A kludge, at best \ldots
\end{dubious} *)
let
options
=
Options
.
extend
F
.
options
[
"progress"
,
Arg
.
Unit
(
fun
()
->
progress_option
:=
Channel
stderr
),
"report progress to the standard error stream"
;
"progress_file"
,
Arg
.
String
(
fun
s
->
progress_option
:=
File
s
),
"report progress to a file"
]
type
flavor
=
M
.
flavor
type
p
=
F
.
p
type
process
=
flavor
list
*
flavor
list
type
amplitude
=
F
.
amplitude
type
fusion
=
F
.
fusion
type
wf
=
F
.
wf
type
exclusions
=
F
.
exclusions
let
no_exclusions
=
F
.
no_exclusions
type
selectors
=
F
.
selectors
type
flavors
=
flavor
list
array
type
helicities
=
int
list
array
type
colors
=
Color
.
Flow
.
t
array
type
amplitudes'
=
amplitude
array
array
array
type
amplitudes
=
{
flavors
:
process
list
;
vanishing_flavors
:
process
list
;
color_flows
:
Color
.
Flow
.
t
list
;
helicities
:
(
int
list
*
int
list
)
list
;
processes
:
amplitude
list
;
process_table
:
amplitude
option
array
array
;
fusions
:
(
fusion
*
amplitude
)
list
;
multiplicity
:
(
wf
->
int
);
dictionary
:
(
amplitude
->
wf
->
int
);
color_factors
:
Color
.
Flow
.
factor
array
array
;
constraints
:
string
option
}
let
flavors
a
=
a
.
flavors
let
vanishing_flavors
a
=
a
.
vanishing_flavors
let
color_flows
a
=
a
.
color_flows
let
helicities
a
=
a
.
helicities
let
processes
a
=
a
.
processes
let
process_table
a
=
a
.
process_table
let
fusions
a
=
a
.
fusions
let
multiplicity
a
=
a
.
multiplicity
let
dictionary
a
=
a
.
dictionary
let
color_factors
a
=
a
.
color_factors
let
constraints
a
=
a
.
constraints
let
sans_colors
f
=
List
.
map
CM
.
flavor_sans_color
f
let
colors
(
fin
,
fout
)
=
List
.
map
M
.
color
(
fin
@
fout
)
let
process_sans_color
a
=
(
sans_colors
(
F
.
incoming
a
),
sans_colors
(
F
.
outgoing
a
))
let
color_flow
a
=
CM
.
flow
(
F
.
incoming
a
)
(
F
.
outgoing
a
)
let
process_to_string
fin
fout
=
String
.
concat
" "
(
List
.
map
M
.
flavor_to_string
fin
)
^
" -> "
^
String
.
concat
" "
(
List
.
map
M
.
flavor_to_string
fout
)
let
count_processes
colored_processes
=
List
.
length
colored_processes
module
FMap
=
Map
.
Make
(
struct
type
t
=
process
let
compare
=
compare
end
)
module
CMap
=
Map
.
Make
(
struct
type
t
=
Color
.
Flow
.
t
let
compare
=
compare
end
)
(* Recently [Product.list] began to guarantee lexicographic order for sorted
arguments. Anyway, we still force a lexicographic order. *)
let
rec
order_spin_table1
s1
s2
=
match
s1
,
s2
with
|
h1
::
t1
,
h2
::
t2
->
let
c
=
compare
h1
h2
in
if
c
<>
0
then
c
else
order_spin_table1
t1
t2
|
[]
,
[]
->
0
|
_
->
invalid_arg
"order_spin_table: inconsistent lengths"
let
order_spin_table
(
s1_in
,
s1_out
)
(
s2_in
,
s2_out
)
=
let
c
=
compare
s1_in
s2_in
in
if
c
<>
0
then
c
else
order_spin_table1
s1_out
s2_out
let
sort_spin_table
table
=
List
.
sort
order_spin_table
table
let
id
x
=
x
let
pair
x
y
=
(
x
,
y
)
(* \begin{dubious}
Improve support for on shell Ward identities: [Coupling.Vector -> [4]] for one
and only one external vector.
\end{dubious} *)
let
rec
hs_of_lorentz
=
function
|
Coupling
.
Scalar
->
[
0
]
|
Coupling
.
Spinor
|
Coupling
.
ConjSpinor
|
Coupling
.
Majorana
|
Coupling
.
Maj_Ghost
->
[-
1
;
1
]
|
Coupling
.
Vector
->
[-
1
;
1
]
|
Coupling
.
Massive_Vector
->
[-
1
;
0
;
1
]
|
Coupling
.
Tensor_1
->
[-
1
;
0
;
1
]
|
Coupling
.
Vectorspinor
->
[-
2
;
-
1
;
1
;
2
]
|
Coupling
.
Tensor_2
->
[-
2
;
-
1
;
0
;
1
;
2
]
|
Coupling
.
BRS
f
->
hs_of_lorentz
f
let
hs_of_flavor
f
=
hs_of_lorentz
(
M
.
lorentz
f
)
let
hs_of_flavors
(
fin
,
fout
)
=
(
List
.
map
hs_of_flavor
fin
,
List
.
map
hs_of_flavor
fout
)
let
rec
unphysical_of_lorentz
=
function
|
Coupling
.
Vector
->
[
4
]
|
Coupling
.
Massive_Vector
->
[
4
]
|
_
->
invalid_arg
"unphysical_of_lorentz: not a vector particle"
let
unphysical_of_flavor
f
=
unphysical_of_lorentz
(
M
.
lorentz
f
)
let
unphysical_of_flavors1
n
f_list
=
ThoList
.
mapi
(
fun
i
f
->
if
i
=
n
then
unphysical_of_flavor
f
else
hs_of_flavor
f
)
1
f_list
let
unphysical_of_flavors
n
(
fin
,
fout
)
=
(
unphysical_of_flavors1
n
fin
,
unphysical_of_flavors1
(
n
-
List
.
length
fin
)
fout
)
let
helicity_table
unphysical
flavors
=
let
hs
=
begin
match
unphysical
with
|
None
->
List
.
map
hs_of_flavors
flavors
|
Some
n
->
List
.
map
(
unphysical_of_flavors
n
)
flavors
end
in
if
not
(
ThoList
.
homogeneous
hs
)
then
invalid_arg
"Fusion.helicity_table: not all flavors have the same helicity states!"
else
match
hs
with
|
[]
->
[]
|
(
hs_in
,
hs_out
)
::
_
->
sort_spin_table
(
Product
.
list2
pair
(
Product
.
list
id
hs_in
)
(
Product
.
list
id
hs_out
))
module
Proc
=
Process
.
Make
(
M
)
module
WFMap
=
Map
.
Make
(
struct
type
t
=
F
.
wf
let
compare
=
compare
end
)
module
WFSet2
=
Set
.
Make
(
struct
type
t
=
F
.
wf
*
(
F
.
wf
,
F
.
coupling
)
Tree2
.
t
let
compare
=
compare
end
)
module
WFMap2
=
Map
.
Make
(
struct
type
t
=
F
.
wf
*
(
F
.
wf
,
F
.
coupling
)
Tree2
.
t
let
compare
=
compare
end
)
module
WFTSet
=
Set
.
Make
(
struct
type
t
=
(
F
.
wf
,
F
.
coupling
)
Tree2
.
t
let
compare
=
compare
end
)
(* All wavefunctions are unique per amplitude. So we can use per-amplitude
dependency trees without additional \emph{internal} tags to identify identical
wave functions. *)
(* \textbf{NB:} we miss potential optimizations, because we assume all coupling to
be different, while in fact we have horizontal/family symmetries and non abelian
gauge couplings are universal anyway. *)
let
disambiguate_fusions
amplitudes
=
let
fusions
=
ThoList
.
flatmap
(
fun
amplitude
->
List
.
map
(
fun
fusion
->
(
fusion
,
F
.
dependencies
amplitude
(
F
.
lhs
fusion
)))
(
F
.
fusions
amplitude
))
amplitudes
in
let
duplicates
=
List
.
fold_left
(
fun
map
(
fusion
,
dependencies
)
->
let
wf
=
F
.
lhs
fusion
in
let
set
=
try
WFMap
.
find
wf
map
with
Not_found
->
WFTSet
.
empty
in
WFMap
.
add
wf
(
WFTSet
.
add
dependencies
set
)
map
)
WFMap
.
empty
fusions
in
let
multiplicity_map
=
WFMap
.
fold
(
fun
wf
dependencies
acc
->
let
cardinal
=
WFTSet
.
cardinal
dependencies
in
if
cardinal
<=
1
then
acc
else
WFMap
.
add
wf
cardinal
acc
)
duplicates
WFMap
.
empty
and
dictionary_map
=
WFMap
.
fold
(
fun
wf
dependencies
acc
->
let
cardinal
=
WFTSet
.
cardinal
dependencies
in
if
cardinal
<=
1
then
acc
else
snd
(
WFTSet
.
fold
(
fun
dependency
(
i'
,
acc'
)
->
(
succ
i'
,
WFMap2
.
add
(
wf
,
dependency
)
i'
acc'
))
dependencies
(
1
,
acc
)))
duplicates
WFMap2
.
empty
in
let
multiplicity
wf
=
WFMap
.
find
wf
multiplicity_map
and
dictionary
amplitude
wf
=
WFMap2
.
find
(
wf
,
F
.
dependencies
amplitude
wf
)
dictionary_map
in
(
multiplicity
,
dictionary
)
let
eliminate_common_fusions1
seen_wfs
amplitude
=
List
.
fold_left
(
fun
(
seen
,
acc
)
f
->
let
wf
=
F
.
lhs
f
in
let
dependencies
=
F
.
dependencies
amplitude
wf
in
if
WFSet2
.
mem
(
wf
,
dependencies
)
seen
then
(
seen
,
acc
)
else
(
WFSet2
.
add
(
wf
,
dependencies
)
seen
,
(
f
,
amplitude
)
::
acc
))
seen_wfs
(
F
.
fusions
amplitude
)
let
eliminate_common_fusions
processes
=
let
_,
rev_fusions
=
List
.
fold_left
eliminate_common_fusions1
(
WFSet2
.
empty
,
[]
)
processes
in
List
.
rev
rev_fusions
(*i
let eliminate_common_fusions processes =
ThoList.flatmap
(fun amplitude ->
(List.map (fun f -> (f, amplitude)) (F.fusions amplitude)))
processes
i*)
(* \thocwmodulesubsection{Calculate All The Amplitudes} *)
let
amplitudes
goldstones
unphysical
exclusions
select_wf
processes
=
(* \begin{dubious}
Eventually, we might want to support inhomogeneous helicities. However,
this makes little physics sense for external particles on the mass shell,
unless we have a model with degenerate massive fermions and bosons.
\end{dubious} *)
if
not
(
ThoList
.
homogeneous
(
List
.
map
hs_of_flavors
processes
))
then
invalid_arg
"Fusion.Multi.amplitudes: incompatible helicities"
;
let
unique_uncolored_processes
=
Proc
.
remove_duplicate_final_states
(
C
.
partition
select_wf
)
processes
in
let
progress
=
match
!
progress_option
with
|
Quiet
->
Progress
.
dummy
|
Channel
oc
->
Progress
.
channel
oc
(
count_processes
unique_uncolored_processes
)
|
File
name
->
Progress
.
file
name
(
count_processes
unique_uncolored_processes
)
in
let
allowed
=
ThoList
.
flatmap
(
fun
(
fi
,
fo
)
->
Progress
.
begin_step
progress
(
process_to_string
fi
fo
);
let
amps
=
F
.
amplitudes
goldstones
exclusions
select_wf
fi
fo
in
begin
match
amps
with
|
[]
->
Progress
.
end_step
progress
"forbidden"
|
_
->
Progress
.
end_step
progress
"allowed"
end
;
amps
)
unique_uncolored_processes
in
Progress
.
summary
progress
"all processes done"
;
let
color_flows
=
ThoList
.
uniq
(
List
.
sort
compare
(
List
.
map
color_flow
allowed
))
and
flavors
=
ThoList
.
uniq
(
List
.
sort
compare
(
List
.
map
process_sans_color
allowed
))
in
let
vanishing_flavors
=
Proc
.
diff
processes
flavors
in
let
helicities
=
helicity_table
unphysical
flavors
in
let
f_index
=
fst
(
List
.
fold_left
(
fun
(
m
,
i
)
f
->
(
FMap
.
add
f
i
m
,
succ
i
))
(
FMap
.
empty
,
0
)
flavors
)
and
c_index
=
fst
(
List
.
fold_left
(
fun
(
m
,
i
)
c
->
(
CMap
.
add
c
i
m
,
succ
i
))
(
CMap
.
empty
,
0
)
color_flows
)
in
let
table
=
Array
.
make_matrix
(
List
.
length
flavors
)
(
List
.
length
color_flows
)
None
in
List
.
iter
(
fun
a
->
let
f
=
FMap
.
find
(
process_sans_color
a
)
f_index
and
c
=
CMap
.
find
(
color_flow
a
)
c_index
in
table
.(
f
).(
c
)
<-
Some
(
a
))
allowed
;
let
cf_array
=
Array
.
of_list
color_flows
in
let
ncf
=
Array
.
length
cf_array
in
let
color_factor_table
=
Array
.
make_matrix
ncf
ncf
Color
.
Flow
.
zero
in
for
i
=
0
to
pred
ncf
do
for
j
=
0
to
i
do
color_factor_table
.(
i
).(
j
)
<-
Color
.
Flow
.
factor
cf_array
.(
i
)
cf_array
.(
j
);
color_factor_table
.(
j
).(
i
)
<-
color_factor_table
.(
i
).(
j
)
done
done
;
let
fusions
=
eliminate_common_fusions
allowed
and
multiplicity
,
dictionary
=
disambiguate_fusions
allowed
in
{
flavors
=
flavors
;
vanishing_flavors
=
vanishing_flavors
;
color_flows
=
color_flows
;
helicities
=
helicities
;
processes
=
allowed
;
process_table
=
table
;
fusions
=
fusions
;
multiplicity
=
multiplicity
;
dictionary
=
dictionary
;
color_factors
=
color_factor_table
;
constraints
=
C
.
description
select_wf
}
(*i
let initialize_cache = F.initialize_cache
let set_cache_name = F.set_cache_name
i*)
let
empty
=
{
flavors
=
[]
;
vanishing_flavors
=
[]
;
color_flows
=
[]
;
helicities
=
[]
;
processes
=
[]
;
process_table
=
Array
.
make_matrix
0
0
None
;
fusions
=
[]
;
multiplicity
=
(
fun
_
->
1
);
dictionary
=
(
fun
_
_
->
1
);
color_factors
=
Array
.
make_matrix
0
0
Color
.
Flow
.
zero
;
constraints
=
None
}
end
File Metadata
Details
Attached
Mime Type
text/x-ruby
Expires
Wed, May 14, 11:47 AM (2 h, 2 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5111517
Default Alt Text
fusion.ml (129 KB)
Attached To
rWHIZARDSVN whizardsvn
Event Timeline
Log In to Comment