PowerMachine commited on
Commit
e95a39d
·
verified ·
1 Parent(s): 7ff1796

HAKO upload: hako/core/plateau.py

Browse files
Files changed (1) hide show
  1. hako/core/plateau.py +112 -0
hako/core/plateau.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """2D plateau: kernel density of prototypes under the mapping Pi: R^N -> R^2.
2
+
3
+ Theorem T-PLATEAU (existence of a compact non-empty plateau; Morse analytics).
4
+ Let Pi: R^N -> R^2 be continuous (here: PCA whitening followed by a trainable
5
+ linear refinement; PCA projection is linear, hence analytic) and let
6
+ rho(x, y) = sum_{k=1..K} exp( -|| Pi(mu_k) - (x, y) ||^2 / (2 sigma^2) ).
7
+ (1) rho is real-analytic on R^2 (finite sum of Gaussian bumps, each analytic).
8
+ (2) For generic sigma > 0, rho is a Morse function: its critical points are
9
+ non-degenerate (degeneracy requires an algebraic coincidence of the K
10
+ bump centers, a measure-zero set in sigma; Sard's theorem argument).
11
+ (3) rho(x,y) -> 0 as ||(x,y)|| -> 0-distance-to-infinity, and
12
+ max rho >= rho(Pi(mu_1)) >= 1 (the bump centered at Pi(mu_1) contributes
13
+ >= 1 there). Hence for any delta with 0 < delta < max rho the superlevel
14
+ set P_delta = {(x,y) : rho >= delta} is non-empty (contains a global
15
+ maximizer, which exists because rho is continuous and vanishes at
16
+ infinity => attains max on a compact set) and compact (closed superlevel
17
+ set of a coercive function). QED.
18
+
19
+ The orchestrator monitors: plateau mass M = rho(argmax), gradient norm at the
20
+ current projected point, and saturation = 1 - entropy of the normalized
21
+ bump contributions (low entropy = one dominant prototype = saturation signal
22
+ feeding the dimensional-growth trigger, per PHASE1 inertia rule).
23
+ """
24
+ from __future__ import annotations
25
+
26
+ from typing import Tuple
27
+
28
+ import numpy as np
29
+ import torch
30
+
31
+
32
+ class Plateau2D:
33
+ def __init__(self, sigma: float = 0.55, n_components: int = 2) -> None:
34
+ self.sigma = float(sigma)
35
+ self.mean = None
36
+ self.comp = None # (N, 2) PCA components
37
+ self.refined = np.zeros((2, 2), dtype=np.float32) # trainable refine I
38
+
39
+ @staticmethod
40
+ def _np(x):
41
+ """Torch/numpy agnostic conversion (detached; plateau coords are
42
+ monitoring/control signals, not gradient carriers)."""
43
+ if isinstance(x, torch.Tensor):
44
+ return x.detach().cpu().numpy().astype(np.float32)
45
+ return np.asarray(x, dtype=np.float32)
46
+
47
+ # ------------------------------------------------------------- fitting
48
+ def fit_pca(self, Z: np.ndarray) -> None:
49
+ X = np.asarray(Z, dtype=np.float32)
50
+ self.mean = X.mean(axis=0)
51
+ Xc = X - self.mean
52
+ # economical SVD on the covariance (N x 2 subspace)
53
+ C = Xc.T @ Xc / max(1, len(X))
54
+ w, V = np.linalg.eigh(C)
55
+ order = np.argsort(w)[::-1][:2]
56
+ self.comp = V[:, order].astype(np.float32) # (N, 2)
57
+
58
+ def project(self, Z: np.ndarray) -> np.ndarray:
59
+ """Pi(z) = refined PCA coords (zeros until fit_pca has run --
60
+ plateau coords are monitoring signals, never gradient carriers)."""
61
+ Xc = self._np(Z)
62
+ if self.mean is None or self.comp is None:
63
+ return np.zeros((len(Xc), 2), dtype=np.float32)
64
+ base = (Xc - self.mean) @ self.comp
65
+ return (base @ self.refined.T).astype(np.float32)
66
+
67
+ def refine_gradient(self, coords: np.ndarray, proto_coords: np.ndarray,
68
+ lr: float = 1e-3) -> float:
69
+ """One gradient step aligning Pi(z) mass toward prototype plateau.
70
+
71
+ Uses the analytic gradient of the local density at the sample points;
72
+ returns the mean gradient norm (monitoring signal for the retrain
73
+ rule: if ||grad rho|| > delta, Pi is refined for one epoch).
74
+ """
75
+ if self.refined is None:
76
+ return 0.0
77
+ base = (np.asarray(coords, dtype=np.float32) @ self.refined.T)
78
+ diff = base[:, None, :] - proto_coords[None, :, :]
79
+ d2 = (diff ** 2).sum(-1)
80
+ w = np.exp(-d2 / (2 * self.sigma ** 2))
81
+ grad_pts = -(diff * w[..., None]).sum(1) / (self.sigma ** 2)
82
+ g = grad_pts.mean(0) # (2,)
83
+ # chain rule back to refined (2x2): dL/dR = outer(g, base_mean)
84
+ self.refined -= lr * np.outer(g, coords.mean(0)).astype(np.float32)
85
+ return float(np.linalg.norm(g))
86
+
87
+ # ------------------------------------------------------------ monitoring
88
+ def density(self, pts: np.ndarray, proto_coords: np.ndarray) -> np.ndarray:
89
+ pts = self._np(pts)
90
+ proto_coords = self._np(proto_coords)
91
+ diff = pts[:, None, :] - proto_coords[None, :, :]
92
+ d2 = (diff ** 2).sum(-1)
93
+ return np.exp(-d2 / (2 * self.sigma ** 2)).sum(1)
94
+
95
+ def saturation(self, proto_coords: np.ndarray) -> float:
96
+ """1 - normalized entropy of bump mass at the density argmax."""
97
+ pts = proto_coords
98
+ dens = self.density(pts, proto_coords)
99
+ p = dens / max(dens.sum(), 1e-12)
100
+ H = -(p * np.log(p + 1e-12)).sum()
101
+ Hn = H / np.log(len(p))
102
+ return float(1.0 - Hn)
103
+
104
+ def inertia(self, proto_coords: np.ndarray) -> float:
105
+ """Mean squared spread of prototypes on the plateau (PHASE1 trigger)."""
106
+ c = proto_coords.mean(0, keepdims=True)
107
+ return float(((proto_coords - c) ** 2).sum(1).mean())
108
+
109
+ def coords_of(self, Z: np.ndarray, protos: np.ndarray
110
+ ) -> Tuple[np.ndarray, np.ndarray]:
111
+ pc = self.project(protos)
112
+ return self.project(Z), pc