Loss Landscape

NonArchimedeanMachineLearning.ConvexHullTreeType
ConvexHullTree{S,T,N}

A tree structure representing the convex hull of a set of polydiscs.

The convex hull consists of the input polydiscs and all their joins, with edges determined by the containment relation.

Fields

  • nodes::Vector{ValuationPolydisc{S,T,N}}: All nodes in the tree (input discs + joins)
  • children::Dict{Int,Vector{Int}}: Maps node index to indices of immediate children
  • parents::Dict{Int,Vector{Int}}: Maps node index to indices of immediate parents
  • leaf_indices::Vector{Int}: Indices of the original input discs
source
NonArchimedeanMachineLearning.build_tree_structureMethod
build_tree_structure(nodes::Vector{ValuationPolydisc{S,T,N}}, num_initial::Int) where {S,T,N}

Build parent-child relationships for the convex hull tree.

Arguments

  • nodes::Vector{ValuationPolydisc{S,T,N}}: All nodes (initial discs first, then joins)
  • num_initial::Int: Number of initial discs (these are the leaves)

Returns

Tuple{Dict{Int,Vector{Int}}, Dict{Int,Vector{Int}}}: (children dict, parents dict)

source
NonArchimedeanMachineLearning.compute_all_joinsMethod
compute_all_joins(initial_discs::Vector{ValuationPolydisc{S,T,N}}) where {S,T,N}

Compute all polydiscs in the convex hull by iteratively computing joins.

Starting from the initial list of polydiscs, repeatedly computes pairwise joins until no new polydiscs are generated.

Arguments

  • initial_discs::Vector{ValuationPolydisc{S,T,N}}: Initial list of polydiscs

Returns

Vector{ValuationPolydisc{S,T,N}}: All polydiscs in the convex hull

source
NonArchimedeanMachineLearning.compute_subtree_widthFunction
compute_subtree_width(tree::ConvexHullTree, node_idx::Int, leaf_width::Float64=1.0)

Recursively compute the width needed for a subtree rooted at the given node.

Arguments

  • tree::ConvexHullTree: The tree structure
  • node_idx::Int: Index of the root of the subtree
  • leaf_width::Float64: Width allocated to each leaf node (default: 1.0)

Returns

Float64: Total width needed for the subtree

source
NonArchimedeanMachineLearning.compute_tree_layoutMethod
compute_tree_layout(tree::ConvexHullTree{S,T,N}) where {S,T,N}

Compute (x, y) positions for all nodes in the tree for visualization.

Uses a radius-based layout where:

  • Y-coordinate is determined by valuation radius (smaller actual radius = higher position)
  • Nodes with the same valuation radius are at the same vertical level
  • X-coordinates spread nodes to avoid overlap while respecting tree structure

Arguments

  • tree::ConvexHullTree{S,T,N}: The convex hull tree

Returns

Tuple{Dict{Int, Tuple{Float64, Float64}}, Dict{Int,Vector{Int}}, Dict{Int,Int}, Int}:

  • positions: Maps node index to (x, y) position
  • spanning_children: Children in the spanning tree
  • spanning_parent: Parent in the spanning tree
  • root_idx: Index of the root
source
NonArchimedeanMachineLearning.convex_hullMethod
convex_hull(discs::Vector{ValuationPolydisc{S,T,N}}) where {S,T,N}

Compute the convex hull tree of a list of polydiscs.

The convex hull is a tree whose nodes are the input polydiscs and all their joins, with edges determined by the containment relation.

Arguments

  • discs::Vector{ValuationPolydisc{S,T,N}}: List of polydiscs

Returns

ConvexHullTree{S,T,N}: The convex hull tree structure

Example

K = PadicField(2, 20)
d1 = ValuationPolydisc([K(0)], [3])
d2 = ValuationPolydisc([K(4)], [3])
d3 = ValuationPolydisc([K(8)], [3])
tree = convex_hull([d1, d2, d3])
source
NonArchimedeanMachineLearning.export_landscape_csvMethod
export_landscape_csv(tree::ConvexHullTree{S,T,N}, landscape::Dict, filename::String) where {S,T,N}

Export loss landscape data to CSV format for external plotting.

The CSV format is:

parent_idx,child_idx,geodesic_param,loss_value

Arguments

  • tree::ConvexHullTree{S,T,N}: The convex hull tree structure
  • landscape::Dict: Loss landscape data from samplelosslandscape
  • filename::String: Output CSV file path

Example

tree = convex_hull([d1, d2, d3])
landscape = sample_loss_landscape(tree, loss_func, 10)
export_landscape_csv(tree, landscape, "landscape_data.csv")
source
NonArchimedeanMachineLearning.extract_spanning_treeMethod
extract_spanning_tree(tree::ConvexHullTree{S,T,N}) where {S,T,N}

Extract a spanning tree from the convex hull DAG.

The convex hull can form a DAG (directed acyclic graph) where nodes have multiple parents. This function extracts a proper tree by:

  1. Creating a virtual root if there are multiple roots
  2. Keeping only one parent per node (the one with smallest radius sum, breaking ties by index)

Arguments

  • tree::ConvexHullTree{S,T,N}: The convex hull tree (potentially a DAG)

Returns

Tuple{Dict{Int,Vector{Int}}, Dict{Int,Int}, Int}: (children, parent, root_idx)

  • children: Maps node index to list of children in the spanning tree
  • parent: Maps node index to its single parent (-1 for root)
  • root_idx: Index of the root node (or -1 for virtual root)
source
NonArchimedeanMachineLearning.find_polydisc_indexMethod
find_polydisc_index(discs::Vector{ValuationPolydisc{S,T,N}}, p::ValuationPolydisc{S,T,N}) where {S,T,N}

Find the index of a polydisc in a vector using Berkovich equality, or return 0 if not found.

Uses the == operator which checks Berkovich equality: two polydiscs are equal if they have the same radius and their centers differ by elements with valuation >= radius.

Arguments

  • discs::Vector{ValuationPolydisc{S,T,N}}: Vector of polydiscs to search
  • p::ValuationPolydisc{S,T,N}: Polydisc to find

Returns

Int: Index of the first equal polydisc in the vector, or 0 if not found

source
NonArchimedeanMachineLearning.geodesic_interpolationMethod
geodesic_interpolation(d1::ValuationPolydisc{S,T,N}, d2::ValuationPolydisc{S,T,N}, x::Real) where {S,T,N}

Compute the polydisc at parameter x along the geodesic from d1 to d2.

For two discs with the same center, the geodesic is linear interpolation of actual radii. If d1 = D(a, r) and d2 = D(a, s) with valuation radii r ≥ s, then at parameter x ∈ [0,1] the actual radius is $(1-x)p^{-r} + xp^{-s}$.

ValuationPolydisc.radius is always returned in valuation coordinates. The interpolation is computed in actual-radius coordinates and converted back to Float64 valuation radii before constructing the result.

Arguments

  • d1::ValuationPolydisc{S,T,N}: First polydisc (must be contained in d2)
  • d2::ValuationPolydisc{S,T,N}: Second polydisc (must contain d1)
  • x::Real: Interpolation parameter in [0, 1]

Returns

ValuationPolydisc{S,Float64,N}: Interpolated polydisc with Float64 valuation radii

Example

K = PadicField(2, 20)
d1 = ValuationPolydisc([K(0)], [5])  # Small disc, valuation 5
d2 = ValuationPolydisc([K(0)], [2])  # Large disc, valuation 2
d_half = geodesic_interpolation(d1, d2, 0.5)  # Midpoint
source
NonArchimedeanMachineLearning.is_immediate_parentMethod
is_immediate_parent(parent_idx::Int, child_idx::Int, nodes::Vector{ValuationPolydisc{S,T,N}}) where {S,T,N}

Check if parent is an immediate parent of child (no intermediate node).

Returns true if:

  1. nodes[childidx] ⊆ nodes[parentidx] (containment)
  2. child has strictly smaller radius than parent (higher valuation = smaller disc)
  3. There is no intermediate node k with child ⊆ k ⊆ parent

Arguments

  • parent_idx::Int: Index of potential parent
  • child_idx::Int: Index of potential child
  • nodes::Vector{ValuationPolydisc{S,T,N}}: All nodes in the tree

Returns

Bool: true if parent is an immediate parent of child

source
NonArchimedeanMachineLearning.loss_to_colorFunction
loss_to_color(loss::Float64, min_loss::Float64, max_loss::Float64, colormap::Symbol=:viridis)

Convert a loss value to an RGB color using a colormap.

Arguments

  • loss::Float64: The loss value
  • min_loss::Float64: Minimum loss for normalization
  • max_loss::Float64: Maximum loss for normalization
  • colormap::Symbol: Color scheme (default: :viridis)

Returns

RGB color from the colormap

source
NonArchimedeanMachineLearning.plot_loss_landscapeMethod
plot_loss_landscape(tree::ConvexHullTree{S,T,N}, landscape::Dict; kwargs...) where {S,T,N}

Plot the loss landscape using Plots.jl (if available).

Creates a visualization showing:

  • Nodes as points
  • Edges colored by loss value
  • Loss values along geodesics

Arguments

  • tree::ConvexHullTree{S,T,N}: The convex hull tree structure
  • landscape::Dict: Loss landscape data from samplelosslandscape

Keyword Arguments

  • title::String: Plot title (default: "Loss Landscape")
  • colormap::Symbol: Color scheme (default: :viridis)
  • show_node_labels::Bool: Whether to show node indices (default: true)
  • line_width::Real: Width of edge lines (default: 2)

Returns

Plot object from Plots.jl

Example

using Plots
tree = convex_hull([d1, d2, d3])
landscape = sample_loss_landscape(tree, loss_func, 10)
plt = plot_loss_landscape(tree, landscape, title="My Loss Landscape")
savefig(plt, "landscape.png")

Note

Requires Plots.jl to be installed and loaded:

using Plots
source
NonArchimedeanMachineLearning.plot_tree_simpleMethod
plot_tree_simple(tree::ConvexHullTree{S,T,N};
                 title::String="Convex Hull Tree",
                 show_node_labels::Bool=true,
                 line_width::Real=2,
                 node_size::Real=10,
                 figsize::Tuple{Int,Int}=(800, 600)) where {S,T,N}

Plot the convex hull tree structure without loss coloring.

Useful for visualizing just the tree structure before sampling loss.

Arguments

  • tree::ConvexHullTree{S,T,N}: The convex hull tree structure

Keyword Arguments

  • title::String: Plot title (default: "Convex Hull Tree")
  • show_node_labels::Bool: Whether to show node indices (default: true)
  • line_width::Real: Width of edge lines (default: 2)
  • node_size::Real: Size of node markers (default: 10)
  • figsize::Tuple{Int,Int}: Figure size in pixels (default: (800, 600))

Returns

Plot object from Plots.jl

source
NonArchimedeanMachineLearning.plot_tree_with_lossMethod
plot_tree_with_loss(tree::ConvexHullTree{S,T,N}, landscape::Dict;
                    title::String="Loss Landscape on Tree",
                    colormap::Symbol=:viridis,
                    show_node_labels::Bool=true,
                    line_width::Real=4,
                    node_size::Real=8,
                    figsize::Tuple{Int,Int}=(800, 600)) where {S,T,N}

Plot the convex hull tree with edges colored by loss values.

Creates a proper tree visualization where:

  • Nodes are positioned in a hierarchical tree layout (root at top)
  • Each edge segment is colored according to the loss value at that point
  • A colorbar shows the loss scale

Arguments

  • tree::ConvexHullTree{S,T,N}: The convex hull tree structure
  • landscape::Dict: Loss landscape data from sample_loss_landscape

Keyword Arguments

  • title::String: Plot title (default: "Loss Landscape on Tree")
  • colormap::Symbol: Color scheme (default: :viridis)
  • show_node_labels::Bool: Whether to show node indices (default: true)
  • line_width::Real: Width of edge lines (default: 4)
  • node_size::Real: Size of node markers (default: 8)
  • figsize::Tuple{Int,Int}: Figure size in pixels (default: (800, 600))

Returns

Plot object from Plots.jl

Example

using Plots
tree = NonArchimedeanMachineLearning.convex_hull([d1, d2, d3])
landscape = sample_loss_landscape(tree, loss_func, 20)
plt = plot_tree_with_loss(tree, landscape)
savefig(plt, "tree_landscape.png")
source
NonArchimedeanMachineLearning.print_landscape_summaryMethod
print_landscape_summary(tree::ConvexHullTree{S,T,N}, landscape::Dict) where {S,T,N}

Print a text-based summary of the loss landscape.

Arguments

  • tree::ConvexHullTree{S,T,N}: The convex hull tree structure
  • landscape::Dict: Loss landscape data from samplelosslandscape

Example

tree = convex_hull([d1, d2, d3])
landscape = sample_loss_landscape(tree, loss_func, 10)
print_landscape_summary(tree, landscape)
source
NonArchimedeanMachineLearning.radius_strictly_smallerMethod
radius_strictly_smaller(r1::NTuple{N,T}, r2::NTuple{N,T}) where {N,T}

Check if r1 is strictly smaller than r2 in the valuation sense.

A radius is "smaller" if it has higher valuation values (representing a smaller disc). Returns true if all components of r1 >= r2 and at least one is strictly greater.

Arguments

  • r1::NTuple{N,T}: First radius tuple (valuation)
  • r2::NTuple{N,T}: Second radius tuple (valuation)

Returns

Bool: true if r1 represents a strictly smaller disc than r2

source
NonArchimedeanMachineLearning.radius_to_valuationMethod
radius_to_valuation(r::Real, p::Integer)

Convert an actual radius to a valuation.

For a p-adic disc with actual radius r, the valuation is -log_p(r).

Arguments

  • r::Real: The actual radius
  • p::Integer: The prime

Returns

Float64: The valuation (possibly non-integer)

source
NonArchimedeanMachineLearning.sample_loss_landscapeMethod
sample_loss_landscape(tree::ConvexHullTree{S,T,N}, f::Function, num_samples::Int=10) where {S,T,N}

Sample function values along geodesics in the convex hull tree.

For each parent-child edge in the tree, this function samples the function f at num_samples points along the geodesic connecting them. The geodesic parameter ranges from 0 (at the child) to 1 (at the parent).

Arguments

  • tree::ConvexHullTree{S,T,N}: The convex hull tree structure
  • f::Function: Function to evaluate, takes a ValuationPolydisc and returns a scalar
  • num_samples::Int: Number of sample points along each geodesic (default: 10)

Returns

Dict{Tuple{Int,Int}, Vector{Tuple{Float64,Float64}}}: Dictionary mapping (parent_idx, child_idx) pairs to vectors of (x, y) pairs, where:

  • x ∈ [0,1] is the geodesic parameter (0 = child, 1 = parent)
  • y is the function value at that point

Example

K = PadicField(2, 20)
d1 = ValuationPolydisc([K(0)], [3])
d2 = ValuationPolydisc([K(4)], [3])
tree = convex_hull([d1, d2])

# Define a simple function
f(disc) = sum(disc.radius)

# Sample the landscape
landscape = sample_loss_landscape(tree, f, 5)
source
NonArchimedeanMachineLearning.valuation_to_radiusMethod
valuation_to_radius(val::Real, p::Integer)

Convert a valuation to an actual radius.

For a p-adic disc with valuation radius val, the actual radius is p^(-val).

Arguments

  • val::Real: The valuation (can be integer or float)
  • p::Integer: The prime

Returns

Float64: The actual radius as a real number

source