diff --git a/docs/src/developer_interfaces.md b/docs/src/developer_interfaces.md index 6379c07a..685a461d 100644 --- a/docs/src/developer_interfaces.md +++ b/docs/src/developer_interfaces.md @@ -1,32 +1,83 @@ # Developer Interfaces `RootedTrees.jl` exposes concrete tree types and iterator types for use in -generic Julia code. This page records the supported contracts. It does not make -the package's internal abstract supertypes extension APIs. +generic Julia code. This page records the supported contracts and the boundary +between those contracts and implementation details. + +## Tree contract + +The supported tree inputs are [`RootedTree`](@ref), +[`ColoredRootedTree`](@ref), and its [`BicoloredRootedTree`](@ref) alias. Create +them with [`rootedtree`](@ref) or [`rootedtree!`](@ref) so that the level +sequence is validated and canonicalized. + +Code consuming a tree generically should use the public functions below: + +- [`order`](@ref) returns the number of nodes. +- [`SubtreeIterator`](@ref) traverses the child subtrees without requiring a + concrete tree type. +- [`symmetry`](@ref), [`density`](@ref), [`α`](@ref), and [`β`](@ref) compute + standard tree invariants. +- [`root_color`](@ref) reads the root color of a colored tree. +- [`partition_skeleton`](@ref) and [`PartitionIterator`](@ref) expose the + partition interface for rooted and colored trees. + +Consumers must not assume that a tree's `level_sequence` is an ordinary +one-based `Vector`, or that a yielded tree owns its storage. Use +[`rootedtree`](@ref) to make an owned canonical copy when that is required. + +`AbstractRootedTree` and `AbstractTimeIntegrationMethod` are internal +classification types. They are deliberately not exported and do not define a +stable third-party subtyping contract. The package's algorithms rely on +canonicalization, copying, and internal tree-buffer operations that are not +part of the public extension surface. External code should use the concrete +types and public functions above rather than subtype either abstract type. ## Iterator contract [`RootedTreeIterator`](@ref), [`BicoloredRootedTreeIterator`](@ref), [`PartitionForestIterator`](@ref), [`PartitionIterator`](@ref), and -[`SplittingIterator`](@ref) implement Julia's standard iterator interface: +[`SplittingIterator`](@ref) implement the standard Julia iterator interface: - `iterate(iter)` starts iteration and `iterate(iter, state)` advances it. - `eltype(iter)` describes each yielded value. - `length(iter)` returns the exact number of yielded values. -These iterators are intended to be consumed by generic code using those Base -functions. Several implementations reuse mutable buffers to avoid allocations: +`PartitionIterator` yields `(forest_iterator, skeleton)` pairs, while +`SplittingIterator` yields `(forest, subtree)` pairs. The other iterators yield +trees. These iterators are intended to be consumed through generic Base +functions such as `iterate`, `length`, `eltype`, `for`, and `collect`. + +[`SubtreeIterator`](@ref) is the exception: it is a lazy traversal helper that +guarantees only the two-argument `iterate` protocol. It intentionally does not +provide `length` or `eltype`; consume it with a `for` loop or a manual generic +`iterate` loop. + +Several implementations reuse mutable buffers to avoid allocations. `RootedTreeIterator`, `BicoloredRootedTreeIterator`, `PartitionForestIterator`, `PartitionIterator`, and `SplittingIterator` may mutate a previously yielded tree, forest, or skeleton when advanced. Copy data -before storing it or passing it to asynchronous work. +before storing it or passing it to asynchronous work. `SubtreeIterator` may +also yield views into the input tree. -## Tree implementation boundary +## Generic usage -`AbstractRootedTree` and `AbstractTimeIntegrationMethod` are internal -classification types. They are deliberately not exported and have no stable -third-party-subtyping contract. The algorithms that dispatch on -`AbstractRootedTree` require package-specific state, canonicalization, and -subtree-iteration operations; `AbstractTimeIntegrationMethod` is only a shared -supertype for the package's own coefficient containers. Use the documented -concrete types and public functions instead of extending either abstract type. +The interface tests exercise consumers that only call the public operations +above. A generic consumer should accept a tree or iterator without dispatching +on its concrete representation, for example: + +```julia +function count_items(iter) + state = iterate(iter) + result = 0 + while state !== nothing + result += 1 + state = iterate(iter, last(state)) + end + return result +end +``` + +Do not call internal functions such as `canonical_representation!`, +`unsafe_resize!`, or `unsafe_copyto!` from downstream code. They are used by the +package's own implementations and may change without a public API guarantee. diff --git a/src/RootedTrees.jl b/src/RootedTrees.jl index f34f403a..0b2ecb41 100644 --- a/src/RootedTrees.jl +++ b/src/RootedTrees.jl @@ -108,6 +108,15 @@ a vector of integers representing the levels of each node of the tree. - `level_sequence`: An integer vector satisfying the rooted-tree level-sequence rules. The input is not mutated. +# Returns + +- `RootedTree`: Canonical rooted tree backed by a copy of `level_sequence`. + +# Throws + +- `ArgumentError`: If `level_sequence` is not a valid rooted-tree level + sequence. + # Examples ```jldoctest @@ -162,6 +171,10 @@ may be modified in this process. See also [`rootedtree`](@ref). - `level_sequence`: A mutable integer vector satisfying the rooted-tree level-sequence rules. Its contents may be reordered in place. +# Returns + +- `RootedTree`: Canonical rooted tree backed by `level_sequence`. + !!! warning This may modify the `level_sequence` and further modifications of the `level_sequence` may invalidate the rooted tree returned by this function. @@ -632,6 +645,18 @@ stored or modified during the iteration, a `copy` has to be made. This iterator implements `iterate`, `eltype`, and `length`. Iteration reuses one mutable tree buffer; copy a yielded tree before retaining it. Computing `length` enumerates the trees. + +# Examples + +```jldoctest +julia> trees = collect(RootedTreeIterator(2)); + +julia> length(trees) +1 + +julia> butcher_representation(first(trees)) +"[τ]" +``` """ struct RootedTreeIterator{T <: Integer} order::T @@ -691,7 +716,26 @@ end """ count_trees(order) -Counts all rooted trees of `order`. +Count all rooted trees with `order` nodes. + +# Arguments + +- `order::Integer`: Number of nodes in each tree. Must be nonnegative. + +# Returns + +- `Int`: Number of canonical rooted trees of the requested order. + +# Throws + +- `ArgumentError`: If `order` is negative. + +# Examples + +```jldoctest +julia> count_trees(4) +4 +``` """ function count_trees(order) order < 0 && throw(ArgumentError("The `order` must be at least zero.")) @@ -712,6 +756,27 @@ tree `t`. Similar to [`RootedTreeIterator`](@ref), you should `copy` the iterates if you want to store or modify them during the iteration since they may be views to internal caches. + +# Arguments + +- `t`: Rooted tree whose child subtrees should be traversed. + +# Iterator interface + +This lazy iterator guarantees the two-argument `iterate` protocol. It yields +each subtree rooted at a child of `t` in level-sequence order. The yielded trees +may share storage with `t`; copy a value before retaining or mutating it. + +Use a `for` loop or a manual `iterate` consumer. This type does not guarantee +`length` or `eltype` and is therefore not intended to be materialized with +`collect`. + +# Examples + +```jldoctest +julia> sum(1 for _ in SubtreeIterator(rootedtree([1, 2, 2]))) +2 +``` """ struct SubtreeIterator{Tree <: AbstractRootedTree} t::Tree @@ -748,9 +813,25 @@ end """ subtrees(t::RootedTree) -Returns a vector of all subtrees of `t`. +Return an allocated vector containing all child subtrees of `t`. + +# Arguments + +- `t::RootedTree`: Rooted tree to decompose. + +# Returns + +- `Vector{<:RootedTree}`: One tree for each child subtree of `t`. The result is + independent of `t` and can be retained safely. See also [`SubtreeIterator`](@ref). + +# Examples + +```jldoctest +julia> length(subtrees(rootedtree([1, 2, 2]))) +2 +``` """ function subtrees(t::RootedTree) subtr = typeof(t)[] @@ -779,9 +860,33 @@ Form the partition forest of the rooted tree `t` where edges marked with `false` in the `edge_set` are removed. The ith value in the Boolean iterable `edge_set` corresponds to the edge connecting node `i+1` in the level sequence to its parent. +# Arguments + +- `t::RootedTree`: Rooted tree to partition. +- `edge_set`: Boolean iterable of length `order(t) - 1`; `false` removes the + corresponding edge and `true` keeps it. + +# Returns + +- `Vector{<:RootedTree}`: Connected trees in the partition forest, ordered from + the deepest removed subtree to the remaining tree. + +# Throws + +- `AssertionError`: If `edge_set` does not have one entry per non-root node. + See also [`partition_skeleton`](@ref), [`PartitionIterator`](@ref), and [`PartitionForestIterator`](@ref). +# Examples + +```jldoctest +julia> forest = partition_forest(rootedtree([1, 2, 2]), Bool[false, true]); + +julia> length(forest) +2 +``` + # References Section 2.3 of @@ -834,6 +939,26 @@ Similar to [`RootedTreeIterator`](@ref), you should `copy` the iterates if you want to store or modify them during the iteration since they may be views to internal caches. +# Arguments + +- `t::AbstractRootedTree`: Tree whose partition forests are enumerated. +- `edge_set`: Boolean vector of length `order(t) - 1`. Each `false` edge is + removed in the next forest. + +# Iterator interface + +This iterator implements `iterate`, `eltype`, and `length`. It yields one tree +for each possible forest obtained by removing a suffix-compatible set of +edges. The yielded tree uses internal working storage; copy it before storing +it. + +# Examples + +```jldoctest +julia> length(PartitionForestIterator(rootedtree([1, 2, 2]), Bool[false, true])) +2 +``` + See also [`partition_forest`](@ref), [`partition_skeleton`](@ref), and [`PartitionIterator`](@ref). @@ -923,8 +1048,32 @@ Form the partition skeleton of the rooted tree `t`, i.e., the rooted tree obtained by contracting each tree of the partition forest to a single vertex and re-establishing the edges removed to obtain the partition forest. +# Arguments + +- `t::AbstractRootedTree`: Rooted tree to partition. +- `edge_set`: Boolean iterable of length `order(t) - 1`; the same convention as + [`partition_forest`](@ref) is used. + +# Returns + +- `AbstractRootedTree`: Canonical partition skeleton with the same concrete tree + type as `t`. + +# Throws + +- `AssertionError`: If `edge_set` does not have one entry per non-root node. + See also [`partition_forest`](@ref) and [`PartitionIterator`](@ref). +# Examples + +```jldoctest +julia> skeleton = partition_skeleton(rootedtree([1, 2, 2]), Bool[false, true]); + +julia> order(skeleton) +2 +``` + # References Section 2.3 (and Section 6.1 for colored trees) of @@ -986,8 +1135,24 @@ Create all partition forests and skeletons of a rooted tree `t`. This returns vectors of the return values of [`partition_forest`](@ref) and [`partition_skeleton`](@ref) when looping over all possible edge sets. +# Arguments + +- `t::RootedTree`: Rooted tree whose edge partitions should be enumerated. + +# Returns + +- `NamedTuple`: A pair of vectors `(forests, skeletons)`. Corresponding entries + describe the same edge set, and there are `2^(order(t) - 1)` entries. + See also [`PartitionIterator`](@ref). +# Examples + +```jldoctest +julia> length(all_partitions(rootedtree([1, 2, 2])).forests) +4 +``` + # References Section 2.3 of @@ -1032,6 +1197,23 @@ Similar to [`RootedTreeIterator`](@ref), you should `copy` the iterates if you want to store or modify them during the iteration since they may be views to internal caches. +# Arguments + +- `t::AbstractRootedTree`: Rooted tree whose partitions should be enumerated. + +# Iterator interface + +This iterator implements `iterate`, `eltype`, and `length`. Each value is a +`(forest_iterator, skeleton)` pair. The forest iterator and skeleton use +working storage and must be copied before being retained across iterations. + +# Examples + +```jldoctest +julia> length(PartitionIterator(rootedtree([1, 2, 2]))) +4 +``` + See also [`partition_forest`](@ref), [`partition_skeleton`](@ref), and [`PartitionForestIterator`](@ref). @@ -1180,8 +1362,24 @@ end Create all splitting forests and subtrees associated to ordered subtrees of a rooted tree `t`. +# Arguments + +- `t::RootedTree`: Rooted tree whose ordered subtrees define the splittings. + +# Returns + +- `NamedTuple`: Vectors `forests` and `subtrees`, with matching entries for + every valid ordered splitting. + See also [`SplittingIterator`](@ref). +# Examples + +```jldoctest +julia> length(all_splittings(rootedtree([1, 2, 2])).forests) +5 +``` + # References Section 2.2 of @@ -1250,6 +1448,13 @@ This iterator implements `iterate`, `eltype`, and `length`. Each iterate is a `(forest, subtree)` pair. The `forest` vector and its trees are mutable working storage; copy values that must outlive the next iteration. +# Examples + +```jldoctest +julia> first(collect(SplittingIterator(rootedtree([1, 2, 2]))))[2] isa RootedTree +true +``` + See also [`partition_forest`](@ref) and [`partition_skeleton`](@ref). # References @@ -1338,7 +1543,23 @@ end """ order(t::AbstractRootedTree) -The `order` of a rooted tree `t`, i.e., the length of its level sequence. +Return the `order` of a rooted tree `t`, i.e., the number of nodes in its level +sequence. + +# Arguments + +- `t::AbstractRootedTree`: Rooted tree to inspect. + +# Returns + +- `Int`: Number of nodes in `t`. + +# Examples + +```jldoctest +julia> order(rootedtree([1, 2, 2])) +3 +``` """ order(t::AbstractRootedTree) = length(t.level_sequence) @@ -1349,6 +1570,22 @@ order(t::AbstractRootedTree) = length(t.level_sequence) The symmetry `σ` of a rooted tree `t`, i.e., the order of the group of automorphisms on a particular labelling (of the vertices) of `t`. +# Arguments + +- `t::AbstractRootedTree`: Rooted tree to inspect. It is canonicalized if + necessary without modifying the input. + +# Returns + +- `Integer`: Symmetry factor of `t`. + +# Examples + +```jldoctest +julia> symmetry(rootedtree([1, 2, 2])) +2 +``` + Reference: Section 301 of - Butcher, John Charles. Numerical methods for ordinary differential equations. @@ -1394,6 +1631,20 @@ function symmetry(t::AbstractRootedTree) return result end +""" + σ(t::AbstractRootedTree) + +Alias for [`symmetry`](@ref). Return the order of the automorphism group of the +rooted tree `t`. + +# Arguments + +- `t::AbstractRootedTree`: Rooted tree to inspect. + +# Returns + +- `Integer`: Symmetry factor of `t`. +""" const σ = symmetry """ @@ -1403,6 +1654,21 @@ const σ = symmetry The density `γ(t)` of a rooted tree, i.e., the product over all vertices of `t` of the order of the subtree rooted at that vertex. +# Arguments + +- `t::AbstractRootedTree`: Rooted tree to inspect. + +# Returns + +- `Integer`: Density of `t`; the empty tree has density one. + +# Examples + +```jldoctest +julia> density(rootedtree([1, 2, 2])) +3 +``` + Reference: Section 301 of - Butcher, John Charles. Numerical methods for ordinary differential equations. @@ -1418,6 +1684,20 @@ function density(t::AbstractRootedTree) return result end +""" + γ(t::AbstractRootedTree) + +Alias for [`density`](@ref). Return the product of the orders of the subtrees +rooted at every vertex of `t`. + +# Arguments + +- `t::AbstractRootedTree`: Rooted tree to inspect. + +# Returns + +- `Integer`: Density of `t`. +""" const γ = density """ @@ -1425,6 +1705,21 @@ const γ = density The number of monotonic labelings of `t` not equivalent under the symmetry group. +# Arguments + +- `t::AbstractRootedTree`: Rooted tree to label. + +# Returns + +- `Integer`: Number of inequivalent monotonic labelings. + +# Examples + +```jldoctest +julia> α(rootedtree([1, 2, 2])) +1 +``` + Reference: Section 302 of - Butcher, John Charles. Numerical methods for ordinary differential equations. @@ -1439,6 +1734,21 @@ end The total number of labelings of `t` not equivalent under the symmetry group. +# Arguments + +- `t::AbstractRootedTree`: Rooted tree to label. + +# Returns + +- `Integer`: Number of inequivalent labelings. + +# Examples + +```jldoctest +julia> β(rootedtree([1, 2, 2])) +3 +``` + Reference: Section 302 of - Butcher, John Charles. Numerical methods for ordinary differential equations. @@ -1475,6 +1785,27 @@ Compute the non-associative Butcher product `t = t1 ∘ t2` of rooted trees in-place. It is formed by adding an edge from the root of `t1` to the root of `t2`. +# Arguments + +- `t::RootedTree`: Mutable destination with enough storage for the result. +- `t1::RootedTree`: Left factor of the Butcher product. +- `t2::RootedTree`: Right factor of the Butcher product. + +# Returns + +- `RootedTree`: The mutated destination `t`, in canonical representation. + +# Examples + +```jldoctest +julia> t = rootedtree([1]); + +julia> butcher_product!(t, rootedtree([1]), rootedtree([1])); + +julia> butcher_representation(t) +"[τ]" +``` + See also [`∘`](@ref) (available as `\\circ` plus TAB). Reference: Section 301 of @@ -1510,13 +1841,23 @@ end """ butcher_representation(t::RootedTree) -Returns the representation of `t::RootedTree` introduced by Butcher as a string. +Return the representation of `t::RootedTree` introduced by Butcher as a string. Thus, the rooted tree consisting whose only vertex is the root itself is represented as `τ`. The representation of other trees is defined recursively; if `t₁, t₂, ... tₙ` are the [`subtrees`](@ref) of the rooted tree `t`, it is represented as `t = [t₁ t₂ ... tₙ]`. If multiple subtrees are the same, their number of occurrences is written as a power. +# Arguments + +- `t::RootedTree`: Rooted tree to represent. +- `normalize::Bool=true`: Whether repeated leaf subtrees should be written as + superscript powers. + +# Returns + +- `String`: Butcher bracket representation of `t`. + # Examples ```jldoctest @@ -1578,6 +1919,21 @@ end Deprecated alias for [`elementary_differential_latexstring`](@ref). Use [`elementary_differential_latexstring`](@ref) for new code. + +# Arguments + +- `t::RootedTree`: Rooted tree whose elementary differential is represented. + +# Returns + +- `LaTeXString`: Deprecated elementary-differential representation. + +# Examples + +```jldoctest +julia> elementary_differential(rootedtree([1])) isa AbstractString +true +``` """ function elementary_differential(t::RootedTree) Base.depwarn( @@ -1594,6 +1950,21 @@ end Returns the elementary differential as a `LaTeXString` from the package [LaTeXStrings.jl](https://github.com/JuliaStrings/LaTeXStrings.jl). +# Arguments + +- `t::RootedTree`: Rooted tree to represent. + +# Returns + +- `LaTeXString`: LaTeX representation of the elementary differential. + +# Examples + +```jldoctest +julia> elementary_differential_latexstring(rootedtree([1])) isa AbstractString +true +``` + """ function elementary_differential_latexstring(t::RootedTree) return latexstring(rec_elementary_differential(t)) @@ -1629,6 +2000,21 @@ end Returns the elementary_weight as a `LaTeXString` from the package [LaTeXStrings.jl](https://github.com/JuliaStrings/LaTeXStrings.jl). + +# Arguments + +- `t::RootedTree`: Rooted tree to represent. + +# Returns + +- `LaTeXString`: LaTeX representation of the elementary weight. + +# Examples + +```jldoctest +julia> elementary_weight_latexstring(rootedtree([1])) isa AbstractString +true +``` """ function elementary_weight_latexstring(t::RootedTree) # Creating the alphabet for the indices diff --git a/src/colored_trees.jl b/src/colored_trees.jl index ccf070ef..6499fcc0 100644 --- a/src/colored_trees.jl +++ b/src/colored_trees.jl @@ -58,7 +58,30 @@ end """ BicoloredRootedTree{T<:Integer} -Representation of bicolored rooted trees. +Alias for [`ColoredRootedTree`](@ref) whose `color_sequence` contains `Bool` +values. `false` and `true` are conventionally used for the two colors. + +# Fields + +- `level_sequence`: Integer level of every node in depth-first order. +- `color_sequence`: Boolean color for each node, with the same axes as + `level_sequence`. +- `iscanonical`: Whether both sequences use the package's canonical ordering. + +# Arguments + +- `level_sequence`: Integer vector satisfying the rooted-tree level-sequence + rules. +- `color_sequence`: Boolean vector with the same axes as `level_sequence`. + +# Examples + +```jldoctest +julia> t = rootedtree([1, 2], Bool[false, true]); + +julia> t isa BicoloredRootedTree +true +``` See also [`ColoredRootedTree`](@ref), [`RootedTree`](@ref), [`rootedtree`](@ref). """ @@ -86,6 +109,17 @@ each node of the tree and a vector of associated colors (e.g., `Bool`s or - `color_sequence`: A vector of node colors with axes equal to `axes(level_sequence)`. The input vectors are not mutated. +# Returns + +- `ColoredRootedTree`: Canonical colored tree backed by copies of both input + vectors. + +# Throws + +- `DimensionMismatch`: If the input vectors have different axes. +- `ArgumentError`: If `level_sequence` is not a valid rooted-tree level + sequence. + # Examples ```jldoctest @@ -127,6 +161,10 @@ and a `color_sequence` which may be modified in this process. See also - `color_sequence`: A mutable color vector with the same axes as `level_sequence`. Its contents may be reordered in place in tandem. +# Returns + +- `ColoredRootedTree`: Canonical colored tree backed by the input vectors. + # References - Terry Beyer and Sandra Mitchell Hedetniemi. @@ -162,6 +200,21 @@ end root_color(t::ColoredRootedTree) Return the color of the root of `t`. + +# Arguments + +- `t::ColoredRootedTree`: Colored rooted tree to inspect. + +# Returns + +- `eltype(t.color_sequence)`: Color stored at the root node. + +# Examples + +```jldoctest +julia> root_color(rootedtree([1, 2], Bool[false, true])) +false +``` """ root_color(t::ColoredRootedTree) = first(t.color_sequence) @@ -482,6 +535,18 @@ trees shall be stored or modified during the iteration, a `copy` has to be made. This iterator implements `iterate`, `eltype`, and `length`. `length` counts only canonical bicolored trees. Iteration reuses one mutable tree buffer; copy a yielded tree before retaining it. + +# Examples + +```jldoctest +julia> trees = collect(BicoloredRootedTreeIterator(1)); + +julia> length(trees) +2 + +julia> first(trees).color_sequence isa AbstractVector{Bool} +true +``` """ struct BicoloredRootedTreeIterator{T <: Integer} number_of_colors::T diff --git a/src/time_integration_methods.jl b/src/time_integration_methods.jl index 46feb1fe..70f5775b 100644 --- a/src/time_integration_methods.jl +++ b/src/time_integration_methods.jl @@ -79,6 +79,17 @@ end Compute the elementary weight Φ(`t`) of the [`RungeKuttaMethod`](@ref) `rk` with Butcher coefficients `A, b, c` for a rooted tree `t`. +# Arguments + +- `t::RootedTree`: Rooted tree whose weight is evaluated. +- `rk::RungeKuttaMethod`: Runge-Kutta coefficients used for the evaluation. +- `A`, `b`, `c`: Alternative coefficient arguments used by the compatibility + method; they are passed to [`RungeKuttaMethod`](@ref). + +# Returns + +- `Number`: Elementary weight of `t` for the supplied method. + Reference: Section 312 of - Butcher, John Charles. Numerical methods for ordinary differential equations. @@ -102,6 +113,15 @@ end Compute the derivative weight (ΦᵢD)(`t`) of the [`RungeKuttaMethod`](@ref) `rk` with Butcher coefficients `A, b, c` for the rooted tree `t`. +# Arguments + +- `t::RootedTree`: Rooted tree whose derivative weight is evaluated. +- `rk::RungeKuttaMethod`: Runge-Kutta coefficients used for the evaluation. + +# Returns + +- `AbstractVector`: Derivative weight for each stage of `rk`. + Reference: Section 312 of - Butcher, John Charles. Numerical methods for ordinary differential equations. @@ -142,6 +162,15 @@ with [`elementary_weight`](@ref) `Φ(t)`, [`density`](@ref) `γ(t)`, and [`symmetry`](@ref) `σ(t)` of the [`RungeKuttaMethod`](@ref) `rk` with Butcher coefficients `A, b, c` for the rooted tree `t`. +# Arguments + +- `t::RootedTree`: Rooted tree whose order condition is evaluated. +- `rk::RungeKuttaMethod`: Runge-Kutta coefficients used for the evaluation. + +# Returns + +- `Number`: Residual of the order condition for `t`. + Reference: Section 315 of - Butcher, John Charles. Numerical methods for ordinary differential equations. @@ -209,6 +238,16 @@ methods, which are applied to partitioned problems of the form (u^2)'(t) = f^2(t, u^1, u^2). ``` +# Examples + +```jldoctest +julia> ark = AdditiveRungeKuttaMethod( + [[0.0;;], [1.0;;]], [[1.0], [1.0]]); + +julia> length(ark.rks) +2 +``` + # References - A. L. Araujo, A. Murua, and J. M. Sanz-Serna. @@ -259,6 +298,16 @@ color_to_index(color::Bool) = 1 + color Compute the elementary weight Φ(`t`) of the [`AdditiveRungeKuttaMethod`](@ref) `ark` for a colored rooted tree `t`. +# Arguments + +- `t::ColoredRootedTree`: Colored rooted tree whose weight is evaluated. +- `ark::AdditiveRungeKuttaMethod`: Additive Runge-Kutta coefficients used for + the evaluation. + +# Returns + +- `Number`: Elementary weight of `t` for `ark`. + # References - A. L. Araujo, A. Murua, and J. M. Sanz-Serna. @@ -286,6 +335,17 @@ end Compute the derivative weight (ΦᵢD)(`t`) of the [`AdditiveRungeKuttaMethod`](@ref) `ark` for the colored rooted tree `t`. +# Arguments + +- `t::ColoredRootedTree`: Colored rooted tree whose derivative weight is + evaluated. +- `ark::AdditiveRungeKuttaMethod`: Additive Runge-Kutta coefficients used for + the evaluation. + +# Returns + +- `AbstractVector`: Derivative weight for each stage of `ark`. + # References - A. L. Araujo, A. Murua, and J. M. Sanz-Serna. @@ -321,6 +381,16 @@ with [`elementary_weight`](@ref) `Φ(t)`, [`density`](@ref) `γ(t)`, and [`symmetry`](@ref) `σ(t)` of the [`AdditiveRungeKuttaMethod`](@ref) `ark` for the colored rooted tree `t`. +# Arguments + +- `t::ColoredRootedTree`: Colored rooted tree whose order condition is evaluated. +- `ark::AdditiveRungeKuttaMethod`: Additive Runge-Kutta coefficients used for + the evaluation. + +# Returns + +- `Number`: Residual of the order condition for `t`. + # References - A. L. Araujo, A. Murua, and J. M. Sanz-Serna. @@ -362,6 +432,16 @@ autonomous problems is applied. - `c=vec(sum(A, dims=2))`: Stage abscissae, one per stage. The default uses row sums of `A`. +# Examples + +```jldoctest +julia> ros = RosenbrockMethod([1.0;;], [0.0;;], [1.0]); + +julia> ros.c +1-element Vector{Float64}: + 0.0 +``` + # Reference - Ernst Hairer, Gerhard Wanner. @@ -422,6 +502,15 @@ end Compute the elementary weight Φ(`t`) of the [`RosenbrockMethod`](@ref) `ros` for a rooted tree `t`. + +# Arguments + +- `t::RootedTree`: Rooted tree whose weight is evaluated. +- `ros::RosenbrockMethod`: Rosenbrock coefficients used for the evaluation. + +# Returns + +- `Number`: Elementary weight of `t` for `ros`. """ function elementary_weight(t::RootedTree, ros::RosenbrockMethod) return dot(ros.b, derivative_weight(t, ros)) @@ -432,6 +521,15 @@ end Compute the derivative weight (ΦᵢD)(`t`) of the [`RosenbrockMethod`](@ref) `ros` for the rooted tree `t`. + +# Arguments + +- `t::RootedTree`: Rooted tree whose derivative weight is evaluated. +- `ros::RosenbrockMethod`: Rosenbrock coefficients used for the evaluation. + +# Returns + +- `AbstractVector`: Derivative weight for each stage of `ros`. """ function derivative_weight(t::RootedTree, ros::RosenbrockMethod) γ = ros.γ @@ -470,6 +568,15 @@ with [`elementary_weight`](@ref) `Φ(t)`, [`density`](@ref) `γ(t)`, and [`symmetry`](@ref) `σ(t)` of the [`RosenbrockMethod`](@ref) `ros` for the rooted tree `t`. +# Arguments + +- `t::RootedTree`: Rooted tree whose order condition is evaluated. +- `ros::RosenbrockMethod`: Rosenbrock coefficients used for the evaluation. + +# Returns + +- `Number`: Residual of the order condition for `t`. + # Reference - Ernst Hairer, Gerhard Wanner. diff --git a/test/interfaces.jl b/test/interfaces.jl index 236d3fdb..fbb5c11b 100644 --- a/test/interfaces.jl +++ b/test/interfaces.jl @@ -27,4 +27,48 @@ end expected_length = length(iter) @test generic_iterator_count(iter) == expected_length end + + # SubtreeIterator deliberately exposes only the two-argument iterate + # protocol, so consume it without assuming length or eltype. + @test generic_iterator_count(SubtreeIterator(tree)) == 2 +end + +function generic_tree_summary(t) + edge_set = fill(false, order(t) - 1) + return ( + order(t), + generic_iterator_count(SubtreeIterator(t)), + generic_iterator_count(PartitionIterator(t)), + order(partition_skeleton(t, edge_set)), + symmetry(t), + density(t), + α(t), + β(t), + ) +end + +@testset "generic tree interface" begin + tree = rootedtree([1, 2, 2]) + @test generic_tree_summary(tree) == (3, 2, 4, 3, 2, 3, 1, 3) + + colored_tree = rootedtree([1, 2, 2], Bool[false, true, true]) + @test generic_tree_summary(colored_tree) == (3, 2, 4, 3, 2, 3, 1, 3) + @test root_color(colored_tree) === false +end + +@testset "exported names are documented" begin + exported = names(RootedTrees; all = false, imported = false) + undocumented = filter(exported) do name + if isdefined(Base.Docs, :hasdoc) + !Base.Docs.hasdoc(RootedTrees, name) + else + ref = Expr(:., :RootedTrees, QuoteNode(name)) + call = Expr( + :macrocall, GlobalRef(Base.Docs, Symbol("@doc")), LineNumberNode(0), ref + ) + doc = Core.eval(@__MODULE__, call) + occursin("No documentation found", sprint(show, MIME"text/plain"(), doc)) + end + end + @test isempty(undocumented) end