Compare commits

..

7 Commits

Author SHA1 Message Date
92f534f6bf Fix congruent ph script
Some checks failed
MetagraphOptimization_CI / test (push) Failing after 1m35s
MetagraphOptimization_CI / docs (push) Failing after 1m39s
2024-07-04 17:03:18 +02:00
55501c15c8 WIP 2024-07-04 15:41:13 +02:00
b5d92b729c Get compute function working
Some checks failed
MetagraphOptimization_CI / docs (push) Failing after 1m32s
MetagraphOptimization_CI / test (push) Failing after 1m49s
2024-07-04 15:31:22 +02:00
6a9a7b41f1 rework a lot of the QED model to use QEDcore/base/processes 2024-07-03 20:24:53 +02:00
a1581182ca WIP 2024-07-02 10:50:30 +02:00
1b4ba285c3 WIP refactor
Some checks failed
MetagraphOptimization_CI / test (push) Failing after 7m59s
MetagraphOptimization_CI / docs (push) Failing after 8m6s
2024-06-24 23:31:30 +02:00
2921882fd4 EOD
Some checks failed
MetagraphOptimization_CI / docs (push) Failing after 8m32s
MetagraphOptimization_CI / test (push) Failing after 11m24s
2024-05-24 19:20:59 +02:00
39 changed files with 1152 additions and 1335 deletions

View File

@ -19,12 +19,17 @@ QEDprocesses = "46de9c38-1bb3-4547-a1ec-da24d767fdad"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
oneAPI = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b"
[extras]
CUDA_Runtime_jll = "76a88914-d11a-5bdc-97e0-2f5a05c973a2"
QEDbase = "10e22c08-3ccb-4172-bfcf-7d7aa3d04d93"
QEDcore = "35dc0263-cb5f-4c33-a114-1d7f54ab753e"
QEDprocesses = "46de9c38-1bb3-4547-a1ec-da24d767fdad"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[targets]
test = ["Test"]
test = ["SafeTestsets", "Test", "QEDbase", "QEDcore", "QEDprocesses"]

View File

@ -1,8 +1,10 @@
using MetagraphOptimization
using QEDbase
using QEDcore
using QEDprocesses
using Random
using UUIDs
using CSV
RNG = Random.MersenneTwister(123)
@ -21,103 +23,79 @@ function mock_machine()
)
end
function congruent_input(processDescription::QEDProcessDescription, omega::Number)
function congruent_input_momenta(processDescription::GenericQEDProcess, omega::Number)
# generate an input sample for given e + nk -> e' + k' process, where the nk are equal
massSum = 0
inputMasses = Vector{Float64}()
for (particle, n) in processDescription.inParticles
for _ in 1:n
massSum += mass(particle)
push!(inputMasses, mass(particle))
end
for particle in incoming_particles(processDescription)
massSum += mass(particle)
push!(inputMasses, mass(particle))
end
outputMasses = Vector{Float64}()
for (particle, n) in processDescription.outParticles
for _ in 1:n
massSum += mass(particle)
push!(outputMasses, mass(particle))
end
for particle in outgoing_particles(processDescription)
massSum += mass(particle)
push!(outputMasses, mass(particle))
end
initialMomenta = [
i == 1 ? SFourMomentum(1, 0, 0, 0) : SFourMomentum(omega, 0, 0, omega) for
i in 1:length(inputMasses)
]
initial_momenta =
[i == 1 ? SFourMomentum(1, 0, 0, 0) : SFourMomentum(omega, 0, 0, omega) for i in 1:length(inputMasses)]
# add some extra random mass to allow for some momentum
ss = sqrt(sum(initialMomenta) * sum(initialMomenta))
ss = sqrt(sum(initial_momenta) * sum(initial_momenta))
final_momenta = MetagraphOptimization.generate_physical_massive_moms(RNG, ss, outputMasses)
result = Vector{QEDProcessInput}()
sizehint!(result, 16)
spin_pol_combinations = Iterators.product(
[SpinUp, SpinDown], [SpinUp, SpinDown], [PolX, PolY], [PolX, PolY]
)
for (in_spin, out_spin, in_pol, out_pol) in spin_pol_combinations
# get the electron first, then the n photons
particles = Vector{QEDParticle}()
for (particle, n) in processDescription.inParticles
if particle <: FermionStateful
mom = initialMomenta[1]
push!(particles, particle(mom, in_spin()))
elseif particle <: PhotonStateful
for i in 1:n
mom = initialMomenta[i + 1]
push!(particles, particle(mom, in_pol()))
end
else
@assert false
end
end
final_momenta = MetagraphOptimization.generate_physical_massive_moms(
RNG, ss, outputMasses
)
index = 1
for (particle, n) in processDescription.outParticles
for _ in 1:n
if particle <: FermionStateful
push!(particles, particle(final_momenta[index], out_spin()))
elseif particle <: PhotonStateful
push!(particles, particle(final_momenta[index], out_pol()))
end
index += 1
end
end
inFerms = MetagraphOptimization._svector_from_type(
processDescription, FermionStateful{Incoming,in_spin}, particles
)
outFerms = MetagraphOptimization._svector_from_type(
processDescription, FermionStateful{Outgoing,out_spin}, particles
)
inAntiferms = MetagraphOptimization._svector_from_type(
processDescription, AntiFermionStateful{Incoming,in_spin}, particles
)
outAntiferms = MetagraphOptimization._svector_from_type(
processDescription, AntiFermionStateful{Outgoing,out_spin}, particles
)
inPhotons = MetagraphOptimization._svector_from_type(
processDescription, PhotonStateful{Incoming,in_pol}, particles
)
outPhotons = MetagraphOptimization._svector_from_type(
processDescription, PhotonStateful{Outgoing,out_pol}, particles
)
processInput = QEDProcessInput(
processDescription,
inFerms,
outFerms,
inAntiferms,
outAntiferms,
inPhotons,
outPhotons,
)
push!(result, processInput)
end
return result
return (tuple(initial_momenta...), tuple(final_momenta...))
end
function build_psp(processDescription::GenericQEDProcess, momenta)
return PhaseSpacePoint(
processDescription,
PerturbativeQED(),
PhasespaceDefinition(SphericalCoordinateSystem(), ElectronRestFrame()),
momenta[1],
momenta[2],
)
end
n = 1000000
photons = 4
omega = 2e-3
# n is the number of incoming photons
# omega is the number
println("Generating $n inputs for $photons photons, omega=$omega...")
# temp process to generate momenta
temp_process = parse_process("k"^photons * "e->ke", QEDModel(), PolX(), SpinUp(), PolX(), SpinUp())
input_momenta = [congruent_input_momenta(temp_process, omega) for _ in 1:n]
results = [0.0im for _ in 1:n]
for (in_pol, in_spin, out_pol, out_spin) in
Iterators.product([PolX(), PolY()], [SpinUp(), SpinDown()], [PolX(), PolY()], [SpinUp(), SpinDown()])
print("Calculating for spin/pol config: $in_pol, $in_spin -> $out_pol, $out_spin... Preparing inputs... ")
process = parse_process("k"^photons * "e->ke", QEDModel(), in_pol, in_spin, out_pol, out_spin)
inputs = build_psp.(Ref(process), input_momenta)
print("Preparing graph... ")
# prepare function
graph = gen_graph(process)
optimize_to_fixpoint!(ReductionOptimizer(), graph)
print("Preparing function... ")
func = get_compute_function(graph, process, mock_machine())
print("Calculating... ")
for i in 1:n
results[i] += func(inputs[i])^2
end
println("Done.")
end
println("Writing results")
open("$(photons)_congruent_photons_omega_$(omega).csv", "w") do f
println(f, "out_photon_momentum;out_electron_momentum;result")
for (momentum, result) in Iterators.zip(input_momenta, results)
println(f, "$(momentum[2][1]);$(momentum[2][2]);$(result)")
end
end

View File

@ -413,7 +413,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Julia 1.10.2",
"display_name": "Julia 1.10.4",
"language": "julia",
"name": "julia-1.10"
},
@ -421,7 +421,7 @@
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.10.2"
"version": "1.10.4"
}
},
"nbformat": 4,

View File

@ -66,8 +66,7 @@ export ComputeTaskABC_Sum
# QED model
export FeynmanDiagram, FeynmanVertex, FeynmanTie, FeynmanParticle
export PhotonStateful, FermionStateful, AntiFermionStateful
export QEDParticle, QEDProcessDescription, QEDProcessInput, QEDModel
export GenericQEDProcess, QEDModel
export ComputeTaskQED_P
export ComputeTaskQED_S1
export ComputeTaskQED_S2
@ -116,6 +115,8 @@ import Base.delete!
import Base.insert!
import Base.collect
include("QEDprocesses_patch.jl")
include("devices/interface.jl")
include("task/type.jl")
include("node/type.jl")
@ -174,22 +175,26 @@ include("optimization/split.jl")
include("models/interface.jl")
include("models/print.jl")
include("models/abc/types.jl")
include("models/abc/particle.jl")
include("models/abc/compute.jl")
include("models/abc/create.jl")
include("models/abc/properties.jl")
include("models/abc/parse.jl")
include("models/abc/print.jl")
include("models/physics_models/interface.jl")
include("models/qed/types.jl")
include("models/qed/particle.jl")
include("models/qed/diagrams.jl")
include("models/qed/compute.jl")
include("models/qed/create.jl")
include("models/qed/properties.jl")
include("models/qed/parse.jl")
include("models/qed/print.jl")
#=
include("models/physics_models/abc/types.jl")
include("models/physics_models/abc/particle.jl")
include("models/physics_models/abc/compute.jl")
include("models/physics_models/abc/create.jl")
include("models/physics_models/abc/properties.jl")
include("models/physics_models/abc/parse.jl")
include("models/physics_models/abc/print.jl")
=#
include("models/physics_models/qed/types.jl")
include("models/physics_models/qed/particle.jl")
include("models/physics_models/qed/diagrams.jl")
include("models/physics_models/qed/compute.jl")
include("models/physics_models/qed/create.jl")
include("models/physics_models/qed/properties.jl")
include("models/physics_models/qed/parse.jl")
include("models/physics_models/qed/print.jl")
include("devices/measure.jl")
include("devices/detect.jl")
@ -198,7 +203,7 @@ include("devices/impl.jl")
include("devices/numa/impl.jl")
include("devices/cuda/impl.jl")
include("devices/rocm/impl.jl")
include("devices/oneapi/impl.jl")
#include("devices/oneapi/impl.jl")
include("scheduler/interface.jl")
include("scheduler/greedy.jl")

71
src/QEDprocesses_patch.jl Normal file
View File

@ -0,0 +1,71 @@
# patch QEDprocesses
# see issue https://github.com/QEDjl-project/QEDprocesses.jl/issues/77
@inline function QEDprocesses.number_particles(
proc_def::QEDbase.AbstractProcessDefinition,
dir::DIR,
::PT,
) where {DIR <: QEDbase.ParticleDirection, PT <: QEDbase.AbstractParticleType}
return count(x -> x isa PT, particles(proc_def, dir))
end
@inline function QEDprocesses.number_particles(
proc_def::QEDbase.AbstractProcessDefinition,
::PS,
) where {
DIR <: QEDbase.ParticleDirection,
PT <: QEDbase.AbstractParticleType,
EL <: AbstractFourMomentum,
PS <: ParticleStateful{DIR, PT, EL},
}
return QEDprocesses.number_particles(proc_def, DIR(), PT())
end
@inline function QEDprocesses.number_particles(
proc_def::QEDbase.AbstractProcessDefinition,
::Type{PS},
) where {
DIR <: QEDbase.ParticleDirection,
PT <: QEDbase.AbstractParticleType,
EL <: AbstractFourMomentum,
PS <: ParticleStateful{DIR, PT, EL},
}
return QEDprocesses.number_particles(proc_def, DIR(), PT())
end
@inline function QEDcore.ParticleStateful{DIR, SPECIES}(
mom::AbstractFourMomentum,
) where {DIR <: ParticleDirection, SPECIES <: AbstractParticleType}
return ParticleStateful(DIR(), SPECIES(), mom)
end
@inline function QEDcore.ParticleStateful{DIR, SPECIES, EL}(
mom::EL,
) where {DIR <: ParticleDirection, SPECIES <: AbstractParticleType, EL <: AbstractFourMomentum}
return ParticleStateful(DIR(), SPECIES(), mom)
end
@inline function QEDbase.momentum(
psp::AbstractPhaseSpacePoint{MODEL, PROC, PS_DEF, INT, OUTT},
dir::ParticleDirection,
species::AbstractParticleType,
n::Int,
) where {MODEL, PROC, PS_DEF, INT, OUTT}
# TODO: can be done through fancy template recursion too with 0 overhead
i = 0
c = n
for p in particles(psp, dir)
i += 1
if particle_species(p) isa typeof(species)
c -= 1
end
if c == 0
break
end
end
if c != 0 || n <= 0
throw(InvalidInputError("could not get $n-th momentum of $dir $species, does not exist"))
end
return momenta(psp, dir)[i]
end

View File

@ -1,10 +1,10 @@
"""
get_compute_function(graph::DAG, process::AbstractProcessDescription, machine::Machine)
get_compute_function(graph::DAG, instance, machine::Machine)
Return a function of signature `compute_<id>(input::AbstractProcessInput)`, which will return the result of the DAG computation on the given input.
Return a function of signature `compute_<id>(input::input_type(instance))`, which will return the result of the DAG computation on the given input.
"""
function get_compute_function(graph::DAG, process::AbstractProcessDescription, machine::Machine)
tape = gen_tape(graph, process, machine)
function get_compute_function(graph::DAG, instance, machine::Machine)
tape = gen_tape(graph, instance, machine)
initCaches = Expr(:block, tape.initCachesCode...)
assignInputs = Expr(:block, expr_from_fc.(tape.inputAssignCode)...)
@ -13,7 +13,7 @@ function get_compute_function(graph::DAG, process::AbstractProcessDescription, m
functionId = to_var_name(UUIDs.uuid1(rng[1]))
resSym = eval(gen_access_expr(entry_device(tape.machine), tape.outputSymbol))
expr = Meta.parse(
"function compute_$(functionId)(data_input::AbstractProcessInput) $(initCaches); $(assignInputs); $code; return $resSym; end",
"function compute_$(functionId)(data_input::$(input_type(instance))) $(initCaches); $(assignInputs); $code; return $resSym; end",
)
func = eval(expr)
@ -22,12 +22,12 @@ function get_compute_function(graph::DAG, process::AbstractProcessDescription, m
end
"""
get_cuda_kernel(graph::DAG, process::AbstractProcessDescription, machine::Machine)
get_cuda_kernel(graph::DAG, instance, machine::Machine)
Return a function of signature `compute_<id>(input::CuVector, output::CuVector, n::Int64)`, which will return the result of the DAG computation of the input on the given output variable.
"""
function get_cuda_kernel(graph::DAG, process::AbstractProcessDescription, machine::Machine)
tape = gen_tape(graph, process, machine)
function get_cuda_kernel(graph::DAG, instance, machine::Machine)
tape = gen_tape(graph, instance, machine)
initCaches = Expr(:block, tape.initCachesCode...)
assignInputs = Expr(:block, expr_from_fc.(tape.inputAssignCode)...)
@ -54,19 +54,19 @@ function get_cuda_kernel(graph::DAG, process::AbstractProcessDescription, machin
end
"""
execute(graph::DAG, process::AbstractProcessDescription, machine::Machine, input::AbstractProcessInput)
execute(graph::DAG, instance, machine::Machine, input)
Execute the code of the given `graph` on the given input particles.
Execute the code of the given `graph` on the given input values.
This is essentially shorthand for
```julia
tape = gen_tape(graph, process, machine)
tape = gen_tape(graph, instance, machine)
return execute_tape(tape, input)
```
See also: [`parse_dag`](@ref), [`parse_process`](@ref), [`gen_process_input`](@ref)
"""
function execute(graph::DAG, process::AbstractProcessDescription, machine::Machine, input::AbstractProcessInput)
tape = gen_tape(graph, process, machine)
function execute(graph::DAG, instance, machine::Machine, input)
tape = gen_tape(graph, instance, machine)
return execute_tape(tape, input)
end

View File

@ -1,10 +1,11 @@
# TODO: do this with macros
function call_fc(fc::FunctionCall{VectorT, 0}, cache::Dict{Symbol, Any}) where {VectorT <: SVector{1}}
cache[fc.return_symbol] = fc.func(cache[fc.arguments[1]])
return nothing
end
function call_fc(fc::FunctionCall{VectorT, 1}, cache::Dict{Symbol, Any}) where {VectorT <: SVector{1}}
cache[fc.return_symbol] = fc.func(fc.additional_arguments[1], cache[fc.arguments[1]])
cache[fc.return_symbol] = fc.func(fc.value_arguments[1], cache[fc.arguments[1]])
return nothing
end
@ -14,12 +15,12 @@ function call_fc(fc::FunctionCall{VectorT, 0}, cache::Dict{Symbol, Any}) where {
end
function call_fc(fc::FunctionCall{VectorT, 1}, cache::Dict{Symbol, Any}) where {VectorT <: SVector{2}}
cache[fc.return_symbol] = fc.func(fc.additional_arguments[1], cache[fc.arguments[1]], cache[fc.arguments[2]])
cache[fc.return_symbol] = fc.func(fc.value_arguments[1], cache[fc.arguments[1]], cache[fc.arguments[2]])
return nothing
end
function call_fc(fc::FunctionCall{VectorT, 1}, cache::Dict{Symbol, Any}) where {VectorT}
cache[fc.return_symbol] = fc.func(fc.additional_arguments[1], getindex.(Ref(cache), fc.arguments)...)
cache[fc.return_symbol] = fc.func(fc.value_arguments[1], getindex.(Ref(cache), fc.arguments)...)
return nothing
end
@ -31,7 +32,7 @@ Execute the given [`FunctionCall`](@ref) on the dictionary.
Several more specialized versions of this function exist to reduce vector unrolling work for common cases.
"""
function call_fc(fc::FunctionCall{VectorT, M}, cache::Dict{Symbol, Any}) where {VectorT, M}
cache[fc.return_symbol] = fc.func(fc.additional_arguments..., getindex.(Ref(cache), fc.arguments)...)
cache[fc.return_symbol] = fc.func(fc.value_arguments..., getindex.(Ref(cache), fc.arguments)...)
return nothing
end
@ -47,12 +48,8 @@ end
For a given function call, return an expression evaluating it.
"""
function expr_from_fc(fc::FunctionCall{VectorT, M}) where {VectorT, M}
func_call = Expr(
:call,
Symbol(fc.func),
fc.additional_arguments...,
eval.(gen_access_expr.(Ref(fc.device), fc.arguments))...,
)
func_call =
Expr(:call, Symbol(fc.func), fc.value_arguments..., eval.(gen_access_expr.(Ref(fc.device), fc.arguments))...)
expr = :($(eval(gen_access_expr(fc.device, fc.return_symbol))) = $func_call)
return expr
@ -73,52 +70,31 @@ function gen_cache_init_code(machine::Machine)
return initializeCaches
end
"""
part_from_x(type::Type, index::Int, x::AbstractProcessInput)
Return the [`ParticleValue`](@ref) of the given type of particle with the given `index` from the given process input.
Function is wrapped into a [`FunctionCall`](@ref) in [`gen_input_assignment_code`](@ref).
"""
part_from_x(type::Type, index::Int, x::AbstractProcessInput) =
ParticleValue{type, ComplexF64}(get_particle(x, type, index), one(ComplexF64))
"""
gen_input_assignment_code(
inputSymbols::Dict{String, Vector{Symbol}},
processDescription::AbstractProcessDescription,
instance::AbstractProblemInstance,
machine::Machine,
processInputSymbol::Symbol = :input,
problemInputSymbol::Symbol = :input,
)
Return a `Vector{Expr}` doing the input assignments from the given `processInputSymbol` onto the `inputSymbols`.
Return a `Vector{Expr}` doing the input assignments from the given `problemInputSymbol` onto the `inputSymbols`.
"""
function gen_input_assignment_code(
inputSymbols::Dict{String, Vector{Symbol}},
processDescription::AbstractProcessDescription,
instance,
machine::Machine,
processInputSymbol::Symbol = :input,
problemInputSymbol::Symbol = :input,
)
@assert length(inputSymbols) >=
sum(values(in_particles(processDescription))) + sum(values(out_particles(processDescription))) "Number of input Symbols is smaller than the number of particles in the process description"
assignInputs = Vector{FunctionCall}()
for (name, symbols) in inputSymbols
(type, index) = type_index_from_name(model(processDescription), name)
# make a function for this, since we can't use anonymous functions in the FunctionCall
for symbol in symbols
device = entry_device(machine)
push!(
assignInputs,
FunctionCall(
# x is the process input
part_from_x,
SVector{1, Symbol}(processInputSymbol),
SVector{2, Any}(type, index),
symbol,
device,
),
FunctionCall(input_expr, SVector{1, Any}(name), SVector{1, Symbol}(problemInputSymbol), symbol, device),
)
end
end
@ -127,14 +103,14 @@ function gen_input_assignment_code(
end
"""
gen_tape(graph::DAG, process::AbstractProcessDescription, machine::Machine)
gen_tape(graph::DAG, instance::AbstractProblemInstance, machine::Machine, scheduler::AbstractScheduler = GreedyScheduler())
Generate the code for a given graph. The return value is a [`Tape`](@ref).
See also: [`execute`](@ref), [`execute_tape`](@ref)
"""
function gen_tape(graph::DAG, process::AbstractProcessDescription, machine::Machine)
schedule = schedule_dag(GreedyScheduler(), graph, machine)
function gen_tape(graph::DAG, instance, machine::Machine, scheduler::AbstractScheduler = GreedyScheduler())
schedule = schedule_dag(scheduler, graph, machine)
# get inSymbols
inputSyms = Dict{String, Vector{Symbol}}()
@ -150,23 +126,24 @@ function gen_tape(graph::DAG, process::AbstractProcessDescription, machine::Mach
outSym = Symbol(to_var_name(get_exit_node(graph).id))
initCaches = gen_cache_init_code(machine)
assignInputs = gen_input_assignment_code(inputSyms, process, machine, :input)
assignInputs = gen_input_assignment_code(inputSyms, instance, machine, :input)
return Tape(initCaches, assignInputs, schedule, inputSyms, outSym, Dict(), process, machine)
return Tape{input_type(instance)}(initCaches, assignInputs, schedule, inputSyms, outSym, Dict(), instance, machine)
end
"""
execute_tape(tape::Tape, input::AbstractProcessInput)
execute_tape(tape::Tape, input::Input) where {Input}
Execute the given tape with the given input.
For implementation reasons, this disregards the set [`CacheStrategy`](@ref) of the devices and always uses a dictionary.
"""
function execute_tape(tape::Tape, input::AbstractProcessInput)
function execute_tape(tape::Tape, input)
cache = Dict{Symbol, Any}()
cache[:input] = input
# simply execute all the code snippets here
# TODO: `@assert` that process input fits the tape.process
@assert typeof(input) == input_type(tape.instance)
# TODO: `@assert` that input fits the tape.instance
for expr in tape.initCachesCode
@eval $expr
end

View File

@ -1,19 +1,21 @@
"""
Tape
Tape{INPUT}
TODO: update docs
- `INPUT` the input type of the problem instance
- `code::Vector{Expr}`: The julia expression containing the code for the whole graph.
- `inputSymbols::Dict{String, Vector{Symbol}}`: A dictionary of symbols mapping the names of the input nodes of the graph to the symbols their inputs should be provided on.
- `outputSymbol::Symbol`: The symbol of the final calculated value
"""
struct Tape
struct Tape{INPUT}
initCachesCode::Vector{Expr}
inputAssignCode::Vector{FunctionCall}
computeCode::Vector{FunctionCall}
inputSymbols::Dict{String, Vector{Symbol}}
outputSymbol::Symbol
cache::Dict{Symbol, Any}
process::AbstractProcessDescription
instance::Any
machine::Machine
end

View File

@ -1,120 +1,46 @@
import QEDbase.mass
import QEDbase.AbstractParticle
"""
AbstractPhysicsModel
AbstractModel
Base type for a model, e.g. ABC-Model or QED. This is used to dispatch many functions.
Base type for all models. From this, [`AbstractProblemInstance`](@ref)s can be constructed.
See also: [`problem_instance`](@ref)
"""
abstract type AbstractPhysicsModel end
abstract type AbstractModel end
"""
ParticleValue{ParticleType <: AbstractParticle}
problem_instance(::AbstractModel, ::Vararg)
A struct describing a particle during a calculation of a Feynman Diagram, together with the value that's being calculated. `AbstractParticle` is the type from the QEDbase package.
`sizeof(ParticleValue())` = 48 Byte
Interface function that must be implemented for any implementation of [`AbstractModel`](@ref). This function should return a specific [`AbstractProblemInstance`](@ref) given some parameters.
"""
struct ParticleValue{ParticleType <: AbstractParticle, ValueType}
p::ParticleType
v::ValueType
end
function problem_instance end
"""
AbstractProcessDescription
AbstractProblemInstance
Base type for process descriptions. An object of this type of a corresponding [`AbstractPhysicsModel`](@ref) should uniquely identify a process in that model.
Base type for problem instances. An object of this type of a corresponding [`AbstractModel`](@ref) should uniquely identify a problem instance of that model.
See also: [`parse_process`](@ref)
"""
abstract type AbstractProcessDescription end
abstract type AbstractProblemInstance end
"""
AbstractProcessInput
input_type(problem::AbstractProblemInstance)
Base type for process inputs. An object of this type contains the input values (e.g. momenta) of the particles in a process.
See also: [`gen_process_input`](@ref)
Return the fully specified input type for a specific [`AbstractProblemInstance`](@ref).
"""
abstract type AbstractProcessInput end
function input_type end
"""
interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: AbstractParticle, T2 <: AbstractParticle}
graph(::AbstractProblemInstance)
Interface function that must be implemented for every subtype of [`AbstractParticle`](@ref), returning the result particle type when the two given particles interact.
Generate the [`DAG`](@ref) for the given [`AbstractProblemInstance`](@ref). Every entry node (see [`get_entry_nodes`](@ref)) to the graph must have a name set. Implement [`input_expr`](@ref) to return a valid expression for each of those names.
"""
function interaction_result end
function graph end
"""
types(::AbstractPhysicsModel)
input_expr(::AbstractProblemInstance, name::String, input)
Interface function that must be implemented for every subtype of [`AbstractPhysicsModel`](@ref), returning a `Vector` of the available particle types in the model.
For a given [`AbstractProblemInstance`](@ref), the problem input (of type `input_type(...)`) and an entry node name, return a that specific input value from the input symbol.
"""
function types end
"""
in_particles(::AbstractProcessDescription)
Interface function that must be implemented for every subtype of [`AbstractProcessDescription`](@ref).
Returns a `<: Dict{Type{AbstractParticle}, Int}` object, representing the number of incoming particles for the process per particle type.
in_particles(::AbstractProcessInput)
Interface function that must be implemented for every subtype of [`AbstractProcessInput`](@ref).
Returns a `<: Vector{AbstractParticle}` object with the values of all incoming particles for the corresponding `ProcessDescription`.
"""
function in_particles end
"""
out_particles(::AbstractProcessDescription)
Interface function that must be implemented for every subtype of [`AbstractProcessDescription`](@ref).
Returns a `<: Dict{Type{AbstractParticle}, Int}` object, representing the number of outgoing particles for the process per particle type.
out_particles(::AbstractProcessInput)
Interface function that must be implemented for every subtype of [`AbstractProcessInput`](@ref).
Returns a `<: Vector{AbstractParticle}` object with the values of all outgoing particles for the corresponding `ProcessDescription`.
"""
function out_particles end
"""
get_particle(::AbstractProcessInput, t::Type, n::Int)
Interface function that must be implemented for every subtype of [`AbstractProcessInput`](@ref).
Returns the `n`th particle of type `t`.
"""
function get_particle end
"""
parse_process(::AbstractString, ::AbstractPhysicsModel)
Interface function that must be implemented for every subtype of [`AbstractPhysicsModel`](@ref).
Returns a `ProcessDescription` object.
"""
function parse_process end
"""
gen_process_input(::AbstractProcessDescription)
Interface function that must be implemented for every specific [`AbstractProcessDescription`](@ref).
Returns a randomly generated and valid corresponding `ProcessInput`.
"""
function gen_process_input end
"""
model(::AbstractProcessDescription)
model(::AbstarctProcessInput)
Return the model of this process description or input.
"""
function model end
"""
type_from_name(model::AbstractModel, name::String)
For a name of a particle in the given [`AbstractModel`](@ref), return the particle's [`Type`] and index as a tuple. The input string can be expetced to be of the form \"<name><index>\".
"""
function type_index_from_name end
function input_expr end

View File

@ -0,0 +1,142 @@
import QEDbase.AbstractParticle
"""
AbstractPhysicsModel
Base type for a model, e.g. ABC-Model or QED. This is used to dispatch many functions.
"""
abstract type AbstractPhysicsModel <: AbstractModel end
"""
ParticleValue{ParticleType <: AbstractParticleStateful}
A struct describing a particle during a calculation of a Feynman Diagram, together with the value that's being calculated. `AbstractParticleStateful` is the type from the QEDbase package.
`sizeof(ParticleValue())` = 48 Byte
"""
struct ParticleValue{ParticleType <: AbstractParticleStateful, ValueType}
p::ParticleType
v::ValueType
end
"""
TBW
particle value + spin/pol info, only used on the external legs (u tasks)
"""
struct ParticleValueSP{ParticleType <: AbstractParticleStateful, SP <: AbstractSpinOrPolarization, ValueType}
p::ParticleType
v::ValueType
sp::SP
end
"""
AbstractProcessDescription <: AbstractProblemInstance
Base type for particle scattering process descriptions. An object of this type of a corresponding [`AbstractPhysicsModel`](@ref) should uniquely identify a scattering process in that model.
See also: [`parse_process`](@ref), [`AbstractProblemInstance`](@ref)
"""
abstract type AbstractProcessDescription end
#TODO: i don't think giving this a base type is a good idea, the input type should just be returned of some function, allowing anything as an input type
"""
AbstractProcessInput
Base type for process inputs. An object of this type contains the input values (e.g. momenta) of the particles in a process.
See also: [`gen_process_input`](@ref)
"""
abstract type AbstractProcessInput end
"""
interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: AbstractParticle, T2 <: AbstractParticle}
Interface function that must be implemented for every subtype of [`AbstractParticle`](@ref), returning the result particle type when the two given particles interact.
"""
function interaction_result end
"""
types(::AbstractPhysicsModel)
Interface function that must be implemented for every subtype of [`AbstractPhysicsModel`](@ref), returning a `Vector` of the available particle types in the model.
"""
function types end
"""
in_particles(::AbstractProcessDescription)
Interface function that must be implemented for every subtype of [`AbstractProcessDescription`](@ref).
Returns a `<: Dict{Type{AbstractParticle}, Int}` object, representing the number of incoming particles for the process per particle type.
in_particles(::AbstractProcessInput)
Interface function that must be implemented for every subtype of [`AbstractProcessInput`](@ref).
Returns a `<: Vector{AbstractParticle}` object with the values of all incoming particles for the corresponding `ProcessDescription`.
"""
function in_particles end
"""
out_particles(::AbstractProcessDescription)
Interface function that must be implemented for every subtype of [`AbstractProcessDescription`](@ref).
Returns a `<: Dict{Type{AbstractParticle}, Int}` object, representing the number of outgoing particles for the process per particle type.
out_particles(::AbstractProcessInput)
Interface function that must be implemented for every subtype of [`AbstractProcessInput`](@ref).
Returns a `<: Vector{AbstractParticle}` object with the values of all outgoing particles for the corresponding `ProcessDescription`.
"""
function out_particles end
"""
get_particle(::AbstractProcessInput, t::Type, n::Int)
Interface function that must be implemented for every subtype of [`AbstractProcessInput`](@ref).
Returns the `n`th particle of type `t`.
"""
function get_particle end
"""
parse_process(::AbstractString, ::AbstractPhysicsModel)
Interface function that must be implemented for every subtype of [`AbstractPhysicsModel`](@ref).
Returns a `ProcessDescription` object.
"""
function parse_process end
"""
gen_process_input(::AbstractProcessDescription)
Interface function that must be implemented for every specific [`AbstractProcessDescription`](@ref).
Returns a randomly generated and valid corresponding `ProcessInput`.
"""
function gen_process_input end
"""
model(::AbstractProcessDescription)
model(::AbstractProcessInput)
Return the model of this process description or input.
"""
function model end
"""
type_from_name(model::AbstractModel, name::String)
For a name of a particle in the given [`AbstractModel`](@ref), return the particle's [`Type`] and index as a tuple. The input string can be expetced to be of the form \"<name><index>\".
"""
function type_index_from_name end
"""
part_from_x(type::Type, index::Int, x::AbstractProcessInput)
Return the [`ParticleValue`](@ref) of the given type of particle with the given `index` from the given process input.
Function is wrapped into a [`FunctionCall`](@ref) in [`gen_input_assignment_code`](@ref).
"""
part_from_x(type::Type, index::Int, x::AbstractProcessInput) =
ParticleValue{type, ComplexF64}(get_particle(x, type, index), one(ComplexF64))

View File

@ -1,41 +1,42 @@
using StaticArrays
"""
compute(::ComputeTaskQED_P, data::QEDParticleValue)
function input_expr(name::String, psp::PhaseSpacePoint)
(type, index) = type_index_from_name(QEDModel(), name)
Return the particle as is and initialize the Value.
"""
function compute(::ComputeTaskQED_P, data::QEDParticleValue{P}) where {P<:QEDParticle}
# TODO do we actually need this for anything?
return ParticleValue{P,DiracMatrix}(data.p, one(DiracMatrix))
return ParticleValueSP(
type(momentum(psp, particle_direction(type), particle_species(type), index)),
0.0im,
spin_or_pol(process(psp), type, index),
)
end
"""
compute(::ComputeTaskQED_U, data::QEDParticleValue)
compute(::ComputeTaskQED_U, data::ParticleValueSP)
Compute an outer edge. Return the particle value with the same particle and the value multiplied by an outer_edge factor.
"""
function compute(
::ComputeTaskQED_U, data::PV
) where {P<:QEDParticle,PV<:QEDParticleValue{P}}
::ComputeTaskQED_U,
data::ParticleValueSP{P, SP, V},
) where {P <: ParticleStateful, V <: ValueType, SP <: AbstractSpinOrPolarization}
part::P = data.p
state = base_state(particle(part), direction(part), momentum(part), spin_or_pol(part))
return ParticleValue{P,typeof(state)}(
state = base_state(particle_species(part), particle_direction(part), momentum(part), SP())
return ParticleValue{P, typeof(state)}(
data.p,
state, # will return a SLorentzVector{ComplexF64}, BiSpinor or AdjointBiSpinor
)
end
"""
compute(::ComputeTaskQED_V, data1::QEDParticleValue, data2::QEDParticleValue)
compute(::ComputeTaskQED_V, data1::ParticleValue, data2::ParticleValue)
Compute a vertex. Preserve momentum and particle types (e + gamma->p etc.) to create resulting particle, multiply values together and times a vertex factor.
"""
function compute(
::ComputeTaskQED_V, data1::PV1, data2::PV2
) where {
P1<:QEDParticle,P2<:QEDParticle,PV1<:QEDParticleValue{P1},PV2<:QEDParticleValue{P2}
}
::ComputeTaskQED_V,
data1::ParticleValue{P1, V1},
data2::ParticleValue{P2, V2},
) where {P1 <: ParticleStateful, P2 <: ParticleStateful, V1 <: ValueType, V2 <: ValueType}
p3 = QED_conserve_momentum(data1.p, data2.p)
P3 = interaction_result(P1, P2)
state = QED_vertex()
@ -50,12 +51,12 @@ function compute(
state = state * data2.v
end
dataOut = ParticleValue{P3,typeof(state)}(P3(momentum(p3)), state)
dataOut = ParticleValue{P3, typeof(state)}(P3(momentum(p3)), state)
return dataOut
end
"""
compute(::ComputeTaskQED_S2, data1::QEDParticleValue, data2::QEDParticleValue)
compute(::ComputeTaskQED_S2, data1::ParticleValue, data2::ParticleValue)
Compute a final inner edge (2 input particles, no output particle).
@ -64,10 +65,19 @@ For valid inputs, both input particles should have the same momenta at this poin
12 FLOP.
"""
function compute(
::ComputeTaskQED_S2, data1::ParticleValue{P1}, data2::ParticleValue{P2}
::ComputeTaskQED_S2,
data1::ParticleValue{P1, V1},
data2::ParticleValue{P2, V2},
) where {
P1<:Union{AntiFermionStateful,FermionStateful},
P2<:Union{AntiFermionStateful,FermionStateful},
D1 <: ParticleDirection,
D2 <: ParticleDirection,
S1 <: Union{Electron, Positron},
S2 <: Union{Electron, Positron},
V1 <: ValueType,
V2 <: ValueType,
EL <: AbstractFourMomentum,
P1 <: ParticleStateful{D1, S1, EL},
P2 <: ParticleStateful{D2, S2, EL},
}
#@assert isapprox(data1.p.momentum, data2.p.momentum, rtol = sqrt(eps()), atol = sqrt(eps())) "$(data1.p.momentum) vs. $(data2.p.momentum)"
@ -82,8 +92,10 @@ function compute(
end
function compute(
::ComputeTaskQED_S2, data1::ParticleValue{P1}, data2::ParticleValue{P2}
) where {P1<:PhotonStateful,P2<:PhotonStateful}
::ComputeTaskQED_S2,
data1::ParticleValue{ParticleStateful{D1, Photon}, V1},
data2::ParticleValue{ParticleStateful{D2, Photon}, V2},
) where {D1 <: ParticleDirection, D2 <: ParticleDirection, V1 <: ValueType, V2 <: ValueType}
# TODO: assert that data1 and data2 are opposites
inner = QED_inner_edge(data1.p)
# inner edge is just a scalar, data1 and data2 are photon states that are just Complex numbers here
@ -91,11 +103,11 @@ function compute(
end
"""
compute(::ComputeTaskQED_S1, data::QEDParticleValue)
compute(::ComputeTaskQED_S1, data::ParticleValue)
Compute inner edge (1 input particle, 1 output particle).
"""
function compute(::ComputeTaskQED_S1, data::QEDParticleValue{P}) where {P<:QEDParticle}
function compute(::ComputeTaskQED_S1, data::ParticleValue{P, V}) where {P <: ParticleStateful, V <: ValueType}
newP = propagation_result(P)
new_p = newP(momentum(data.p))
# inner edge is just a scalar, can multiply from either side

View File

@ -0,0 +1,160 @@
ComputeTaskQED_Sum() = ComputeTaskQED_Sum(0)
function _svector_from_type(processDescription::GenericQEDProcess, type, particles)
if haskey(in_particles(processDescription), type)
return SVector{in_particles(processDescription)[type], type}(filter(x -> typeof(x) <: type, particles))
end
if haskey(out_particles(processDescription), type)
return SVector{out_particles(processDescription)[type], type}(filter(x -> typeof(x) <: type, particles))
end
end
"""
gen_process_input(processDescription::GenericQEDProcess)
Return a ProcessInput of randomly generated [`QEDParticle`](@ref)s from a [`GenericQEDProcess`](@ref). The process description can be created manually or parsed from a string using [`parse_process`](@ref).
Note: This uses RAMBO to create a valid process with conservation of momentum and energy.
"""
function gen_process_input(processDescription::GenericQEDProcess)
massSum = 0
inputMasses = Vector{Float64}()
for particle in incoming_particles(processDescription)
massSum += mass(particle)
push!(inputMasses, mass(particle))
end
outputMasses = Vector{Float64}()
for particle in outgoing_particles(processDescription)
massSum += mass(particle)
push!(outputMasses, mass(particle))
end
# add some extra random mass to allow for some momentum
massSum += rand(rng[threadid()]) * (length(inputMasses) + length(outputMasses))
initial_momenta = generate_initial_moms(massSum, inputMasses)
final_momenta = generate_physical_massive_moms(rng[threadid()], massSum, outputMasses)
processInput = PhaseSpacePoint(
processDescription,
PerturbativeQED(),
PhasespaceDefinition(SphericalCoordinateSystem(), ElectronRestFrame()),
tuple(initial_momenta...),
tuple(final_momenta...),
)
return processInput
end
"""
gen_graph(process_description::GenericQEDProcess)
For a given [`GenericQEDProcess`](@ref), return the [`DAG`](@ref) that computes it.
"""
function gen_graph(process_description::GenericQEDProcess)
initial_diagram = FeynmanDiagram(process_description)
diagrams = gen_diagrams(initial_diagram)
graph = DAG()
COMPLEX_SIZE = sizeof(ComplexF64)
PARTICLE_VALUE_SIZE = 96.0
# TODO: Not all diagram outputs should always be summed at the end, if they differ by fermion exchange they need to be diffed
# Should not matter for n-Photon Compton processes though
sum_node = insert_node!(graph, make_node(ComputeTaskQED_Sum(0)); track = false, invalidate_cache = false)
global_data_out = insert_node!(graph, make_node(DataTask(COMPLEX_SIZE)); track = false, invalidate_cache = false)
insert_edge!(graph, sum_node, global_data_out; track = false, invalidate_cache = false)
# remember the data out nodes for connection
dataOutNodes = Dict()
for particle in initial_diagram.particles
# generate data in and U tasks
data_in = insert_node!(
graph,
make_node(DataTask(PARTICLE_VALUE_SIZE), String(particle));
track = false,
invalidate_cache = false,
) # read particle data node
compute_u = insert_node!(graph, make_node(ComputeTaskQED_U()); track = false, invalidate_cache = false) # compute U node
data_out =
insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)); track = false, invalidate_cache = false) # transfer data out from u (one ABCParticleValue object)
insert_edge!(graph, data_in, compute_u; track = false, invalidate_cache = false)
insert_edge!(graph, compute_u, data_out; track = false, invalidate_cache = false)
# remember the data_out node for future edges
dataOutNodes[String(particle)] = data_out
end
# TODO: this should be parallelizable somewhat easily
for diagram in diagrams
tie = diagram.tie[]
# handle the vertices
for vertices in diagram.vertices
for vertex in vertices
data_in1 = dataOutNodes[String(vertex.in1)]
data_in2 = dataOutNodes[String(vertex.in2)]
compute_V = insert_node!(graph, make_node(ComputeTaskQED_V()); track = false, invalidate_cache = false) # compute vertex
insert_edge!(graph, data_in1, compute_V; track = false, invalidate_cache = false)
insert_edge!(graph, data_in2, compute_V; track = false, invalidate_cache = false)
data_V_out = insert_node!(
graph,
make_node(DataTask(PARTICLE_VALUE_SIZE));
track = false,
invalidate_cache = false,
)
insert_edge!(graph, compute_V, data_V_out; track = false, invalidate_cache = false)
if (vertex.out == tie.in1 || vertex.out == tie.in2)
# out particle is part of the tie -> there will be an S2 task with it later, don't make S1 task
dataOutNodes[String(vertex.out)] = data_V_out
continue
end
# otherwise, add S1 task
compute_S1 =
insert_node!(graph, make_node(ComputeTaskQED_S1()); track = false, invalidate_cache = false) # compute propagator
insert_edge!(graph, data_V_out, compute_S1; track = false, invalidate_cache = false)
data_S1_out = insert_node!(
graph,
make_node(DataTask(PARTICLE_VALUE_SIZE));
track = false,
invalidate_cache = false,
)
insert_edge!(graph, compute_S1, data_S1_out; track = false, invalidate_cache = false)
# overrides potentially different nodes from previous diagrams, which is intentional
dataOutNodes[String(vertex.out)] = data_S1_out
end
end
# handle the tie
data_in1 = dataOutNodes[String(tie.in1)]
data_in2 = dataOutNodes[String(tie.in2)]
compute_S2 = insert_node!(graph, make_node(ComputeTaskQED_S2()); track = false, invalidate_cache = false)
data_S2 = insert_node!(graph, make_node(DataTask(PARTICLE_VALUE_SIZE)); track = false, invalidate_cache = false)
insert_edge!(graph, data_in1, compute_S2; track = false, invalidate_cache = false)
insert_edge!(graph, data_in2, compute_S2; track = false, invalidate_cache = false)
insert_edge!(graph, compute_S2, data_S2; track = false, invalidate_cache = false)
insert_edge!(graph, data_S2, sum_node; track = false, invalidate_cache = false)
add_child!(task(sum_node))
end
return graph
end

View File

@ -8,10 +8,10 @@ import Base.show
"""
FeynmanParticle
Representation of a particle for use in [`FeynmanDiagram`](@ref)s. Consist of the [`QEDParticle`](@ref) type and an id.
Representation of a particle for use in [`FeynmanDiagram`](@ref)s. Consist of the `ParticleStateful` type and an id.
"""
struct FeynmanParticle
particle::Type{<:QEDParticle}
particle::Type{<:ParticleStateful}
id::Int
end
@ -45,37 +45,27 @@ The [`FeynmanTie`](@ref) represents the final inner edge of the diagram.
"""
struct FeynmanDiagram
vertices::Vector{Set{FeynmanVertex}}
tie::Ref{Union{FeynmanTie,Missing}}
tie::Ref{Union{FeynmanTie, Missing}}
particles::Vector{FeynmanParticle}
type_ids::Dict{Type,Int64} # lut for number of used ids for a particle type
type_ids::Dict{Type, Int64} # lut for number of used ids for a particle type
end
"""
FeynmanDiagram(pd::QEDProcessDescription)
FeynmanDiagram(pd::GenericQEDProcess)
Create an initial [`FeynmanDiagram`](@ref) with only its initial particles set and no vertices or ties.
Use [`gen_diagrams`](@ref) to generate all possible diagrams from this one.
"""
function FeynmanDiagram(pd::QEDProcessDescription)
function FeynmanDiagram(pd::GenericQEDProcess)
parts = Vector{FeynmanParticle}()
for (type, n) in pd.inParticles
for i in 1:n
ids = Dict{Type, Int64}()
for type in types(model(pd))
for i in 1:number_particles(pd, type)
push!(parts, FeynmanParticle(type, i))
end
end
for (type, n) in pd.outParticles
for i in 1:n
push!(parts, FeynmanParticle(type, i))
end
end
ids = Dict{Type,Int64}()
for t in types(QEDModel())
if (isincoming(t))
ids[t] = get(pd.inParticles, t, 0)
else
ids[t] = get(pd.outParticles, t, 0)
end
ids[type] = number_particles(pd, type)
end
return FeynmanDiagram([], missing, parts, ids)
@ -83,17 +73,13 @@ end
function particle_after_tie(p::FeynmanParticle, t::FeynmanTie)
if p == t.in1 || p == t.in2
return FeynmanParticle(FermionStateful{Incoming,SpinUp}, -1) # placeholder particle and id for tied particles
return FeynmanParticle(ParticleStateful{Incoming, Electron, SFourMomentum}, -1) # placeholder particle and id for tied particles
end
return p
end
function vertex_after_tie(v::FeynmanVertex, t::FeynmanTie)
return FeynmanVertex(
particle_after_tie(v.in1, t),
particle_after_tie(v.in2, t),
particle_after_tie(v.out, t),
)
return FeynmanVertex(particle_after_tie(v.in1, t), particle_after_tie(v.in2, t), particle_after_tie(v.out, t))
end
function vertex_after_tie(v::FeynmanVertex, t::Missing)
@ -108,9 +94,7 @@ function vertex_set_after_tie(vs::Set{FeynmanVertex}, t::Missing)
return vs
end
function vertex_set_after_tie(
vs::Set{FeynmanVertex}, t1::Union{FeynmanTie,Missing}, t2::Union{FeynmanTie,Missing}
)
function vertex_set_after_tie(vs::Set{FeynmanVertex}, t1::Union{FeynmanTie, Missing}, t2::Union{FeynmanTie, Missing})
return Set{FeynmanVertex}(vertex_after_tie(vertex_after_tie(v, t1), t2) for v in vs)
end
@ -120,7 +104,7 @@ end
Return a string representation of the [`FeynmanParticle`](@ref) in a format that is readable by [`type_index_from_name`](@ref).
"""
function String(p::FeynmanParticle)
return "$(String(p.particle))$(String(direction(p.particle)))$(p.id)"
return "$(String(p.particle))$(String(particle_direction(p.particle)))$(p.id)"
end
function hash(v::FeynmanVertex)
@ -144,8 +128,7 @@ function ==(t1::FeynmanTie, t2::FeynmanTie)
end
function ==(d1::FeynmanDiagram, d2::FeynmanDiagram)
if (!ismissing(d1.tie[]) && ismissing(d2.tie[])) ||
(ismissing(d1.tie[]) && !ismissing(d2.tie[]))
if (!ismissing(d1.tie[]) && ismissing(d2.tie[])) || (ismissing(d1.tie[]) && !ismissing(d2.tie[]))
return false
end
if d1.particles != d2.particles
@ -157,8 +140,7 @@ function ==(d1::FeynmanDiagram, d2::FeynmanDiagram)
# TODO can i prove that this works?
for (v1, v2) in zip(d1.vertices, d2.vertices)
if vertex_set_after_tie(v1, d1.tie[], d2.tie[]) !=
vertex_set_after_tie(v2, d1.tie[], d2.tie[])
if vertex_set_after_tie(v1, d1.tie[], d2.tie[]) != vertex_set_after_tie(v2, d1.tie[], d2.tie[])
return false
end
end
@ -171,17 +153,15 @@ function ==(d1::FeynmanDiagram, d2::FeynmanDiagram)
end
function copy(fd::FeynmanDiagram)
return FeynmanDiagram(
deepcopy(fd.vertices), copy(fd.tie[]), deepcopy(fd.particles), copy(fd.type_ids)
)
return FeynmanDiagram(deepcopy(fd.vertices), copy(fd.tie[]), deepcopy(fd.particles), copy(fd.type_ids))
end
"""
id_for_type(d::FeynmanDiagram, t::Type{<:QEDParticle})
id_for_type(d::FeynmanDiagram, t::Type{<:ParticleStateful})
Return the highest id of any particle of the given type in the diagram + 1.
"""
function id_for_type(d::FeynmanDiagram, t::Type{<:QEDParticle})
function id_for_type(d::FeynmanDiagram, t::Type{<:ParticleStateful})
return d.type_ids[t] + 1
end
@ -240,7 +220,7 @@ end
Return a vector of the particles after applying the vertices and tie of the diagram up to the given level. If no level is given, apply all. The tie comes last and is its own "level".
"""
function get_particles(fd::FeynmanDiagram, level::Int=-1)
function get_particles(fd::FeynmanDiagram, level::Int = -1)
if level == -1
level = length(fd.vertices) + 1
end
@ -376,14 +356,8 @@ function possible_vertices(fd::FeynmanDiagram)
p1 = particles[i]
p2 = particles[j]
if (caninteract(p1.particle, p2.particle))
interaction_res = propagation_result(
interaction_result(p1.particle, p2.particle)
)
v = FeynmanVertex(
p1,
p2,
FeynmanParticle(interaction_res, id_for_type(fd, interaction_res)),
)
interaction_res = propagation_result(interaction_result(p1.particle, p2.particle))
v = FeynmanVertex(p1, p2, FeynmanParticle(interaction_res, id_for_type(fd, interaction_res)))
#@assert !(v.out in particles) "$v is in $fd"
if !can_apply_vertex(fully_generated_particles, v)
continue
@ -456,18 +430,19 @@ function remove_duplicates(compare_set::Set{FeynmanDiagram})
return result
end
"""
is_compton(fd::FeynmanDiagram)
Returns true iff the given feynman diagram is an (empty) diagram of a compton process like ke->k^ne
"""
function is_compton(fd::FeynmanDiagram)
return fd.type_ids[FermionStateful{Incoming}] == 1 &&
fd.type_ids[FermionStateful{Outgoing}] == 1 &&
fd.type_ids[AntiFermionStateful{Incoming}] == 0 &&
fd.type_ids[AntiFermionStateful{Outgoing}] == 0 &&
fd.type_ids[PhotonStateful{Incoming}] >= 1 &&
fd.type_ids[PhotonStateful{Outgoing}] >= 1
return fd.type_ids[ParticleStateful{Incoming, Electron, SFourMomentum}] == 1 &&
fd.type_ids[ParticleStateful{Outgoing, Electron, SFourMomentum}] == 1 &&
fd.type_ids[ParticleStateful{Incoming, Positron, SFourMomentum}] == 0 &&
fd.type_ids[ParticleStateful{Outgoing, Positron, SFourMomentum}] == 0 &&
fd.type_ids[ParticleStateful{Incoming, Photon, SFourMomentum}] >= 1 &&
fd.type_ids[ParticleStateful{Outgoing, Photon, SFourMomentum}] >= 1
end
"""
@ -477,19 +452,19 @@ Helper function for [`gen_compton_diagrams`](@Ref). Generates a single diagram f
"""
function gen_compton_diagram_from_order(order::Vector{Int}, inFerm, outFerm, n::Int, m::Int)
photons = vcat(
[FeynmanParticle(PhotonStateful{Incoming,PolX}, i) for i in 1:n],
[FeynmanParticle(PhotonStateful{Outgoing,PolX}, i) for i in 1:m],
[FeynmanParticle(ParticleStateful{Incoming, Photon, SFourMomentum}, i) for i in 1:n],
[FeynmanParticle(ParticleStateful{Outgoing, Photon, SFourMomentum}, i) for i in 1:m],
)
new_diagram = FeynmanDiagram(
[],
missing,
[inFerm, outFerm, photons...],
Dict{Type,Int64}(
FermionStateful{Incoming,SpinUp} => 1,
FermionStateful{Outgoing,SpinUp} => 1,
PhotonStateful{Incoming,PolX} => n,
PhotonStateful{Outgoing,PolX} => m,
Dict{Type, Int64}(
ParticleStateful{Incoming, Electron, SFourMomentum} => 1,
ParticleStateful{Outgoing, Electron, SFourMomentum} => 1,
ParticleStateful{Incoming, Photon, SFourMomentum} => n,
ParticleStateful{Outgoing, Photon, SFourMomentum} => m,
),
)
@ -501,9 +476,9 @@ function gen_compton_diagram_from_order(order::Vector{Int}, inFerm, outFerm, n::
while left_index <= right_index
# left side
v_left = FeynmanVertex(
FeynmanParticle(FermionStateful{Incoming,SpinUp}, iterations),
FeynmanParticle(ParticleStateful{Incoming, Electron, SFourMomentum}, iterations),
photons[order[left_index]],
FeynmanParticle(FermionStateful{Incoming,SpinUp}, iterations + 1),
FeynmanParticle(ParticleStateful{Incoming, Electron, SFourMomentum}, iterations + 1),
)
left_index += 1
add_vertex!(new_diagram, v_left)
@ -514,9 +489,9 @@ function gen_compton_diagram_from_order(order::Vector{Int}, inFerm, outFerm, n::
# right side
v_right = FeynmanVertex(
FeynmanParticle(FermionStateful{Outgoing,SpinUp}, iterations),
FeynmanParticle(ParticleStateful{Outgoing, Electron, SFourMomentum}, iterations),
photons[order[right_index]],
FeynmanParticle(FermionStateful{Outgoing,SpinUp}, iterations + 1),
FeynmanParticle(ParticleStateful{Outgoing, Electron, SFourMomentum}, iterations + 1),
)
right_index -= 1
add_vertex!(new_diagram, v_right)
@ -529,6 +504,7 @@ function gen_compton_diagram_from_order(order::Vector{Int}, inFerm, outFerm, n::
return new_diagram
end
#=
"""
gen_compton_diagram_from_order_one_side(order::Vector{Int}, inFerm, outFerm, n::Int, m::Int)
@ -538,19 +514,19 @@ function gen_compton_diagram_from_order_one_side(
order::Vector{Int}, inFerm, outFerm, n::Int, m::Int
)
photons = vcat(
[FeynmanParticle(PhotonStateful{Incoming,PolX}, i) for i in 1:n],
[FeynmanParticle(PhotonStateful{Outgoing,PolX}, i) for i in 1:m],
[FeynmanParticle(ParticleStateful{Incoming, Photon}, i) for i in 1:n],
[FeynmanParticle(ParticleStateful{Outgoing, Photon}, i) for i in 1:m],
)
new_diagram = FeynmanDiagram(
[],
missing,
[inFerm, outFerm, photons...],
Dict{Type,Int64}(
FermionStateful{Incoming,SpinUp} => 1,
FermionStateful{Outgoing,SpinUp} => 1,
PhotonStateful{Incoming,PolX} => n,
PhotonStateful{Outgoing,PolX} => m,
Dict{Type, Int64}(
ParticleStateful{Incoming, Electron} => 1,
ParticleStateful{Outgoing, Electron} => 1,
ParticleStateful{Incoming, Photon} => n,
ParticleStateful{Outgoing, Photon} => m,
),
)
@ -562,9 +538,9 @@ function gen_compton_diagram_from_order_one_side(
while left_index <= right_index
# left side
v_left = FeynmanVertex(
FeynmanParticle(FermionStateful{Incoming,SpinUp}, iterations),
FeynmanParticle(ParticleStateful{Incoming, Electron}, iterations),
photons[order[left_index]],
FeynmanParticle(FermionStateful{Incoming,SpinUp}, iterations + 1),
FeynmanParticle(ParticleStateful{Incoming, Electron}, iterations + 1),
)
left_index += 1
add_vertex!(new_diagram, v_left)
@ -577,9 +553,9 @@ function gen_compton_diagram_from_order_one_side(
if (iterations == 1)
# right side
v_right = FeynmanVertex(
FeynmanParticle(FermionStateful{Outgoing,SpinUp}, iterations),
FeynmanParticle(ParticleStateful{Outgoing, Electron}, iterations),
photons[order[right_index]],
FeynmanParticle(FermionStateful{Outgoing,SpinUp}, iterations + 1),
FeynmanParticle(ParticleStateful{Outgoing, Electron}, iterations + 1),
)
right_index -= 1
add_vertex!(new_diagram, v_right)
@ -593,6 +569,7 @@ function gen_compton_diagram_from_order_one_side(
add_tie!(new_diagram, FeynmanTie(ps[1], ps[2]))
return new_diagram
end
=#
"""
gen_compton_diagrams(n::Int, m::Int)
@ -600,17 +577,14 @@ end
Special case diagram generation for Compton processes, i.e., processes of the form k^ne->k^me
"""
function gen_compton_diagrams(n::Int, m::Int)
inFerm = FeynmanParticle(FermionStateful{Incoming,SpinUp}, 1)
outFerm = FeynmanParticle(FermionStateful{Outgoing,SpinUp}, 1)
inFerm = FeynmanParticle(ParticleStateful{Incoming, Electron, SFourMomentum}, 1)
outFerm = FeynmanParticle(ParticleStateful{Outgoing, Electron, SFourMomentum}, 1)
perms = [permutations([i for i in 1:(n + m)])...]
diagrams = [Vector{FeynmanDiagram}() for i in 1:nthreads()]
@threads for order in perms
push!(
diagrams[threadid()],
gen_compton_diagram_from_order(order, inFerm, outFerm, n, m),
)
push!(diagrams[threadid()], gen_compton_diagram_from_order(order, inFerm, outFerm, n, m))
end
return vcat(diagrams...)
@ -622,17 +596,14 @@ end
Special case diagram generation for Compton processes, i.e., processes of the form k^ne->k^me, but generating from one end, yielding larger diagrams
"""
function gen_compton_diagrams_one_side(n::Int, m::Int)
inFerm = FeynmanParticle(FermionStateful{Incoming,SpinUp}, 1)
outFerm = FeynmanParticle(FermionStateful{Outgoing,SpinUp}, 1)
inFerm = FeynmanParticle(ParticleStateful{Incoming, Electron, SFourMomentum}, 1)
outFerm = FeynmanParticle(ParticleStateful{Outgoing, Electron, SFourMomentum}, 1)
perms = [permutations([i for i in 1:(n + m)])...]
diagrams = [Vector{FeynmanDiagram}() for i in 1:nthreads()]
@threads for order in perms
push!(
diagrams[threadid()],
gen_compton_diagram_from_order_one_side(order, inFerm, outFerm, n, m),
)
push!(diagrams[threadid()], gen_compton_diagram_from_order_one_side(order, inFerm, outFerm, n, m))
end
return vcat(diagrams...)
@ -645,11 +616,15 @@ From a given feynman diagram in its initial state, e.g. when created through the
"""
function gen_diagrams(fd::FeynmanDiagram)
if is_compton(fd)
return gen_compton_diagrams_one_side(
fd.type_ids[PhotonStateful{Incoming}], fd.type_ids[PhotonStateful{Outgoing}]
return gen_compton_diagrams(
fd.type_ids[ParticleStateful{Incoming, Photon, SFourMomentum}],
fd.type_ids[ParticleStateful{Outgoing, Photon, SFourMomentum}],
)
end
throw(error("Unimplemented for non-compton!"))
#=
working = Set{FeynmanDiagram}()
results = Set{FeynmanDiagram}()
@ -688,4 +663,5 @@ function gen_diagrams(fd::FeynmanDiagram)
end
return remove_duplicates(results)
=#
end

View File

@ -0,0 +1,69 @@
# TODO generalize this somehow, probably using AllSpin/AllPol as defaults
"""
parse_process(string::AbstractString, model::QEDModel)
Parse a string representation of a process, such as "ke->ke" into the corresponding [`QEDProcessDescription`](@ref).
"""
function parse_process(
str::AbstractString,
model::QEDModel,
inphpol::AbstractDefinitePolarization,
inelspin::AbstractDefiniteSpin,
outphpol::AbstractDefinitePolarization,
outelspin::AbstractDefiniteSpin,
)
inParticles = Dict(
ParticleStateful{Incoming, Photon, SFourMomentum} => 0,
ParticleStateful{Incoming, Electron, SFourMomentum} => 0,
ParticleStateful{Incoming, Positron, SFourMomentum} => 0,
)
outParticles = Dict(
ParticleStateful{Outgoing, Photon, SFourMomentum} => 0,
ParticleStateful{Outgoing, Electron, SFourMomentum} => 0,
ParticleStateful{Outgoing, Positron, SFourMomentum} => 0,
)
if !(contains(str, "->"))
throw("Did not find -> while parsing process \"$str\"")
end
(inStr, outStr) = split(str, "->")
if (isempty(inStr) || isempty(outStr))
throw("Process (\"$str\") input or output part is empty!")
end
for t in types(model)
if (is_incoming(t))
inCount = count(x -> x == String(t)[1], inStr)
if inCount != 0
inParticles[t] = inCount
end
end
if (is_outgoing(t))
outCount = count(x -> x == String(t)[1], outStr)
if outCount != 0
outParticles[t] = outCount
end
end
end
if length(inStr) != sum(values(inParticles))
throw("Encountered unknown characters in the input part of process \"$str\"")
elseif length(outStr) != sum(values(outParticles))
throw("Encountered unknown characters in the output part of process \"$str\"")
end
in_ph = inParticles[ParticleStateful{Incoming, Photon, SFourMomentum}]
in_el = inParticles[ParticleStateful{Incoming, Electron, SFourMomentum}]
in_pos = inParticles[ParticleStateful{Incoming, Positron, SFourMomentum}]
out_ph = outParticles[ParticleStateful{Outgoing, Photon, SFourMomentum}]
out_el = outParticles[ParticleStateful{Outgoing, Electron, SFourMomentum}]
out_pos = outParticles[ParticleStateful{Outgoing, Positron, SFourMomentum}]
in_spin_pols = tuple([i <= in_ph ? inphpol : inelspin for i in 1:(in_ph + in_el)]...)
out_spin_pols = tuple([i <= out_ph ? outphpol : outelspin for i in 1:(out_ph + out_el)]...)
return GenericQEDProcess(in_ph, out_ph, in_el, out_el, in_pos, out_pos, in_spin_pols, out_spin_pols)
end

View File

@ -0,0 +1,377 @@
using QEDprocesses
using StaticArrays
const e = sqrt(4π / 137)
"""
QEDModel <: AbstractPhysicsModel
Singleton definition for identification of the QED-Model.
"""
struct QEDModel <: AbstractPhysicsModel end
QEDbase.is_incoming(::Type{<:ParticleStateful{Incoming}}) = true
QEDbase.is_outgoing(::Type{<:ParticleStateful{Outgoing}}) = true
QEDbase.is_incoming(::Type{<:ParticleStateful{Outgoing}}) = false
QEDbase.is_outgoing(::Type{<:ParticleStateful{Incoming}}) = false
QEDbase.particle_direction(::Type{<:ParticleStateful{DIR}}) where {DIR <: ParticleDirection} = DIR()
QEDbase.particle_species(
::Type{<:ParticleStateful{DIR, SPECIES}},
) where {DIR <: ParticleDirection, SPECIES <: AbstractParticleType} = SPECIES()
_assert_particle_type_tuple(::Tuple{}) = nothing
_assert_particle_type_tuple(t::Tuple{AbstractParticleType, Vararg}) = _assert_particle_type_tuple(t[2:end])
_assert_particle_type_tuple(t::Any) =
throw(InvalidInputError("invalid input, provide a tuple of AbstractParticleTypes to construct a GenericQEDProcess"))
struct GenericQEDProcess{INT, OUTT, INSP, OUTSP} <:
AbstractProcessDefinition where {INT <: Tuple, OUTT <: Tuple, INSP <: Tuple, OUTSP <: Tuple}
incoming_particles::INT
outgoing_particles::OUTT
incoming_spins_pols::INSP
outgoing_spins_pols::OUTSP
function GenericQEDProcess(
in_particles::INT,
out_particles::OUTT,
in_sp::INSP,
out_sp::OUTSP,
) where {INT <: Tuple, OUTT <: Tuple, INSP <: Tuple, OUTSP <: Tuple}
_assert_particle_type_tuple(in_particles)
_assert_particle_type_tuple(out_particles)
# TODO: check in_sp/out_sp fits to particles (spin->fermion, pol->photon)
return new{INT, OUTT, INSP, OUTSP}(in_particles, out_particles, in_sp, out_sp)
end
"""
GenericQEDProcess{INSP, OUTSP}(in_ph::Int, out_ph::Int, in_el::Int, out_el::Int, in_po::Int, out_po::Int, in_sp::INSP, out_sp::OUTSP)
Convenience constructor from numbers of input/output photons, electrons and positrons.
`in_sp` and `out_sp` are tuples of the spin and polarization configurations of the given particles. Their order is Photons, then Electrons, then Positrons for both incoming and outgoing particles.
# TODO: make default for in_sp and out_sp available for convenience
"""
function GenericQEDProcess(
in_ph::Int,
out_ph::Int,
in_el::Int,
out_el::Int,
in_po::Int,
out_po::Int,
in_sp::INSP,
out_sp::OUTSP,
) where {INSP <: Tuple, OUTSP <: Tuple}
in_p = ntuple(i -> i <= in_ph ? Photon() : i <= in_ph + in_el ? Electron() : Positron(), in_ph + in_el + in_po)
out_p = ntuple(
i -> i <= out_ph ? Photon() : i <= out_ph + out_el ? Electron() : Positron(),
out_ph + out_el + out_po,
)
return GenericQEDProcess(in_p, out_p, in_sp, out_sp)
end
end
function spin_or_pol(
process::GenericQEDProcess,
type::Type{ParticleStateful{DIR, SPECIES, EL}},
n::Int,
) where {DIR <: ParticleDirection, SPECIES <: AbstractParticleType, EL <: AbstractFourMomentum}
i = 0
c = n
for p in particles(process, DIR())
i += 1
if p == SPECIES()
c -= 1
end
if c == 0
break
end
end
if c != 0 || n <= 0
throw(InvalidInputError("could not get $n-th spin/pol of $(DIR()) $species, does not exist"))
end
if DIR <: Incoming
return process.incoming_spins_pols[i]
elseif DIR <: Outgoing
return process.outgoing_spins_pols[i]
else
throw(InvalidInputError("unknown direction $(DIR()) given"))
end
end
QEDprocesses.incoming_particles(proc::GenericQEDProcess) = proc.incoming_particles
QEDprocesses.outgoing_particles(proc::GenericQEDProcess) = proc.outgoing_particles
function isphysical(proc::GenericQEDProcess)
return (
number_particles(proc, Incoming(), Electron()) + number_particles(proc, Outgoing(), Positron()) ==
number_particles(proc, Incoming(), Positron()) + number_particles(proc, Outgoing(), Electron())
) && number_particles(proc, Incoming()) + number_particles(proc, Outgoing()) >= 2
end
function input_type(p::GenericQEDProcess)
in_t = QEDcore._assemble_tuple_type(incoming_particles(p), Incoming(), SFourMomentum)
out_t = QEDcore._assemble_tuple_type(outgoing_particles(p), Outgoing(), SFourMomentum)
return PhaseSpacePoint{
typeof(p),
PerturbativeQED,
PhasespaceDefinition{SphericalCoordinateSystem, ElectronRestFrame},
Tuple{in_t...},
Tuple{out_t...},
}
end
ValueType = Union{BiSpinor, AdjointBiSpinor, DiracMatrix, SLorentzVector{Float64}, ComplexF64}
"""
interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: QEDParticle, T2 <: QEDParticle}
For two given particle types that can interact, return the third.
"""
function interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: ParticleStateful, T2 <: ParticleStateful}
@assert false "Invalid interaction between particles of types $t1 and $t2"
end
interaction_result(
::Type{ParticleStateful{Incoming, Electron, EL}},
::Type{ParticleStateful{Outgoing, Electron, EL}},
) where {EL <: AbstractFourMomentum} = ParticleStateful{Incoming, Photon, EL}
interaction_result(
::Type{ParticleStateful{Incoming, Electron, EL}},
::Type{ParticleStateful{Incoming, Positron, EL}},
) where {EL <: AbstractFourMomentum} = ParticleStateful{Incoming, Photon, EL}
interaction_result(
::Type{ParticleStateful{Incoming, Electron, EL}},
::Type{ParticleStateful{DIR, Photon, EL}},
) where {EL <: AbstractFourMomentum, DIR <: ParticleDirection} = ParticleStateful{Outgoing, Electron, EL}
interaction_result(
::Type{ParticleStateful{Outgoing, Electron, EL}},
::Type{ParticleStateful{Incoming, Electron, EL}},
) where {EL <: AbstractFourMomentum} = ParticleStateful{Incoming, Photon, EL}
interaction_result(
::Type{ParticleStateful{Outgoing, Electron, EL}},
::Type{ParticleStateful{Outgoing, Positron, EL}},
) where {EL <: AbstractFourMomentum} = ParticleStateful{Incoming, Photon, EL}
interaction_result(
::Type{ParticleStateful{Outgoing, Electron, EL}},
::Type{ParticleStateful{DIR, Photon, EL}},
) where {EL <: AbstractFourMomentum, DIR <: ParticleDirection} = ParticleStateful{Incoming, Electron, EL}
# antifermion mirror
interaction_result(
::Type{ParticleStateful{Incoming, Positron, EL}},
t2::Type{<:ParticleStateful},
) where {EL <: AbstractFourMomentum} = interaction_result(ParticleStateful{Outgoing, Electron, EL}, t2)
interaction_result(
::Type{ParticleStateful{Outgoing, Positron, EL}},
t2::Type{<:ParticleStateful},
) where {EL <: AbstractFourMomentum} = interaction_result(ParticleStateful{Incoming, Electron, EL}, t2)
# photon commutativity
interaction_result(
t1::Type{ParticleStateful{DIR, Photon, EL}},
t2::Type{<:ParticleStateful},
) where {EL <: AbstractFourMomentum, DIR <: ParticleDirection} = interaction_result(t2, t1)
# but prevent stack overflow
function interaction_result(
t1::Type{ParticleStateful{DIR1, Photon, EL}},
t2::Type{ParticleStateful{DIR2, Photon, EL}},
) where {DIR1 <: ParticleDirection, DIR2 <: ParticleDirection, EL <: AbstractFourMomentum}
@assert false "Invalid interaction between particles of types $t1 and $t2"
end
"""
propagation_result(t1::Type{T}) where {T <: QEDParticle}
Return the type of the inverted direction. E.g.
"""
propagation_result(::Type{ParticleStateful{Incoming, Electron, EL}}) where {EL <: AbstractFourMomentum} =
ParticleStateful{Outgoing, Electron, EL}
propagation_result(::Type{ParticleStateful{Outgoing, Electron, EL}}) where {EL <: AbstractFourMomentum} =
ParticleStateful{Incoming, Electron, EL}
propagation_result(::Type{ParticleStateful{Incoming, Positron, EL}}) where {EL <: AbstractFourMomentum} =
ParticleStateful{Outgoing, Positron, EL}
propagation_result(::Type{ParticleStateful{Outgoing, Positron, EL}}) where {EL <: AbstractFourMomentum} =
ParticleStateful{Incoming, Positron, EL}
propagation_result(::Type{ParticleStateful{Incoming, Photon, EL}}) where {EL <: AbstractFourMomentum} =
ParticleStateful{Outgoing, Photon, EL}
propagation_result(::Type{ParticleStateful{Outgoing, Photon, EL}}) where {EL <: AbstractFourMomentum} =
ParticleStateful{Incoming, Photon, EL}
"""
types(::QEDModel)
Return a Vector of the possible types of particle in the [`QEDModel`](@ref).
"""
function types(::QEDModel)
return [
ParticleStateful{Incoming, Photon, SFourMomentum},
ParticleStateful{Outgoing, Photon, SFourMomentum},
ParticleStateful{Incoming, Electron, SFourMomentum},
ParticleStateful{Outgoing, Electron, SFourMomentum},
ParticleStateful{Incoming, Positron, SFourMomentum},
ParticleStateful{Outgoing, Positron, SFourMomentum},
]
end
# type piracy?
String(::Type{Incoming}) = "Incoming"
String(::Type{Outgoing}) = "Outgoing"
String(::Type{PolX}) = "polx"
String(::Type{PolY}) = "poly"
String(::Type{SpinUp}) = "spinup"
String(::Type{SpinDown}) = "spindown"
String(::Incoming) = "i"
String(::Outgoing) = "o"
function String(::Type{<:ParticleStateful{DIR, Photon}}) where {DIR <: ParticleDirection}
return "k"
end
function String(::Type{<:ParticleStateful{DIR, Electron}}) where {DIR <: ParticleDirection}
return "e"
end
function String(::Type{<:ParticleStateful{DIR, Positron}}) where {DIR <: ParticleDirection}
return "p"
end
"""
caninteract(T1::Type{<:ParticleStateful}, T2::Type{<:ParticleStateful})
For two given `ParticleStateful` types, return whether they can interact at a vertex. This is equivalent to `!issame(T1, T2)`.
See also: [`issame`](@ref) and [`interaction_result`](@ref)
"""
function caninteract(
T1::Type{<:ParticleStateful{D1, S1}},
T2::Type{<:ParticleStateful{D2, S2}},
) where {D1 <: ParticleDirection, S1 <: AbstractParticleType, D2 <: ParticleDirection, S2 <: AbstractParticleType}
if (T1 == T2)
return false
end
if (S1 == Photon && S2 == Photon)
return false
end
for (P1, P2) in [(T1, T2), (T2, T1)]
if (P1 <: ParticleStateful{Incoming, Electron} && P2 <: ParticleStateful{Outgoing, Positron})
return false
end
if (P1 <: ParticleStateful{Outgoing, Electron} && P2 <: ParticleStateful{Incoming, Positron})
return false
end
end
return true
end
function type_index_from_name(::QEDModel, name::String)
if startswith(name, "ki")
return (ParticleStateful{Incoming, Photon, SFourMomentum}, parse(Int, name[3:end]))
elseif startswith(name, "ko")
return (ParticleStateful{Outgoing, Photon, SFourMomentum}, parse(Int, name[3:end]))
elseif startswith(name, "ei")
return (ParticleStateful{Incoming, Electron, SFourMomentum}, parse(Int, name[3:end]))
elseif startswith(name, "eo")
return (ParticleStateful{Outgoing, Electron, SFourMomentum}, parse(Int, name[3:end]))
elseif startswith(name, "pi")
return (ParticleStateful{Incoming, Positron, SFourMomentum}, parse(Int, name[3:end]))
elseif startswith(name, "po")
return (ParticleStateful{Outgoing, Positron, SFourMomentum}, parse(Int, name[3:end]))
else
throw("Invalid name for a particle in the QED model")
end
end
"""
issame(T1::Type{<:ParticleStateful}, T2::Type{<:ParticleStateful})
For two given `ParticleStateful` types, return whether they are equivalent for the purpose of a Feynman Diagram. That means e.g. an `Incoming` `AntiFermion` is the same as an `Outgoing` `Fermion`. This is equivalent to `!caninteract(T1, T2)`.
See also: [`caninteract`](@ref) and [`interaction_result`](@ref)
"""
function issame(T1::Type{<:ParticleStateful}, T2::Type{<:ParticleStateful})
return !caninteract(T1, T2)
end
"""
QED_vertex()
Return the factor of a vertex in a QED feynman diagram.
"""
@inline function QED_vertex()::SLorentzVector{DiracMatrix}
# Peskin-Schroeder notation
return -1im * e * gamma()
end
@inline function QED_inner_edge(p::ParticleStateful)
return propagator(particle_species(p), momentum(p))
end
"""
QED_conserve_momentum(p1::ParticleStateful, p2::ParticleStateful)
Calculate and return a new particle from two given interacting ones at a vertex.
"""
function QED_conserve_momentum(
p1::P1,
p2::P2,
) where {
Dir1 <: ParticleDirection,
Dir2 <: ParticleDirection,
Species1 <: AbstractParticleType,
Species2 <: AbstractParticleType,
P1 <: ParticleStateful{Dir1, Species1},
P2 <: ParticleStateful{Dir2, Species2},
}
P3 = interaction_result(P1, P2)
p1_mom = momentum(p1)
if (Dir1 <: Outgoing)
p1_mom *= -1
end
p2_mom = momentum(p2)
if (Dir2 <: Outgoing)
p2_mom *= -1
end
p3_mom = p1_mom + p2_mom
if (particle_direction(P3) isa Incoming)
return ParticleStateful(particle_direction(P3), particle_species(P3), -p3_mom)
end
return ParticleStateful(particle_direction(P3), particle_species(P3), p3_mom)
end
"""
model(::AbstractProcessDescription)
Return the model of this process description.
"""
model(::GenericQEDProcess) = QEDModel()
model(::PhaseSpacePoint) = QEDModel()
function get_particle(
input::PhaseSpacePoint,
t::Type{ParticleStateful{DIR, SPECIES}},
n::Int,
) where {DIR <: ParticleDirection, SPECIES <: AbstractParticleType}
i = 0
for p in particles(input, DIR())
if p isa t
i += 1
if i == n
return p
end
end
end
@assert false "Invalid type given"
end

View File

@ -1,8 +1,9 @@
#=
"""
show(io::IO, process::QEDProcessDescription)
show(io::IO, process::GenericQEDProcess)
Pretty print an [`QEDProcessDescription`](@ref) (no newlines).
Pretty print an [`GenericQEDProcess`](@ref) (no newlines).
```jldoctest
julia> using MetagraphOptimization
@ -14,7 +15,7 @@ julia> print(parse_process("kk->ep", QEDModel()))
QED Process: 'kk->ep'
```
"""
function show(io::IO, process::QEDProcessDescription)
function show(io::IO, process::GenericQEDProcess)
# types() gives the types in order (QED) instead of random like keys() would
print(io, "QED Process: \'")
for type in types(QEDModel())
@ -34,7 +35,7 @@ end
"""
String(process::QEDProcessDescription)
String(process::GenericQEDProcess)
Create a short string suitable as a filename or similar, describing the given process.
@ -64,7 +65,9 @@ function String(process::QEDProcessDescription)
end
return str
end
=#
#=
"""
show(io::IO, processInput::QEDProcessInput)
@ -92,7 +95,9 @@ function show(io::IO, processInput::QEDProcessInput)
end
return nothing
end
=#
#=
"""
show(io::IO, particle::T) where {T <: QEDParticle}
@ -102,13 +107,14 @@ function show(io::IO, particle::T) where {T <: QEDParticle}
print(io, "$(String(typeof(particle))): $(particle.momentum)")
return nothing
end
=#
"""
show(io::IO, particle::FeynmanParticle)
Pretty print a [`FeynmanParticle`](@ref) (no newlines).
"""
show(io::IO, p::FeynmanParticle) = print(io, "$(String(p.particle))_$(String(direction(p.particle)))_$(p.id)")
show(io::IO, p::FeynmanParticle) = print(io, "$(String(p.particle))_$(String(particle_direction(p.particle)))_$(p.id)")
"""
show(io::IO, particle::FeynmanVertex)

View File

@ -1,10 +0,0 @@
"""
show(io::IO, particleValue::ParticleValue)
Pretty print a [`ParticleValue`](@ref), no newlines.
"""
function show(io::IO, particleValue::ParticleValue)
print(io, "($(particleValue.p), value: $(particleValue.v))")
return nothing
end

View File

@ -1,253 +0,0 @@
ComputeTaskQED_Sum() = ComputeTaskQED_Sum(0)
function _svector_from_type(
processDescription::QEDProcessDescription, type::Type{T}, particles
) where {DIR<:ParticleDirection,T<:QEDParticle{DIR}}
if DIR <: Incoming
l = 0
for (k, v) in in_particles(processDescription)
if T <: k
l = v
break
end
end
return SVector{l,T}(filter(x -> typeof(x) <: T, particles))
elseif DIR <: Outgoing
l = 0
for (k, v) in out_particles(processDescription)
if T <: k
l = v
break
end
end
return SVector{l,T}(filter(x -> typeof(x) <: T, particles))
end
end
"""
gen_process_input(processDescription::QEDProcessDescription)
Return a ProcessInput of randomly generated [`QEDParticle`](@ref)s from a [`QEDProcessDescription`](@ref). The process description can be created manually or parsed from a string using [`parse_process`](@ref).
Note: This uses RAMBO to create a valid process with conservation of momentum and energy.
"""
function gen_process_input(processDescription::QEDProcessDescription)
massSum = 0
inputMasses = Vector{Float64}()
for (particle, n) in processDescription.inParticles
for _ in 1:n
massSum += mass(particle)
push!(inputMasses, mass(particle))
end
end
outputMasses = Vector{Float64}()
for (particle, n) in processDescription.outParticles
for _ in 1:n
massSum += mass(particle)
push!(outputMasses, mass(particle))
end
end
# add some extra random mass to allow for some momentum
massSum += rand(rng[threadid()]) * (length(inputMasses) + length(outputMasses))
particles = Vector{QEDParticle}()
initialMomenta = generate_initial_moms(massSum, inputMasses)
index = 1
for (particle, n) in processDescription.inParticles
for _ in 1:n
mom = initialMomenta[index]
push!(particles, particle(mom))
index += 1
end
end
final_momenta = generate_physical_massive_moms(rng[threadid()], massSum, outputMasses)
index = 1
for (particle, n) in processDescription.outParticles
for _ in 1:n
push!(particles, particle(final_momenta[index]))
index += 1
end
end
inFerms = _svector_from_type(
processDescription, FermionStateful{Incoming,SpinUp}, particles
)
outFerms = _svector_from_type(
processDescription, FermionStateful{Outgoing,SpinUp}, particles
)
inAntiferms = _svector_from_type(
processDescription, AntiFermionStateful{Incoming,SpinUp}, particles
)
outAntiferms = _svector_from_type(
processDescription, AntiFermionStateful{Outgoing,SpinUp}, particles
)
inPhotons = _svector_from_type(
processDescription, PhotonStateful{Incoming,PolX}, particles
)
outPhotons = _svector_from_type(
processDescription, PhotonStateful{Outgoing,PolX}, particles
)
processInput = QEDProcessInput(
processDescription,
inFerms,
outFerms,
inAntiferms,
outAntiferms,
inPhotons,
outPhotons,
)
return processInput
end
"""
gen_graph(process_description::QEDProcessDescription)
For a given [`QEDProcessDescription`](@ref), return the [`DAG`](@ref) that computes it.
"""
function gen_graph(process_description::QEDProcessDescription)
initial_diagram = FeynmanDiagram(process_description)
diagrams = gen_diagrams(initial_diagram)
graph = DAG()
COMPLEX_SIZE = sizeof(ComplexF64)
PARTICLE_VALUE_SIZE = 96.0
# TODO: Not all diagram outputs should always be summed at the end, if they differ by fermion exchange they need to be diffed
# Should not matter for n-Photon Compton processes though
sum_node = insert_node!(
graph, make_node(ComputeTaskQED_Sum(0)); track=false, invalidate_cache=false
)
global_data_out = insert_node!(
graph, make_node(DataTask(COMPLEX_SIZE)); track=false, invalidate_cache=false
)
insert_edge!(graph, sum_node, global_data_out; track=false, invalidate_cache=false)
# remember the data out nodes for connection
dataOutNodes = Dict()
for particle in initial_diagram.particles
# generate data in and U tasks
data_in = insert_node!(
graph,
make_node(DataTask(PARTICLE_VALUE_SIZE), String(particle));
track=false,
invalidate_cache=false,
) # read particle data node
compute_u = insert_node!(
graph, make_node(ComputeTaskQED_U()); track=false, invalidate_cache=false
) # compute U node
data_out = insert_node!(
graph,
make_node(DataTask(PARTICLE_VALUE_SIZE));
track=false,
invalidate_cache=false,
) # transfer data out from u (one ABCParticleValue object)
insert_edge!(graph, data_in, compute_u; track=false, invalidate_cache=false)
insert_edge!(graph, compute_u, data_out; track=false, invalidate_cache=false)
# remember the data_out node for future edges
dataOutNodes[String(particle)] = data_out
end
# TODO: this should be parallelizable somewhat easily
for diagram in diagrams
tie = diagram.tie[]
# handle the vertices
for vertices in diagram.vertices
for vertex in vertices
data_in1 = dataOutNodes[String(vertex.in1)]
data_in2 = dataOutNodes[String(vertex.in2)]
compute_V = insert_node!(
graph,
make_node(ComputeTaskQED_V());
track=false,
invalidate_cache=false,
) # compute vertex
insert_edge!(
graph, data_in1, compute_V; track=false, invalidate_cache=false
)
insert_edge!(
graph, data_in2, compute_V; track=false, invalidate_cache=false
)
data_V_out = insert_node!(
graph,
make_node(DataTask(PARTICLE_VALUE_SIZE));
track=false,
invalidate_cache=false,
)
insert_edge!(
graph, compute_V, data_V_out; track=false, invalidate_cache=false
)
if (vertex.out == tie.in1 || vertex.out == tie.in2)
# out particle is part of the tie -> there will be an S2 task with it later, don't make S1 task
dataOutNodes[String(vertex.out)] = data_V_out
continue
end
# otherwise, add S1 task
compute_S1 = insert_node!(
graph,
make_node(ComputeTaskQED_S1());
track=false,
invalidate_cache=false,
) # compute propagator
insert_edge!(
graph, data_V_out, compute_S1; track=false, invalidate_cache=false
)
data_S1_out = insert_node!(
graph,
make_node(DataTask(PARTICLE_VALUE_SIZE));
track=false,
invalidate_cache=false,
)
insert_edge!(
graph, compute_S1, data_S1_out; track=false, invalidate_cache=false
)
# overrides potentially different nodes from previous diagrams, which is intentional
dataOutNodes[String(vertex.out)] = data_S1_out
end
end
# handle the tie
data_in1 = dataOutNodes[String(tie.in1)]
data_in2 = dataOutNodes[String(tie.in2)]
compute_S2 = insert_node!(
graph, make_node(ComputeTaskQED_S2()); track=false, invalidate_cache=false
)
data_S2 = insert_node!(
graph,
make_node(DataTask(PARTICLE_VALUE_SIZE));
track=false,
invalidate_cache=false,
)
insert_edge!(graph, data_in1, compute_S2; track=false, invalidate_cache=false)
insert_edge!(graph, data_in2, compute_S2; track=false, invalidate_cache=false)
insert_edge!(graph, compute_S2, data_S2; track=false, invalidate_cache=false)
insert_edge!(graph, data_S2, sum_node; track=false, invalidate_cache=false)
add_child!(task(sum_node))
end
return graph
end

View File

@ -1,44 +0,0 @@
"""
parse_process(string::AbstractString, model::QEDModel)
Parse a string representation of a process, such as "ke->ke" into the corresponding [`QEDProcessDescription`](@ref).
"""
function parse_process(str::AbstractString, model::QEDModel)
inParticles = Dict{Type, Int}()
outParticles = Dict{Type, Int}()
if !(contains(str, "->"))
throw("Did not find -> while parsing process \"$str\"")
end
(inStr, outStr) = split(str, "->")
if (isempty(inStr) || isempty(outStr))
throw("Process (\"$str\") input or output part is empty!")
end
for t in types(model)
if (isincoming(t))
inCount = count(x -> x == String(t)[1], inStr)
if inCount != 0
inParticles[t] = inCount
end
end
if (isoutgoing(t))
outCount = count(x -> x == String(t)[1], outStr)
if outCount != 0
outParticles[t] = outCount
end
end
end
if length(inStr) != sum(values(inParticles))
throw("Encountered unknown characters in the input part of process \"$str\"")
elseif length(outStr) != sum(values(outParticles))
throw("Encountered unknown characters in the output part of process \"$str\"")
end
return QEDProcessDescription(inParticles, outParticles)
end

View File

@ -1,532 +0,0 @@
using QEDprocesses
using StaticArrays
import QEDbase.mass
# TODO check
const e = sqrt(4π / 137)
"""
QEDModel <: AbstractPhysicsModel
Singleton definition for identification of the QED-Model.
"""
struct QEDModel <: AbstractPhysicsModel end
"""
QEDParticle
Base type for all particles in the [`QEDModel`](@ref).
Its template parameter specifies the particle's direction.
The concrete types contain singletons of the types that they are, like `Photon` and `Electron` from QEDbase, and their state descriptions.
"""
abstract type QEDParticle{Direction<:ParticleDirection} <: AbstractParticle end
"""
QEDProcessDescription <: AbstractProcessDescription
A description of a process in the QED-Model. Contains the input and output particles.
See also: [`in_particles`](@ref), [`out_particles`](@ref), [`parse_process`](@ref)
"""
struct QEDProcessDescription <: AbstractProcessDescription
inParticles::Dict{Type{<:QEDParticle{Incoming}},Int}
outParticles::Dict{Type{<:QEDParticle{Outgoing}},Int}
end
QEDParticleValue{ParticleType<:QEDParticle} = Union{
ParticleValue{ParticleType,BiSpinor},
ParticleValue{ParticleType,AdjointBiSpinor},
ParticleValue{ParticleType,DiracMatrix},
ParticleValue{ParticleType,SLorentzVector{Float64}},
ParticleValue{ParticleType,ComplexF64},
}
"""
PhotonStateful <: QEDParticle
A photon of the [`QEDModel`](@ref) with its state.
"""
struct PhotonStateful{Direction<:ParticleDirection,Pol<:AbstractDefinitePolarization} <:
QEDParticle{Direction}
momentum::SFourMomentum
function PhotonStateful{Direction,Pol}(
mom::SFourMomentum
) where {Direction<:ParticleDirection,Pol<:AbstractDefinitePolarization}
return new{Direction,Pol}(mom)
end
function PhotonStateful{Direction}(
mom::SFourMomentum
) where {Direction<:ParticleDirection}
return new{Direction,AbstractDefinitePolarization}(mom)
end
function PhotonStateful{Direction}(
mom::SFourMomentum, pol::AbstractDefinitePolarization
) where {Direction<:ParticleDirection}
return new{Direction,typeof(pol)}(mom)
end
function PhotonStateful{Dir,Pol}(ph::PhotonStateful) where {Dir,Pol}
return new{Dir,Pol}(ph.momentum)
end
end
"""
FermionStateful <: QEDParticle
A fermion of the [`QEDModel`](@ref) with its state.
"""
struct FermionStateful{Direction<:ParticleDirection,Spin<:AbstractDefiniteSpin} <:
QEDParticle{Direction}
momentum::SFourMomentum
# TODO: mass for electron/muon/tauon representation?
function FermionStateful{Direction,Spin}(
mom::SFourMomentum
) where {Direction<:ParticleDirection,Spin<:AbstractDefiniteSpin}
return new{Direction,Spin}(mom)
end
function FermionStateful{Direction}(
mom::SFourMomentum
) where {Direction<:ParticleDirection}
return new{Direction,AbstractDefiniteSpin}(mom)
end
function FermionStateful{Direction}(
mom::SFourMomentum, spin::AbstractDefiniteSpin
) where {Direction<:ParticleDirection}
return new{Direction,typeof(spin)}(mom)
end
function FermionStateful{Dir,Spin}(f::FermionStateful) where {Dir,Spin}
return new{Dir,Spin}(f.momentum)
end
end
"""
AntiFermionStateful <: QEDParticle
An anti-fermion of the [`QEDModel`](@ref) with its state.
"""
struct AntiFermionStateful{Direction<:ParticleDirection,Spin<:AbstractDefiniteSpin} <:
QEDParticle{Direction}
momentum::SFourMomentum
# TODO: mass for electron/muon/tauon representation?
function AntiFermionStateful{Direction,Spin}(
mom::SFourMomentum
) where {Direction<:ParticleDirection,Spin<:AbstractDefiniteSpin}
return new{Direction,Spin}(mom)
end
function AntiFermionStateful{Direction}(
mom::SFourMomentum
) where {Direction<:ParticleDirection}
return new{Direction,AbstractDefiniteSpin}(mom)
end
function AntiFermionStateful{Direction}(
mom::SFourMomentum, spin::AbstractDefiniteSpin
) where {Direction<:ParticleDirection}
return new{Direction,typeof(spin)}(mom)
end
function AntiFermionStateful{Dir,Spin}(f::AntiFermionStateful) where {Dir,Spin}
return new{Dir,Spin}(f.momentum)
end
end
"""
interaction_result(t1::Type{T1}, t2::Type{T2}) where {T1 <: QEDParticle, T2 <: QEDParticle}
For two given particle types that can interact, return the third.
"""
function interaction_result(
t1::Type{T1}, t2::Type{T2}
) where {T1<:QEDParticle,T2<:QEDParticle}
@assert false "Invalid interaction between particles of types $t1 and $t2"
end
function interaction_result(
::Type{FermionStateful{Incoming,Spin1}}, ::Type{FermionStateful{Outgoing,Spin2}}
) where {Spin1,Spin2}
return PhotonStateful{Incoming,PolX}
end
function interaction_result(
::Type{FermionStateful{Incoming,Spin1}}, ::Type{AntiFermionStateful{Incoming,Spin2}}
) where {Spin1,Spin2}
return PhotonStateful{Incoming,PolX}
end
function interaction_result(
::Type{FermionStateful{Incoming,Spin1}}, ::Type{<:PhotonStateful}
) where {Spin1}
return FermionStateful{Outgoing,SpinUp}
end
function interaction_result(
::Type{FermionStateful{Outgoing,Spin1}}, ::Type{FermionStateful{Incoming,Spin2}}
) where {Spin1,Spin2}
return PhotonStateful{Incoming,PolX}
end
function interaction_result(
::Type{FermionStateful{Outgoing,Spin1}}, ::Type{AntiFermionStateful{Outgoing,Spin2}}
) where {Spin1,Spin2}
return PhotonStateful{Incoming,PolX}
end
function interaction_result(
::Type{FermionStateful{Outgoing,Spin1}}, ::Type{<:PhotonStateful}
) where {Spin1}
return FermionStateful{Incoming,SpinUp}
end
# antifermion mirror
function interaction_result(
::Type{AntiFermionStateful{Incoming,Spin}}, t2::Type{<:QEDParticle}
) where {Spin}
return interaction_result(FermionStateful{Outgoing,Spin}, t2)
end
function interaction_result(
::Type{AntiFermionStateful{Outgoing,Spin}}, t2::Type{<:QEDParticle}
) where {Spin}
return interaction_result(FermionStateful{Incoming,Spin}, t2)
end
# photon commutativity
function interaction_result(t1::Type{<:PhotonStateful}, t2::Type{<:QEDParticle})
return interaction_result(t2, t1)
end
# but prevent stack overflow
function interaction_result(t1::Type{<:PhotonStateful}, t2::Type{<:PhotonStateful})
@assert false "Invalid interaction between particles of types $t1 and $t2"
end
"""
propagation_result(t1::Type{T}) where {T <: QEDParticle}
Return the type of the inverted direction. E.g.
"""
propagation_result(
::Type{FermionStateful{Incoming,Spin}}
) where {Spin<:AbstractDefiniteSpin} = FermionStateful{Outgoing,Spin}
function propagation_result(
::Type{FermionStateful{Outgoing,Spin}}
) where {Spin<:AbstractDefiniteSpin}
return FermionStateful{Incoming,Spin}
end
function propagation_result(
::Type{AntiFermionStateful{Incoming,Spin}}
) where {Spin<:AbstractDefiniteSpin}
return AntiFermionStateful{Outgoing,Spin}
end
function propagation_result(
::Type{AntiFermionStateful{Outgoing,Spin}}
) where {Spin<:AbstractDefiniteSpin}
return AntiFermionStateful{Incoming,Spin}
end
function propagation_result(
::Type{PhotonStateful{Incoming,Pol}}
) where {Pol<:AbstractDefinitePolarization}
return PhotonStateful{Outgoing,Pol}
end
function propagation_result(
::Type{PhotonStateful{Outgoing,Pol}}
) where {Pol<:AbstractDefinitePolarization}
return PhotonStateful{Incoming,Pol}
end
"""
types(::QEDModel)
Return a Vector of the possible types of particle in the [`QEDModel`](@ref).
"""
function types(::QEDModel)
return [
PhotonStateful{Incoming},
PhotonStateful{Outgoing},
FermionStateful{Incoming},
FermionStateful{Outgoing},
AntiFermionStateful{Incoming},
AntiFermionStateful{Outgoing},
]
end
# type piracy?
String(::Type{Incoming}) = "Incoming"
String(::Type{Outgoing}) = "Outgoing"
String(::Type{PolX}) = "polx"
String(::Type{PolY}) = "poly"
String(::Type{SpinUp}) = "spinup"
String(::Type{SpinDown}) = "spindown"
String(::Incoming) = "i"
String(::Outgoing) = "o"
function String(::Type{<:PhotonStateful})
return "k"
end
function String(::Type{<:FermionStateful})
return "e"
end
function String(::Type{<:AntiFermionStateful})
return "p"
end
function unique_name(::Type{PhotonStateful{Dir,Pol}}) where {Dir,Pol}
return String(PhotonStateful) * String(Dir) * String(Pol)
end
function unique_name(::Type{FermionStateful{Dir,Spin}}) where {Dir,Spin}
return String(FermionStateful) * String(Dir) * String(Spin)
end
function unique_name(::Type{AntiFermionStateful{Dir,Spin}}) where {Dir,Spin}
return String(AntiFermionStateful) * String(Dir) * String(Spin)
end
@inline particle(::PhotonStateful) = Photon()
@inline particle(::FermionStateful) = Electron()
@inline particle(::AntiFermionStateful) = Positron()
@inline particle(::Type{<:PhotonStateful}) = Photon()
@inline particle(::Type{<:FermionStateful}) = Electron()
@inline particle(::Type{<:AntiFermionStateful}) = Positron()
@inline momentum(p::PhotonStateful)::SFourMomentum = p.momentum
@inline momentum(p::FermionStateful)::SFourMomentum = p.momentum
@inline momentum(p::AntiFermionStateful)::SFourMomentum = p.momentum
@inline spin_or_pol(
p::PhotonStateful{Dir,Pol}
) where {Dir,Pol<:AbstractDefinitePolarization} = Pol()
@inline spin_or_pol(p::FermionStateful{Dir,Spin}) where {Dir,Spin<:AbstractDefiniteSpin} =
Spin()
@inline spin_or_pol(
p::AntiFermionStateful{Dir,Spin}
) where {Dir,Spin<:AbstractDefiniteSpin} = Spin()
@inline direction(
::Type{P}
) where {
P<:Union{
FermionStateful{Incoming},AntiFermionStateful{Incoming},PhotonStateful{Incoming}
},
} = Incoming()
@inline direction(
::Type{P}
) where {
P<:Union{
FermionStateful{Outgoing},AntiFermionStateful{Outgoing},PhotonStateful{Outgoing}
},
} = Outgoing()
@inline direction(
::P
) where {
P<:Union{
FermionStateful{Incoming},AntiFermionStateful{Incoming},PhotonStateful{Incoming}
},
} = Incoming()
@inline direction(
::P
) where {
P<:Union{
FermionStateful{Outgoing},AntiFermionStateful{Outgoing},PhotonStateful{Outgoing}
},
} = Outgoing()
@inline isincoming(::QEDParticle{Incoming}) = true
@inline isincoming(::QEDParticle{Outgoing}) = false
@inline isoutgoing(::QEDParticle{Incoming}) = false
@inline isoutgoing(::QEDParticle{Outgoing}) = true
@inline isincoming(::Type{<:QEDParticle{Incoming}}) = true
@inline isincoming(::Type{<:QEDParticle{Outgoing}}) = false
@inline isoutgoing(::Type{<:QEDParticle{Incoming}}) = false
@inline isoutgoing(::Type{<:QEDParticle{Outgoing}}) = true
@inline mass(::Type{<:FermionStateful}) = 1.0
@inline mass(::Type{<:AntiFermionStateful}) = 1.0
@inline mass(::Type{<:PhotonStateful}) = 0.0
@inline invert_momentum(p::FermionStateful{Dir,Spin}) where {Dir,Spin} =
FermionStateful{Dir,Spin}(-p.momentum, p.spin)
@inline invert_momentum(p::AntiFermionStateful{Dir,Spin}) where {Dir,Spin} =
AntiFermionStateful{Dir,Spin}(-p.momentum, p.spin)
@inline invert_momentum(k::PhotonStateful{Dir,Spin}) where {Dir,Spin} =
PhotonStateful{Dir,Spin}(-k.momentum, k.polarization)
"""
caninteract(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle})
For two given [`QEDParticle`](@ref) types, return whether they can interact at a vertex. This is equivalent to `!issame(T1, T2)`.
See also: [`issame`](@ref) and [`interaction_result`](@ref)
"""
function caninteract(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle})
if (T1 == T2)
return false
end
if (T1 <: PhotonStateful && T2 <: PhotonStateful)
return false
end
for (P1, P2) in [(T1, T2), (T2, T1)]
if (P1 <: FermionStateful{Incoming} && P2 <: AntiFermionStateful{Outgoing})
return false
end
if (P1 <: FermionStateful{Outgoing} && P2 <: AntiFermionStateful{Incoming})
return false
end
end
return true
end
function type_index_from_name(::QEDModel, name::String)
if startswith(name, "ki")
return (PhotonStateful{Incoming,PolX}, parse(Int, name[3:end]))
elseif startswith(name, "ko")
return (PhotonStateful{Outgoing,PolX}, parse(Int, name[3:end]))
elseif startswith(name, "ei")
return (FermionStateful{Incoming,SpinUp}, parse(Int, name[3:end]))
elseif startswith(name, "eo")
return (FermionStateful{Outgoing,SpinUp}, parse(Int, name[3:end]))
elseif startswith(name, "pi")
return (AntiFermionStateful{Incoming,SpinUp}, parse(Int, name[3:end]))
elseif startswith(name, "po")
return (AntiFermionStateful{Outgoing,SpinUp}, parse(Int, name[3:end]))
else
throw("Invalid name for a particle in the QED model")
end
end
"""
issame(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle})
For two given [`QEDParticle`](@ref) types, return whether they are equivalent for the purpose of a Feynman Diagram. That means e.g. an `Incoming` `AntiFermion` is the same as an `Outgoing` `Fermion`. This is equivalent to `!caninteract(T1, T2)`.
See also: [`caninteract`](@ref) and [`interaction_result`](@ref)
"""
function issame(T1::Type{<:QEDParticle}, T2::Type{<:QEDParticle})
return !caninteract(T1, T2)
end
"""
QED_vertex()
Return the factor of a vertex in a QED feynman diagram.
"""
@inline function QED_vertex()::SLorentzVector{DiracMatrix}
# Peskin-Schroeder notation
return -1im * e * gamma()
end
@inline function QED_inner_edge(p::QEDParticle)
return QEDbase.propagator(particle(p), p.momentum)
end
"""
QED_conserve_momentum(p1::QEDParticle, p2::QEDParticle)
Calculate and return a new particle from two given interacting ones at a vertex.
"""
function QED_conserve_momentum(
p1::P1, p2::P2
) where {
Dir1<:ParticleDirection,
Dir2<:ParticleDirection,
SpinPol1<:AbstractSpinOrPolarization,
SpinPol2<:AbstractSpinOrPolarization,
P1<:Union{
FermionStateful{Dir1,SpinPol1},
AntiFermionStateful{Dir1,SpinPol1},
PhotonStateful{Dir1,SpinPol1},
},
P2<:Union{
FermionStateful{Dir2,SpinPol2},
AntiFermionStateful{Dir2,SpinPol2},
PhotonStateful{Dir2,SpinPol2},
},
}
P3 = interaction_result(P1, P2)
p1_mom = p1.momentum
if (Dir1 <: Outgoing)
p1_mom *= -1
end
p2_mom = p2.momentum
if (Dir2 <: Outgoing)
p2_mom *= -1
end
p3_mom = p1_mom + p2_mom
if (typeof(direction(P3)) <: Incoming)
return P3(-p3_mom)
end
return P3(p3_mom)
end
"""
QEDProcessInput <: AbstractProcessInput
Input for a QED Process. Contains the [`QEDProcessDescription`](@ref) of the process it is an input for, and the values of the in and out particles.
See also: [`gen_process_input`](@ref)
"""
struct QEDProcessInput{N1,N2,N3,N4,N5,N6,S1,S2,S3,S4,P1,P2} <: AbstractProcessInput
process::QEDProcessDescription
inFerms::SVector{N1,FermionStateful{Incoming,S1}}
outFerms::SVector{N2,FermionStateful{Outgoing,S2}}
inAntiferms::SVector{N3,AntiFermionStateful{Incoming,S3}}
outAntiferms::SVector{N4,AntiFermionStateful{Outgoing,S4}}
inPhotons::SVector{N5,PhotonStateful{Incoming,P1}}
outPhotons::SVector{N6,PhotonStateful{Outgoing,P2}}
end
"""
model(::AbstractProcessDescription)
Return the model of this process description.
"""
model(::QEDProcessDescription) = QEDModel()
model(::QEDProcessInput) = QEDModel()
function copy(process::QEDProcessDescription)
return QEDProcessDescription(copy(process.inParticles), copy(process.outParticles))
end
function ==(p1::QEDProcessDescription, p2::QEDProcessDescription)
return p1.inParticles == p2.inParticles && p1.outParticles == p2.outParticles
end
function in_particles(process::QEDProcessDescription)
return process.inParticles
end
function out_particles(process::QEDProcessDescription)
return process.outParticles
end
function get_particle(
input::QEDProcessInput, t::Type{Particle}, n::Int
)::Particle where {Particle}
if (t <: FermionStateful{Incoming})
return input.inFerms[n]
elseif (t <: FermionStateful{Outgoing})
return input.outFerms[n]
elseif (t <: AntiFermionStateful{Incoming})
return input.inAntiferms[n]
elseif (t <: AntiFermionStateful{Outgoing})
return input.outAntiferms[n]
elseif (t <: PhotonStateful{Incoming})
return input.inPhotons[n]
elseif (t <: PhotonStateful{Outgoing})
return input.outPhotons[n]
end
@assert false "Invalid type given"
end

View File

@ -4,7 +4,7 @@
A greedy implementation of a scheduler, creating a topological ordering of nodes and naively balancing them onto the different devices.
"""
struct GreedyScheduler end
struct GreedyScheduler <: AbstractScheduler end
function schedule_dag(::GreedyScheduler, graph::DAG, machine::Machine)
nodeQueue = PriorityQueue{Node, Int}()

View File

@ -1,10 +1,10 @@
"""
Scheduler
AbstractScheduler
Abstract base type for scheduler implementations. The scheduler is used to assign each node to a device and create a topological ordering of tasks.
"""
abstract type Scheduler end
abstract type AbstractScheduler end
"""
schedule_dag(::Scheduler, ::DAG, ::Machine)

View File

@ -5,10 +5,11 @@ using StaticArrays
Type representing a function call with `N` parameters. Contains the function to call, argument symbols, the return symbol and the device to execute on.
"""
struct FunctionCall{VectorType <: AbstractVector, M}
struct FunctionCall{VectorType <: AbstractVector, N}
func::Function
arguments::VectorType
additional_arguments::SVector{M, Any} # additional arguments (as values) for the function call, will be prepended to the other arguments
# TODO: this should be a tuple
value_arguments::SVector{N, Any} # value arguments for the function call, will be prepended to the other arguments
arguments::VectorType # symbols of the inputs to the function call
return_symbol::Symbol
device::AbstractDevice
end

View File

@ -32,7 +32,7 @@ function get_function_call(
inSymbols::AbstractVector,
outSymbol::Symbol,
) where {CompTask <: AbstractComputeTask}
return [FunctionCall(compute, inSymbols, SVector{1, Any}(t), outSymbol, device)]
return [FunctionCall(compute, SVector{1, Any}(t), inSymbols, outSymbol, device)]
end
function get_function_call(node::ComputeTaskNode)
@ -64,8 +64,8 @@ function get_function_call(node::DataTaskNode)
return [
FunctionCall(
unpack_identity,
SVector{1, Symbol}(Symbol(to_var_name(first(children(node)).id))),
SVector{0, Any}(),
SVector{1, Symbol}(Symbol(to_var_name(first(children(node)).id))),
Symbol(to_var_name(node.id)),
first(children(node)).device,
),
@ -77,8 +77,8 @@ function get_init_function_call(node::DataTaskNode, device::AbstractDevice)
return FunctionCall(
unpack_identity,
SVector{1, Symbol}(Symbol("$(to_var_name(node.id))_in")),
SVector{0, Any}(),
SVector{1, Symbol}(Symbol("$(to_var_name(node.id))_in")),
Symbol(to_var_name(node.id)),
device,
)

View File

@ -3,28 +3,21 @@
Fallback implementation of the compute function of a compute task, throwing an error.
"""
function compute(t::AbstractTask, data...)
return error("Need to implement compute()")
end
function compute end
"""
compute_effort(t::AbstractTask)
Fallback implementation of the compute effort of a task, throwing an error.
"""
function compute_effort(t::AbstractTask)::Float64
# default implementation using compute
return error("Need to implement compute_effort()")
end
function compute_effort end
"""
data(t::AbstractTask)
Fallback implementation of the data of a task, throwing an error.
"""
function data(t::AbstractTask)::Float64
return error("Need to implement data()")
end
function data end
"""
compute_effort(t::AbstractDataTask)

View File

@ -1,3 +1,6 @@
using Roots
using ForwardDiff
"""
noop()
@ -249,7 +252,6 @@ end
first_derivative(func) = x -> ForwardDiff.derivative(func, float(x))
function generate_physical_massive_moms(rng, ss, masses; x0 = 0.1)
n = length(masses)
massless_moms = generate_physical_massless_moms(rng, ss, n)

View File

@ -1,10 +0,0 @@
[deps]
AccurateArithmetic = "22286c92-06ac-501d-9306-4abd417d9753"
QEDbase = "10e22c08-3ccb-4172-bfcf-7d7aa3d04d93"
QEDprocesses = "46de9c38-1bb3-4547-a1ec-da24d767fdad"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

View File

@ -1,5 +1,6 @@
using SafeTestsets
#=
@safetestset "Utility Unit Tests " begin
include("unit_tests_utility.jl")
end
@ -18,9 +19,11 @@ end
@safetestset "ABC-Model Unit Tests " begin
include("unit_tests_abcmodel.jl")
end
=#
@safetestset "QED-Model Unit Tests " begin
include("unit_tests_qedmodel.jl")
end
#=
@safetestset "QED Feynman Diagram Generation Tests" begin
include("unit_tests_qed_diagrams.jl")
end
@ -39,3 +42,4 @@ end
@safetestset "Known Graph Tests " begin
include("known_graphs.jl")
end
=#

View File

@ -22,7 +22,7 @@ trident = ("Trident", parse_process("ke->epe", model), 8)
n_particles = 0
for type in types(model)
if (isincoming(type))
if (is_incoming(type))
n_particles += get(process.inParticles, type, 0)
else
n_particles += get(process.outParticles, type, 0)

View File

@ -1,5 +1,6 @@
using MetagraphOptimization
using QEDbase
using QEDcore
using QEDprocesses
using StatsBase # for countmap
using Random
@ -9,8 +10,6 @@ import MetagraphOptimization.caninteract
import MetagraphOptimization.issame
import MetagraphOptimization.interaction_result
import MetagraphOptimization.propagation_result
import MetagraphOptimization.direction
import MetagraphOptimization.spin_or_pol
import MetagraphOptimization.QED_vertex
def_momentum = SFourMomentum(1.0, 0.0, 0.0, 0.0)
@ -18,32 +17,32 @@ def_momentum = SFourMomentum(1.0, 0.0, 0.0, 0.0)
RNG = Random.MersenneTwister(0)
testparticleTypes = [
PhotonStateful{Incoming,PolX},
PhotonStateful{Outgoing,PolX},
FermionStateful{Incoming,SpinUp},
FermionStateful{Outgoing,SpinUp},
AntiFermionStateful{Incoming,SpinUp},
AntiFermionStateful{Outgoing,SpinUp},
ParticleStateful{Incoming, Photon, SFourMomentum},
ParticleStateful{Outgoing, Photon, SFourMomentum},
ParticleStateful{Incoming, Electron, SFourMomentum},
ParticleStateful{Outgoing, Electron, SFourMomentum},
ParticleStateful{Incoming, Positron, SFourMomentum},
ParticleStateful{Outgoing, Positron, SFourMomentum},
]
testparticleTypesPropagated = [
PhotonStateful{Outgoing,PolX},
PhotonStateful{Incoming,PolX},
FermionStateful{Outgoing,SpinUp},
FermionStateful{Incoming,SpinUp},
AntiFermionStateful{Outgoing,SpinUp},
AntiFermionStateful{Incoming,SpinUp},
ParticleStateful{Outgoing, Photon, SFourMomentum},
ParticleStateful{Incoming, Photon, SFourMomentum},
ParticleStateful{Outgoing, Electron, SFourMomentum},
ParticleStateful{Incoming, Electron, SFourMomentum},
ParticleStateful{Outgoing, Positron, SFourMomentum},
ParticleStateful{Incoming, Positron, SFourMomentum},
]
function compton_groundtruth(input::QEDProcessInput)
function compton_groundtruth(input::PhaseSpacePoint)
# p1k1 -> p2k2
# formula: (ie)^2 (u(p2) slashed(ε1) S(p2 k1) slashed(ε2) u(p1) + u(p2) slashed(ε2) S(p1 + k1) slashed(ε1) u(p1))
p1 = input.inFerms[1]
p2 = input.outFerms[1]
p1 = momentum(psp, Incoming(), 2)
p2 = momentum(psp, Outgoing(), 2)
k1 = input.inPhotons[1]
k2 = input.outPhotons[1]
k1 = momentum(psp, Incoming(), 1)
k2 = momentum(psp, Outgoing(), 1)
u_p1 = base_state(Electron(), Incoming(), p1.momentum, spin_or_pol(p1))
u_p2 = base_state(Electron(), Outgoing(), p2.momentum, spin_or_pol(p2))
@ -87,26 +86,22 @@ end
@test issame(typeof(resultParticle), interaction_result(p1, p2))
totalMom = zero(SFourMomentum)
for (p, mom) in [
(p1, testParticle1.momentum),
(p2, testParticle2.momentum),
(p3, resultParticle.momentum),
]
if (typeof(direction(p)) <: Incoming)
for (p, mom) in [(p1, momentum(testParticle1)), (p2, momentum(testParticle2)), (p3, momentum(resultParticle))]
if (typeof(particle_direction(p)) <: Incoming)
totalMom += mom
else
totalMom -= mom
end
end
@test isapprox(totalMom, zero(SFourMomentum); atol=sqrt(eps()))
@test isapprox(totalMom, zero(SFourMomentum); atol = sqrt(eps()))
end
end
@testset "Propagation Result" begin
for (p, propResult) in zip(testparticleTypes, testparticleTypesPropagated)
@test issame(propagation_result(p), propResult)
@test direction(propagation_result(p)(def_momentum)) != direction(p(def_momentum))
@test particle_direction(propagation_result(p)(def_momentum)) != particle_direction(p(def_momentum))
end
end
@ -116,64 +111,27 @@ end
@test parse_process("ke->ke", QEDModel()) == parse_process("ek->ek", QEDModel())
@test parse_process("ke->ke", QEDModel()) == parse_process("ke->ek", QEDModel())
@test parse_process("kkke->eep", QEDModel()) ==
parse_process("kkek->epe", QEDModel())
@test parse_process("kkke->eep", QEDModel()) == parse_process("kkek->epe", QEDModel())
end
@testset "Known processes" begin
compton_process = QEDProcessDescription(
Dict{Type,Int}(
PhotonStateful{Incoming,PolX} => 1, FermionStateful{Incoming,SpinUp} => 1
),
Dict{Type,Int}(
PhotonStateful{Outgoing,PolX} => 1, FermionStateful{Outgoing,SpinUp} => 1
),
)
compton_process = GenericQEDProcess(1, 1, 1, 1, 0, 0)
@test parse_process("ke->ke", QEDModel()) == compton_process
positron_compton_process = QEDProcessDescription(
Dict{Type,Int}(
PhotonStateful{Incoming,PolX} => 1,
AntiFermionStateful{Incoming,SpinUp} => 1,
),
Dict{Type,Int}(
PhotonStateful{Outgoing,PolX} => 1,
AntiFermionStateful{Outgoing,SpinUp} => 1,
),
)
positron_compton_process = GenericQEDProcess(1, 1, 0, 0, 1, 1)
@test parse_process("kp->kp", QEDModel()) == positron_compton_process
trident_process = QEDProcessDescription(
Dict{Type,Int}(
PhotonStateful{Incoming,PolX} => 1, FermionStateful{Incoming,SpinUp} => 1
),
Dict{Type,Int}(
FermionStateful{Outgoing,SpinUp} => 2,
AntiFermionStateful{Outgoing,SpinUp} => 1,
),
)
trident_process = GenericQEDProcess(1, 0, 1, 2, 0, 1)
@test parse_process("ke->eep", QEDModel()) == trident_process
pair_production_process = QEDProcessDescription(
Dict{Type,Int}(PhotonStateful{Incoming,PolX} => 2),
Dict{Type,Int}(
FermionStateful{Outgoing,SpinUp} => 1,
AntiFermionStateful{Outgoing,SpinUp} => 1,
),
)
pair_production_process = GenericQEDProcess(2, 0, 0, 1, 0, 1)
@test parse_process("kk->pe", QEDModel()) == pair_production_process
pair_annihilation_process = QEDProcessDescription(
Dict{Type,Int}(
FermionStateful{Incoming,SpinUp} => 1,
AntiFermionStateful{Incoming,SpinUp} => 1,
),
Dict{Type,Int}(PhotonStateful{Outgoing,PolX} => 2),
)
pair_annihilation_process = GenericQEDProcess(0, 2, 1, 0, 1, 0)
@test parse_process("pe->kk", QEDModel()) == pair_annihilation_process
end
@ -187,17 +145,17 @@ end
for i in 1:100
input = gen_process_input(process)
@test length(input.inFerms) ==
get(process.inParticles, FermionStateful{Incoming,SpinUp}, 0)
get(process.inParticles, ParticleStateful{Incoming, Electron, SFourMomentum}, 0)
@test length(input.inAntiferms) ==
get(process.inParticles, AntiFermionStateful{Incoming,SpinUp}, 0)
get(process.inParticles, ParticleStateful{Incoming, Positron, SFourMomentum}, 0)
@test length(input.inPhotons) ==
get(process.inParticles, PhotonStateful{Incoming,PolX}, 0)
get(process.inParticles, ParticleStateful{Incoming, Photon, SFourMomentum}, 0)
@test length(input.outFerms) ==
get(process.outParticles, FermionStateful{Outgoing,SpinUp}, 0)
get(process.outParticles, ParticleStateful{Outgoing, Electron, SFourMomentum}, 0)
@test length(input.outAntiferms) ==
get(process.outParticles, AntiFermionStateful{Outgoing,SpinUp}, 0)
get(process.outParticles, ParticleStateful{Outgoing, Positron, SFourMomentum}, 0)
@test length(input.outPhotons) ==
get(process.outParticles, PhotonStateful{Outgoing,PolX}, 0)
get(process.outParticles, ParticleStateful{Outgoing, Photon, SFourMomentum}, 0)
@test isapprox(
sum([
@ -210,12 +168,13 @@ end
getfield.(input.outAntiferms, :momentum)...,
getfield.(input.outPhotons, :momentum)...,
]);
atol=sqrt(eps()),
atol = sqrt(eps()),
)
end
end
end
#=
@testset "Compton" begin
import MetagraphOptimization.insert_node!
import MetagraphOptimization.insert_edge!
@ -381,3 +340,4 @@ end
@test isapprox(compute_function.(input), reduced_compute_function.(input))
end
end
=#