Skip to content

Commit

Permalink
Add fermion exchange logic and tests for bhabha scattering (#37)
Browse files Browse the repository at this point in the history
* Add fermion exchange logic and tests for Bhabha scattering

* WIP

* Working until n=3, generation still seems broken

* WIP

* Pass sanity checks

* Some beautification

* Delete previous generation and exports

* Add more docs and refs

* Add number of diagrams test

* Improve manual and number_of_diagrams docs

* Self-review
  • Loading branch information
AntonReinhard authored Jan 2, 2025
1 parent dd3c037 commit 3420bde
Show file tree
Hide file tree
Showing 20 changed files with 883 additions and 867 deletions.
8 changes: 3 additions & 5 deletions docs/src/examples/compton.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ proc = ScatteringProcess(
(AllSpin(), AllPol()), # outgoing particle spin/pols
)

# The [`feynman_diagrams`](@ref) function returns an iterator for all possible Feynman diagrams
# for this scattering process. With its `length` overload, we can check how many diagrams
# there are. For an n-photon Compton process with `n` incoming photons, this should be
# $(n+1)!$.
length(feynman_diagrams(proc))
# The [`number_of_diagrams`](@ref) function returns how many diagrams there are for a given process.
# For an n-photon Compton process with `n` incoming photons, this should be $(n+1)!$.
number_of_diagrams(proc)

# Next, we can generate the DAG representing the computation for our scattering process'
# squared matrix element. This uses [`ComputableDAGs.jl`](https://github.com/ComputableDAGs/ComputableDAGs.jl).
Expand Down
7 changes: 3 additions & 4 deletions docs/src/examples/trident.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ proc = ScatteringProcess(
(SpinUp(), ntuple(_ -> SpinUp(), 2 * n)...), # outgoing particle spin/pols
)

# The [`feynman_diagrams`](@ref) function returns an iterator for all possible Feynman diagrams
# for this scattering process. With its `length` overload, we can check how many diagrams
# there are.
length(feynman_diagrams(proc))
# The [`number_of_diagrams`](@ref) function returns how many valid Feynman diagrams
# there are for a given process.
number_of_diagrams(proc)

# Next, we can generate the DAG representing the computation for our scattering process'
# squared matrix element. This uses `ComputableDAGs.jl`.
Expand Down
9 changes: 6 additions & 3 deletions docs/src/lib/internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ Pages = ["internal.md"]
## Types

```@docs
AbstractTreeLevelFeynmanDiagram
VirtualParticle
```

## Functions

```@docs
_is_index_valid_combination
_pseudo_virtual_particles
virtual_particles
particle_pairs
total_particle_triples
are_total
contains
disjunct
make_up
_is_index_valid_combination
_pseudo_virtual_particles
```
12 changes: 1 addition & 11 deletions docs/src/lib/public.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,8 @@
Pages = ["public.md"]
```

## Types

```@docs
FeynmanDiagram
VirtualParticle
```

## Functions
```@docs
external_particles
feynman_diagrams
graph
process
virtual_particles
number_of_diagrams
```
14 changes: 11 additions & 3 deletions docs/src/manual.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
# Manual

TBW
## Usage

![Virtual Particle Currents](VPC_explanation.svg)
!!! note
This project uses [ComputableDAGs.jl](https://github.com/ComputableDAGs/ComputableDAGs.jl), which uses [RuntimeGeneratedFunctions.jl](https://github.com/SciML/RuntimeGeneratedFunctions.jl). The latter requires an initialization step, so when using this project, make sure to have
```julia
using RuntimeGeneratedFunctions
RuntimeGeneratedFunctions.init(@__MODULE__)
```
somewhere in the preamble of your code or at the beginning of your REPL session. Otherwise the function generation will not work.

For a usage example, please refer to the notebooks in the `./notebooks` directory of the repository.
This package currently exports two functions, [`graph`](@ref) and [`number_of_diagrams`](@ref). Both of these take a [`QEDbase.AbstractProcessDefinition`](@extref) as input. `graph` returns a [`ComputableDAGs.DAG`](@extref) representing the computation of the squared matrix element for [`QEDcore.PhaseSpacePoint`](@extref)s. `number_of_diagrams` is a utility function that returns the number of valid tree-level Feynman diagrams for the given process.

For usage examples, please refer to the examples section of the docs.
15 changes: 7 additions & 8 deletions src/QEDFeynmanDiagrams.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@ module QEDFeynmanDiagrams
using Reexport
@reexport using QEDbase
@reexport using QEDcore
@reexport using ComputableDAGs

using ComputableDAGs
using Combinatorics
using LRUCache
using Memoization
using DataStructures

export FeynmanDiagram, VirtualParticle
export feynman_diagrams
export external_particles, virtual_particles, process, graph
export graph, number_of_diagrams

include("flat_matrix.jl")

include("diagrams/labelled_plane_trees.jl")
include("diagrams/interface.jl")
include("diagrams/virtual_particle.jl")
include("diagrams/vp_utils.jl")
include("diagrams/diagrams.jl")

include("computable_dags/compute.jl")
include("computable_dags/generation.jl")
include("computable_dags/fermion_sign.jl")
include("computable_dags/indexing.jl")
include("computable_dags/utils.jl")

end # module QEDFeynmanDiagrams
34 changes: 31 additions & 3 deletions src/computable_dags/compute.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
struct ComputeTask_BaseState <: AbstractComputeTask end # calculate the base state of an external particle
struct ComputeTask_Propagator <: AbstractComputeTask end # calculate the propagator term of a virtual particle
struct ComputeTask_Pair <: AbstractComputeTask end # from a pair of virtual particle currents, calculate the product
struct ComputeTask_PairNegated <: AbstractComputeTask end # same as Pair, but multiplies the result by -1
struct ComputeTask_CollectPairs <: AbstractComputeTask # for a list of virtual particle current pair products, sum
children::Int
end
struct ComputeTask_PropagatePairs <: AbstractComputeTask end # for the result of a CollectPairs compute task and a propagator, propagate the sum
struct ComputeTask_Triple <: AbstractComputeTask end # from a triple of virtual particle currents, calculate the diagram result
struct ComputeTask_TripleNegated <: AbstractComputeTask end # same as Triple, but multiplies the result by -1
struct ComputeTask_CollectTriples <: AbstractComputeTask # sum over triples results and
children::Int
end
Expand All @@ -22,18 +24,22 @@ const VERTEX = -1im * e * gamma()
compute_effort(::ComputeTask_BaseState) = 0
compute_effort(::ComputeTask_Propagator) = 0
compute_effort(::ComputeTask_Pair) = 0
compute_effort(::ComputeTask_PairNegated) = 0
compute_effort(::ComputeTask_CollectPairs) = 0
compute_effort(::ComputeTask_PropagatePairs) = 0
compute_effort(::ComputeTask_Triple) = 0
compute_effort(::ComputeTask_TripleNegated) = 0
compute_effort(::ComputeTask_CollectTriples) = 0
compute_effort(::ComputeTask_SpinPolCumulation) = 0

children(::ComputeTask_BaseState) = 1
children(::ComputeTask_Propagator) = 1
children(::ComputeTask_Pair) = 2
children(::ComputeTask_PairNegated) = 2
children(t::ComputeTask_CollectPairs) = t.children
children(::ComputeTask_PropagatePairs) = 2
children(::ComputeTask_Triple) = 3
children(::ComputeTask_TripleNegated) = 3
children(t::ComputeTask_CollectTriples) = t.children
children(t::ComputeTask_SpinPolCumulation) = t.children

Expand Down Expand Up @@ -105,6 +111,12 @@ end
@inline function Base.:+(a::Unpropagated{P,V}, b::Unpropagated{P,V}) where {P,V}
return Unpropagated(a.particle, a.value + b.value)
end
@inline function Base.:*(z::Number, a::Unpropagated{P,V}) where {P,V}
return Unpropagated(a.particle, z * a.value)
end
@inline function Base.:*(a::Unpropagated{P,V}, z::Number) where {P,V}
return Unpropagated(a.particle, z * a.value)
end

struct Propagated{PARTICLE_T<:AbstractParticleType,VALUE_T}
particle::PARTICLE_T
Expand Down Expand Up @@ -133,6 +145,12 @@ end
return Unpropagated(Photon(), positron.value * VERTEX * electron.value) # electron - positron -> photon
end

@inline function compute(
::ComputeTask_PairNegated, v1::Propagated{P1}, v2::Propagated{P2}
) where {P1,P2}
return -1 * compute(ComputeTask_Pair(), v1, v2)
end

@inline function compute(::ComputeTask_PropagatePairs, prop, photon::Unpropagated{Photon})
return Propagated(Photon(), photon.value * prop)
end
Expand All @@ -155,13 +173,23 @@ end
)
return positron.value * (VERTEX * photon.value) * electron.value
end
@inline function compute(
::ComputeTask_TripleNegated,
photon::Propagated{Photon},
electron::Propagated{Electron},
positron::Propagated{Positron},
)
return -1 * positron.value * (VERTEX * photon.value) * electron.value
end

# this compiles in a reasonable amount of time for up to about 1e4 parameters
# use a summation algorithm with more accuracy and/or parallelization
@inline function compute(::ComputeTask_CollectPairs, args::Vararg{N,T}) where {N,T}
# TODO: use a summation algorithm with more accuracy and/or parallelization
function compute(::ComputeTask_CollectPairs, args::Vararg{N,T}) where {N,T}
return sum(args)
end
@inline function compute(::ComputeTask_CollectTriples, args::Vararg{N,T}) where {N,T}
function compute(::ComputeTask_CollectTriples, args::Vararg{N,T}) where {N,T}
#println("$([args...])")
#println("$(sum(args))")
return sum(args)
end
function compute(::ComputeTask_SpinPolCumulation, args::Vararg{N,T}) where {N,T}
Expand Down
62 changes: 62 additions & 0 deletions src/computable_dags/fermion_sign.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""
relative_sign_pair(p1::VirtualParticle, p2::VirtualParticle)
Returns true if the Pair task combining p1 and p2 into res needs a relative sign in the DAG.
"""
function relative_sign_pair(p1::VirtualParticle, p2::VirtualParticle)
if (p1.species == Photon || p2.species == Photon)
#@info "P [FALS] relative sign of $p1 + $p2 -> $res"
return false
end

local vec::Vector{OPEN_FERMION_CYCLE_T}

(l, el) = if p1.species == Electron
_canonical_index(p1)
elseif p2.species == Electron
_canonical_index(p2)
end
(r, po) = if p1.species == Positron
_canonical_index(p1)
elseif p2.species == Positron
_canonical_index(p2)
end

vec = [p1.open_cycles..., p2.open_cycles..., (el, po)]

n = _count_closed_cycles(vec)

return n % 2 == 1
end

"""
relative_sign_triple(p2::VirtualParticle, p3::VirtualParticle, p3::VirtualParticle)
Returns true if the Triple task combining electron p1, and positron p2 into a full diagram family needs a relative sign in the DAG.
"""
function relative_sign_triple(p1::VirtualParticle, p2::VirtualParticle, p3::VirtualParticle)
# p1 + p2 is the photon
n1 = relative_sign_pair(p1, p2)

new_cycle = (_canonical_index(p1)[2], _canonical_index(p2)[2])

res = VirtualParticle(
p1.proc,
Photon(),
(_contributions(p1) + _contributions(p2))...,
sort(
reduce_cycles(
OPEN_FERMION_CYCLE_T[p1.open_cycles..., p2.open_cycles..., new_cycle]
),
),
)

# cycles closed?
closed_cycle_count = _count_closed_cycles(
OPEN_FERMION_CYCLE_T[p3.open_cycles..., res.open_cycles...]
)

n2 = closed_cycle_count % 2 == 1

return n1 != n2 # xor
end
Loading

0 comments on commit 3420bde

Please sign in to comment.