Asi ultrametric-distance
Non-Archimedean distance metrics for hierarchical clustering and p-adic analysis
install
source · Clone the upstream repo
git clone https://github.com/plurigrid/asi
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/ultrametric-distance" ~/.claude/skills/plurigrid-asi-ultrametric-distance-d50ea0 && rm -rf "$T"
manifest:
skills/ultrametric-distance/SKILL.mdsource content
Ultrametric Distance Skill
Status: ✅ Production Ready Trit: -1 (MINUS - validator/constrainer) Principle: d(x,z) ≤ max(d(x,y), d(y,z)) — Strong Triangle Inequality
Overview
Ultrametric Distance provides non-Archimedean distance functions where the strong triangle inequality holds. Essential for:
- Hierarchical clustering: Natural tree structures emerge
- p-adic analysis: Number-theoretic computations
- Phylogenetic trees: Evolution distance metrics
- Version control: Commit ancestry distances
Core Property
Ultrametric Inequality: d(x, z) ≤ max(d(x, y), d(y, z)) Unlike Euclidean: d(x,z) ≤ d(x,y) + d(y,z) Ultrametric is STRONGER: max instead of sum
Key Insight
In ultrametric space, ALL triangles are isoceles with the unequal side being the shortest.
Python Implementation
import math from typing import List, Tuple, Callable def ultrametric_distance(x: List[float], y: List[float]) -> float: """Compute ultrametric (sup-norm) distance.""" return max(abs(a - b) for a, b in zip(x, y)) def p_adic_valuation(n: int, p: int) -> int: """Compute p-adic valuation v_p(n) = max k such that p^k | n.""" if n == 0: return float('inf') v = 0 while n % p == 0: n //= p v += 1 return v def p_adic_distance(x: int, y: int, p: int) -> float: """ Compute p-adic distance: d_p(x,y) = p^(-v_p(x-y)) Properties: - d_p(x,x) = 0 - d_p(x,y) = d_p(y,x) - d_p(x,z) ≤ max(d_p(x,y), d_p(y,z)) # Ultrametric! """ if x == y: return 0.0 v = p_adic_valuation(abs(x - y), p) return p ** (-v) def verify_ultrametric(d: Callable, points: List) -> dict: """Verify that distance function satisfies ultrametric inequality.""" violations = [] for i, x in enumerate(points): for j, y in enumerate(points): for k, z in enumerate(points): dxz = d(x, z) dxy = d(x, y) dyz = d(y, z) if dxz > max(dxy, dyz) + 1e-10: violations.append({ 'x': x, 'y': y, 'z': z, 'd(x,z)': dxz, 'max(d(x,y),d(y,z))': max(dxy, dyz) }) return { 'is_ultrametric': len(violations) == 0, 'violations': violations[:5], 'total_violations': len(violations) }
Hierarchical Clustering (UPGMA)
def ultrametric_upgma(distance_matrix: List[List[float]]) -> dict: """ Build ultrametric tree via UPGMA clustering. Returns dendrogram as nested dict. """ n = len(distance_matrix) clusters = [{i} for i in range(n)] heights = [0.0] * n tree = {i: {'leaf': i, 'height': 0} for i in range(n)} while len(clusters) > 1: # Find closest pair min_dist = float('inf') merge_i, merge_j = 0, 1 for i in range(len(clusters)): for j in range(i + 1, len(clusters)): # Average linkage distance d = sum(distance_matrix[a][b] for a in clusters[i] for b in clusters[j]) / (len(clusters[i]) * len(clusters[j])) if d < min_dist: min_dist, merge_i, merge_j = d, i, j # Merge clusters new_cluster = clusters[merge_i] | clusters[merge_j] new_height = min_dist / 2 new_node = { 'left': tree[merge_i], 'right': tree[merge_j], 'height': new_height, 'members': list(new_cluster) } # Update tree[merge_i] = new_node del tree[merge_j] clusters[merge_i] = new_cluster del clusters[merge_j] return tree[0]
Git Commit Distance
def commit_ultrametric_distance(repo, commit_a: str, commit_b: str) -> int: """ Ultrametric distance between commits = depth to common ancestor. d(A, B) = depth(merge_base(A, B)) Satisfies ultrametric: branching creates natural hierarchy. """ import subprocess # Find merge base merge_base = subprocess.check_output( ['git', 'merge-base', commit_a, commit_b], cwd=repo ).decode().strip() # Count commits from merge base to root depth = int(subprocess.check_output( ['git', 'rev-list', '--count', merge_base], cwd=repo ).decode().strip()) return depth
Julia Implementation
module UltrametricDistance """ p_adic_distance(x::Int, y::Int, p::Int) -> Float64 Compute p-adic distance between integers. """ function p_adic_distance(x::Int, y::Int, p::Int) x == y && return 0.0 diff = abs(x - y) v = 0 while diff % p == 0 diff ÷= p v += 1 end return Float64(p)^(-v) end """ ultrametric_ball(center, radius, points, d) Return all points within ultrametric ball. Note: In ultrametric space, every point in the ball is a center! """ function ultrametric_ball(center, radius, points, d) filter(p -> d(center, p) ≤ radius, points) end """ is_ultrametric(d, points) -> Bool Verify ultrametric inequality for all triples. """ function is_ultrametric(d, points) for x in points, y in points, z in points d(x, z) > max(d(x, y), d(y, z)) && return false end return true end end # module
Integration with GF(3)
Ultrametric distances map naturally to trits:
def distance_to_trit(d: float, thresholds: Tuple[float, float] = (0.33, 0.66)) -> int: """ Map ultrametric distance to trit. Close (d < 0.33) → +1 (PLUS, same cluster) Medium (0.33-0.66) → 0 (ERGODIC, sibling clusters) Far (d > 0.66) → -1 (MINUS, distant branches) """ if d < thresholds[0]: return 1 elif d < thresholds[1]: return 0 else: return -1
Commands
# Verify p-adic distances python -c "from ultrametric import p_adic_distance; print(p_adic_distance(12, 20, 2))" # Build UPGMA tree python -m ultrametric.upgma --input distances.csv --output tree.json # Git commit distance git-ultrametric HEAD~5 main
Properties
| Property | Euclidean | Ultrametric |
|---|---|---|
| Triangle | d(x,z) ≤ d(x,y) + d(y,z) | d(x,z) ≤ max(d(x,y), d(y,z)) |
| Ball centers | Unique | Every interior point |
| Triangles | Arbitrary | Isoceles (short base) |
| Topology | Connected | Totally disconnected |
Skill Name: ultrametric-distance Type: Distance Metric / Clustering Trit: -1 (MINUS) Use Case: Hierarchical validation, tree construction, version ancestry