Spaces:
Running
Running
| """Numerical primitives for the frozen judged-baseline reconstruction. | |
| The game is | |
| min_x max_y (a/2) x^2 + c x y - (b/2) y^2, | |
| so its GDA Jacobian is [[-a, -c], [c, -b]]. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| def make_game(a: float, b: float, c: float) -> dict[str, object]: | |
| jacobian = np.array([[-a, -c], [c, -b]], dtype=float) | |
| return { | |
| "a": float(a), | |
| "b": float(b), | |
| "c": float(c), | |
| "jacobian": jacobian, | |
| "lam": np.linalg.eigvals(jacobian), | |
| } | |
| def gradf(state: tuple[float, float], game: dict[str, object]) -> tuple[float, float]: | |
| x, y = state | |
| a = float(game["a"]) | |
| b = float(game["b"]) | |
| c = float(game["c"]) | |
| return a * x + c * y, c * x - b * y | |
| def simulate( | |
| game: dict[str, object], | |
| x0: float, | |
| y0: float, | |
| beta: float, | |
| rho: float, | |
| eps: float, | |
| h: float, | |
| steps: int, | |
| ) -> np.ndarray: | |
| """Run the simultaneous, bias-corrected Adam-DA update from the paper.""" | |
| x, y = float(x0), float(y0) | |
| mx = my = vx = vy = 0.0 | |
| trajectory = np.empty((steps + 1, 2), dtype=float) | |
| trajectory[0] = (x, y) | |
| for n in range(1, steps + 1): | |
| gx, gy = gradf((x, y), game) | |
| mx = beta * mx + (1.0 - beta) * gx | |
| my = beta * my + (1.0 - beta) * gy | |
| vx = rho * vx + (1.0 - rho) * gx * gx | |
| vy = rho * vy + (1.0 - rho) * gy * gy | |
| m_bias = 1.0 - beta**n | |
| v_bias = 1.0 - rho**n | |
| x -= h * (mx / m_bias) / np.sqrt(vx / v_bias + eps) | |
| y += h * (my / m_bias) / np.sqrt(vy / v_bias + eps) | |
| trajectory[n] = (x, y) | |
| if not np.isfinite(x + y) or max(abs(x), abs(y)) > 1e8: | |
| trajectory[n:] = np.nan | |
| break | |
| return trajectory | |
| def converges( | |
| game: dict[str, object], | |
| beta: float, | |
| rho: float, | |
| eps: float, | |
| h: float, | |
| T: int = 1500, | |
| ) -> bool: | |
| trajectory = simulate(game, 0.5, 0.5, beta, rho, eps, h, T) | |
| if not np.isfinite(trajectory).all(): | |
| return False | |
| radius = np.linalg.norm(trajectory, axis=1) | |
| tail = radius[-max(100, T // 10) :] | |
| return bool(tail.mean() < 0.08 * radius[0] and tail[-1] < 0.1 * radius[0]) | |
| def bound_continuous(game: dict[str, object], beta: float, eps: float) -> float: | |
| """Theorem 4.3, minimized over interaction-dominated eigenvalues.""" | |
| bounds: list[float] = [] | |
| for lam in np.asarray(game["lam"]): | |
| re, im = abs(float(lam.real)), abs(float(lam.imag)) | |
| if im > re and re > 0.0: | |
| bounds.append(2.0 * np.sqrt(eps) * (1.0 - beta) * re / ((1.0 + beta) * (im**2 - re**2))) | |
| return float(min(bounds)) if bounds else 0.0 | |
| def bound_discrete(game: dict[str, object], beta: float, eps: float) -> float: | |
| """Theorem 4.4, minimized over the full Jacobian spectrum.""" | |
| bounds: list[float] = [] | |
| for lam in np.asarray(game["lam"]): | |
| re, im = abs(float(lam.real)), abs(float(lam.imag)) | |
| if re == 0.0: | |
| return 0.0 | |
| denominator = (1.0 + beta**2) * abs(lam) ** 2 + 2.0 * beta * (im**2 - re**2) | |
| if denominator > 0.0: | |
| bounds.append(2.0 * np.sqrt(eps) * (1.0 - beta**2) * re / denominator) | |
| return float(min(bounds)) if bounds else 0.0 | |