"""Python 3.14, NumPy 2.3 and Matplotlib 3.10. Output PNG basename to caller cwd. Uses caller MPLCONFIGDIR unchanged. Exact Jaccard/complete-linkage calculation. Ties are resolved by original row order; other tied trees are also valid. """ from fractions import Fraction import numpy as np import matplotlib.pyplot as plt NAMES = ['Taeko','Luitgard','Alet','Tom','LinYee','Pio','LingChen','HuiChin','Martin','Nicolas','Mohammad','Meg'] DATA = np.array([[1,1,0,1,1,1,0,0,0],[0,1,0,0,1,1,1,1,0], [1,1,1,0,0,1,0,1,0],[1,1,1,1,1,0,1,1,0],[1,1,0,0,0,0,1,1,0], [1,1,0,0,0,0,1,0,0],[1,0,0,0,0,1,1,0,0],[1,1,0,0,0,1,1,1,0], [1,1,1,1,0,0,1,1,0],[1,1,1,0,0,0,1,1,1],[1,1,0,0,0,0,0,1,0], [1,1,0,0,0,1,1,0,0]], dtype=bool) def exact_distances(): return [[Fraction(int(np.count_nonzero(x != y)), int(np.count_nonzero(x | y))) for y in DATA] for x in DATA] def merges(): distances = exact_distances() clusters = {i:(i,) for i in range(len(NAMES))} history = [] for new_id in range(len(NAMES), 2*len(NAMES)-1): keys = sorted(clusters) candidates = [(max(distances[i][j] for i in clusters[a] for j in clusters[b]), clusters[a], clusters[b], a, b) for ia,a in enumerate(keys) for b in keys[ia+1:]] height, _, _, a, b = min(candidates) history.append((a,b,new_id,height,clusters[a],clusters[b])) clusters[new_id] = tuple(sorted(clusters.pop(a)+clusters.pop(b))) return history def main(): n = len(NAMES) history = merges() children = {new:(a,b) for a,b,new,*_ in history} def leaves(node): return [node] if node