commit a959137e221a8ccd628acc15a25da7cfa9524b50 Author: Anton Reinhard Date: Mon Apr 1 21:52:05 2024 +0200 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9d1611 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +**/Manifest.toml diff --git a/Project.toml b/Project.toml new file mode 100644 index 0000000..05e041b --- /dev/null +++ b/Project.toml @@ -0,0 +1,4 @@ +name = "FeynmanDiagramGenerator" +uuid = "8f7ec768-9801-4863-9e03-72391fd2204f" +authors = ["antonr "] +version = "0.1.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ec7d1a --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ + +# FeynmanDiagrams.jl + +Generator for FeynmanDiagrams of any QFT as long as all vertices have exactly 3 legs. + +Very much WIP. diff --git a/src/FeynmanDiagramGenerator.jl b/src/FeynmanDiagramGenerator.jl new file mode 100644 index 0000000..af60273 --- /dev/null +++ b/src/FeynmanDiagramGenerator.jl @@ -0,0 +1,9 @@ +module FeynmanDiagramGenerator + +export Forest + +include("trees/trees.jl") +include("trees/iterator.jl") +include("trees/print.jl") + +end # module FeynmanDiagramGenerator diff --git a/src/trees/iterator.jl b/src/trees/iterator.jl new file mode 100644 index 0000000..7e9ee74 --- /dev/null +++ b/src/trees/iterator.jl @@ -0,0 +1,102 @@ + +mutable struct ForestIterator + # how many leaves do the left subtrees have + left_leaves::Int + left_subiterator::Union{ForestIterator,Missing} + + # how many leaves do the right subtrees have + right_leaves::Int + right_subiterator::Union{ForestIterator,Missing} + + # counter is like an id and incremented each time _iterate is called + # important for symmetry breaking for iterators where n is even and the subtrees can have equal leave numbers + counter::Int +end + +function ForestIterator(n::Int) + @assert n >= 1 "Cannot create a ForestIterator with fewer than 1 leaves" + if n == 1 + return missing + end + return ForestIterator(1, ForestIterator(1), n - 1, ForestIterator(n - 1), 1) +end + +ForestIterator(forest::Forest) = ForestIterator(forest.leaves) + +""" + IteratorSize(::Forest) + +Would be OEIS sequence A001190, Wedderburn-Etherington numbers: Number of unlabeled binary rooted trees with n leaves. +""" +Base.IteratorSize(::Forest) = Base.SizeUnknown() +Base.IteratorSize(::ForestIterator) = Base.SizeUnknown() + +""" + is_end(::ForestIterator) + +Return whether the given [`ForestIterator`](@ref) is exhausted. +""" +is_end(::Missing) = true +is_end(iterator::ForestIterator) = iterator.left_leaves + 1 >= iterator.right_leaves && is_end(iterator.left_subiterator) && is_end(iterator.right_subiterator) + +""" + tree(iterator::ForestIterator) + +Return an [`AbstractTree`](@ref) made from the current state of the iterator. +""" +tree(::Missing) = EmptyTree() +function tree(iterator::ForestIterator) + return Subtree(tree(iterator.left_subiterator), tree(iterator.right_subiterator)) +end + +""" + _iterate(iterator::ForestIterator) + +Internal function for iterating the [`ForestIterator`](@ref) once. +""" +function _iterate(iterator::ForestIterator) + @assert !is_end(iterator) "Trying to iterate a forest iterator that is already at its end" + iterator.counter += 1 + # reverse direction, iterate right first, then left (reset right), then try new balance + if !is_end(iterator.right_subiterator) + _iterate(iterator.right_subiterator) + return nothing + elseif !is_end(iterator.left_subiterator) + iterator.right_subiterator = ForestIterator(iterator.right_leaves) + + if (iterator.left_leaves == iterator.right_leaves) + # if the subiterators have equal lengths, take care to start the one we reset where the other one currently is (to not get duplicates) + # this actually only changes things at forest(8) and higher + while (iterator.right_subiterator.counter <= iterator.left_subiterator.counter) + _iterate(iterator.right_subiterator) + end + end + _iterate(iterator.left_subiterator) + + return nothing + else + iterator.right_leaves -= 1 + iterator.left_leaves += 1 + iterator.right_subiterator = ForestIterator(iterator.right_leaves) + iterator.left_subiterator = ForestIterator(iterator.left_leaves) + return nothing + end +end + +function Base.iterate(forest::Forest) + state = ForestIterator(forest.leaves) + return (tree(state), state) +end + +function Base.iterate(forest::Forest, state::Missing) + return nothing +end + +function Base.iterate(forest::Forest, state::ForestIterator) + if (is_end(state)) + return nothing + end + + _iterate(state) + return (tree(state), state) +end diff --git a/src/trees/print.jl b/src/trees/print.jl new file mode 100644 index 0000000..9c85f13 --- /dev/null +++ b/src/trees/print.jl @@ -0,0 +1,8 @@ + +function Base.show(io::IO, tree::EmptyTree) + print(io, "T1") +end + +function Base.show(io::IO, tree::Subtree) + print(io, "T$(leaves(tree))<$(tree.left)> <$(tree.right)>") +end diff --git a/src/trees/trees.jl b/src/trees/trees.jl new file mode 100644 index 0000000..3415385 --- /dev/null +++ b/src/trees/trees.jl @@ -0,0 +1,60 @@ +# The subtrees of tree-level feynman diagrams +# the subtrees are always binary since we assume that all feynman vertices have 3 leaves +# the order of left and right don't matter because we are only interested in topologies + +import Base.iterate + +""" + AbstractTree + +Base type for trees, such as [`EmptyTree`](@ref) or [`Subtree`](@ref). + +Implementations provide [`leaves`](@ref), returning the number of leaves of the tree. +""" +abstract type AbstractTree end + +""" + EmptyTree <: AbstractTree + +A singleton type representing an empty tree, which has 1 leaf, see [`leaves`](@ref). +""" +struct EmptyTree <: AbstractTree end + +""" + Subtree <: AbstractTree + +Representation of a specific subtree topology, with two subtrees of its own. +""" +struct Subtree <: AbstractTree + left::AbstractTree + right::AbstractTree +end + +""" + Forest + +Representation of a forest containing all topologically unique trees with a given number of leaves. +Use [`iterate`](@ref) to iterate through the [`Subtree`](@ref)s. +""" +struct Forest + leaves::Int + + function Forest(n::Int) + @assert n > 0 "Can not create forest of trees with $n leaves, must be at least 1" + new(n) + end +end + + +""" + leaves(tree::AbstractTree) + +Returns the number of leaves or leaves of the tree. +""" +function leaves end + +leaves(tree::EmptyTree) = 1 + +leaves(tree::Subtree) = leaves(tree.left) + leaves(tree.right) + +leaves(forest::Forest) = forest.leaves diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 0000000..4e8527a --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,3 @@ +[deps] +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 0000000..ce35902 --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,5 @@ +using SafeTestsets + +@safetestset "Trees" begin + include("trees.jl") +end diff --git a/test/trees.jl b/test/trees.jl new file mode 100644 index 0000000..d2e1641 --- /dev/null +++ b/test/trees.jl @@ -0,0 +1,26 @@ +using FeynmanDiagramGenerator + +import FeynmanDiagramGenerator.EmptyTree +import FeynmanDiagramGenerator.Subtree +import FeynmanDiagramGenerator.leaves +import FeynmanDiagramGenerator.ForestIterator + +@testset "Number of leaves" begin + @test leaves(EmptyTree()) == 1 + @test leaves(Subtree(EmptyTree(), EmptyTree())) == 2 + @test leaves(Subtree(EmptyTree(), Subtree(EmptyTree(), EmptyTree()))) == 3 +end + +@testset "Number of Forests" begin + @test_throws AssertionError Forest(0) + @test_throws AssertionError ForestIterator(0) + + groundtruth_lengths = [1, 1, 1, 2, 3, 6, 11, 23, 46, 98, 207, 451, 983, 2179, 4850, 10905, 24631, 56011, 127912, 293547] + + @testset "Forest with $i leaves" for i in 1:20 + @test length([t for t in Forest(i)]) == groundtruth_lengths[i] + for t in Forest(i) + @test leaves(t) == i + end + end +end