erring_zones.py

Checks: SIZE: Growing widths observation; pattern; SIZE: The rate of the least error theorem; observation.

Cited on: SIZE.

To run it: save erring_zones.py, with erring_label.py, erring_widths.py beside it; needs numpy.

"""
erring_zones.py -- the continuum error of a cut set, the scaling limits
of the growing-width profiles, and the zone construction at reachable m.

QUESTION. Fill [0, c) with m intervals below one overflow label
[c, 2c - 1), the error as in erring_label.py, kappa_m the limsup over c
of the least error over c^2. A cut set P scaled by k has error / (kc)^2
tending to the continuum error E(P / c): each cell's area less its
largest label area, overflow [1, 2). What are the growing-width
profiles' limits, each an upper bound on kappa_m at its m, and what does
the zone construction (independent uniform phases per zone, the route
by which kappa_m -> 0) print at the m a run reaches?

DESIGN. cont_error computes E exactly: the area of a cell [A, A + a) x
[B, B + b) below t is G(t) - G(t - a) - G(t - b) + G(t - a - b),
G(t) = max(t, 0)^2 / 2, so a label's share is a difference of two such
areas. zones builds the construction: [0, t0) one interval, [t0, 1) cut
into zones of width D, zone k a grid of width eps z_k^p (z_k its start)
at a phase uniform on [0, w_k), zone ends cuts. bound prints the mean
bound B = main + 2 t0 + 4 (nz + 1) wmax + 2 (nz + 2) wmax, main the
integral over x, y >= t0, x + y < 1 of (w(x) + w(y)) / (4 w(x + y)) on a
midpoint grid; the edge terms are areas, far above what the edges cost.

PREDICTIONS AND KILLS (written before the run).
  C1 the continuum error of rounding's cut set (u = 0.8, m = 162) equals
     erring_label's error of its x16 scaling over c^2 within 0.002.
  L1..L4 the limits of the profiles p = 1 at m = 162 and 400, p = 2 at
     m = 400 (top 0.8, c = 4000) and p = 2 at m = 1000 (top 0.9,
     c = 10000): 0.0905..0.0908, 0.0857..0.0860, 0.0799..0.0802,
     0.0734..0.0740, each at or a little above its largest scaled
     reading. Kill: a limit less its largest scaled reading below
     -0.0005 or above +0.002 (cont_error and the scaling disagree).
  Z (written before the zone run, as a check of the bound) the mean of
     E over 8 seeds at most B; at the scales a run reaches B's edge
     areas dwarf main, so Z prints beside its bound and checks nothing.

FINDINGS (from the printed run; both controls passed first: C1 0.1030
against 0.1030 at m = 162, the s = 3 form exact).
  L1..L4 held, the kill silent: the limits print 0.09054, 0.08572,
     0.08000 and 0.07380, against the largest scaled readings 0.0905,
     0.0857, 0.0799 (x16) and 0.0734 (x4). So kappa_162 < 0.0906,
     kappa_400 < 0.0801 and kappa_1000 < 0.0739.
  Z: mean E 0.2285 (p = 1, eps 0.005, D = t0 = 0.1, m near 575)
     against B 0.5536; 0.1684 (p = 1, eps 0.0025, D = t0 = 0.05, m near
     1438) against 0.4870; 0.2074 and 0.2094 (p = 2, D = t0 = 0.1, eps
     0.01 and 0.005, m near 1550 and 3090) against 0.7448 and 0.4937.
     Eight seeds spread under 0.0014 in every case. At t0 = 0.1 the
     means sit near 2 t0 = 0.2, the bound's bottom-interval term (no
     arm splits E by interval), and halving eps at p = 2 moved E up by
     0.0020; the construction errs above rounding's 1/10. kappa_m -> 0
     rests on its proof, not on this run.
"""

import os

os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
import numpy as np  # noqa: E402

from erring_label import error, rounding  # noqa: E402
from erring_widths import profile_at, ratio  # noqa: E402

CHECKS = []


def check(name, ok, detail=""):
    CHECKS.append(bool(ok))
    tail = f"  ({detail})" if detail else ""
    print(f"  {'PASS' if ok else 'FAIL'}  {name}{tail}")


def G(t):
    t = np.maximum(t, 0.0)
    return t * t / 2


def below(a, b, t):
    """Area of {(u, v) in [0, a) x [0, b) : u + v < t}."""
    return G(t) - G(t - a) - G(t - b) + G(t - a - b)


def cont_error(cuts):
    """Continuum error of cuts 0 = c_0 < ... < c_m = 1, overflow [1, 2)."""
    C = np.asarray(cuts, dtype=float)
    lab = np.append(C, 2.0)
    A, W = C[:-1], np.diff(C)
    return sum(_rows(lab, A, W, i, i + 128) for i in range(0, len(W), 128))


def _rows(lab, A, W, i, j):
    """The error of the cells whose first summand is interval i..j - 1."""
    base = (A[i:j, None] + A[None, :]).ravel()
    a = np.broadcast_to(W[i:j, None], (len(W[i:j]), len(W))).ravel()
    b = np.broadcast_to(W[None, :], (len(W[i:j]), len(W))).ravel()
    lo = np.searchsorted(lab, base, side="right") - 1
    hi = np.searchsorted(lab, base + a + b, side="left") - 1
    best = np.zeros_like(base)
    K = 8
    for k in range(K + 1):
        L = np.minimum(lo + k, len(lab) - 2)
        land = (below(a, b, lab[L + 1] - base) - below(a, b, lab[L] - base))
        best = np.maximum(best, np.where(lo + k <= hi, land, 0.0))
    for i in np.flatnonzero(hi - lo > K):
        L = np.arange(lo[i], hi[i] + 1)
        land = (below(a[i], b[i], lab[L + 1] - base[i])
                - below(a[i], b[i], lab[L] - base[i]))
        best[i] = land.max()
    return float((a * b - best).sum())


def zones(p, eps, D, t0, rng):
    """[0, t0) one interval; zones of width D on [t0, 1), each a grid of
    width eps z^p (z the zone start) at an independent uniform phase."""
    cuts = [0.0, t0]
    z, ws = t0, []
    while z < 1 - 1e-12:
        z2 = min(z + D, 1.0)
        w = eps * z ** p
        ws.append(w)
        t = z + rng.uniform(0, w)
        while t < z2 - 1e-12:
            if t > cuts[-1] + 1e-12:
                cuts.append(t)
            t += w
        cuts.append(z2)
        z = z2
    return cuts, ws


def bound(p, eps, D, t0, n=1500):
    """B: main by a midpoint grid of n x n over [t0, 1)^2, plus areas."""
    starts = np.arange(t0, 1 - 1e-12, D)
    wz = eps * starts ** p

    def w(x):
        return wz[np.minimum(((x - t0) / D).astype(int), len(wz) - 1)]
    h = (1 - t0) / n
    x = t0 + (np.arange(n) + 0.5) * h
    X, Y = np.meshgrid(x, x)
    S = X + Y
    ok = S < 1
    main = float(((w(X) + w(Y)) / (4 * w(np.where(ok, S, X))))[ok].sum()
                 * h * h)
    nz, wmax = len(wz), float(wz.max())
    return main, main + 2 * t0 + 4 * (nz + 1) * wmax + 2 * (nz + 2) * wmax


def section_controls():
    print("controls")
    cu = rounding(4000, 0.8 * 4000 / 160, 0.8)
    m = len(cu) - 1
    c = 4000 * 16
    disc = error(tuple(16 * x for x in cu) + (2 * c - 1,), 2 * c - 1) / c / c
    cont = cont_error([x / 4000 for x in cu])
    check("C1: continuum error of rounding equals its x16 scaling",
          abs(disc - cont) < 0.002, f"m = {m}, {cont:.4f} against {disc:.4f}")
    two = cont_error([0, 0.3, 1])
    want = 3 * 0.3 ** 2 / 2 + 0.4 ** 2 / 2
    check("control: one cut at 0.3 is the s = 3 continuum form "
          "3a^2/2 + (1 - 2a)^2/2", abs(two - want) < 1e-12,
          f"{two:.6f} against {want:.6f}")


def section_limits(cases):
    """L1..L4: each profile cut set's continuum error, its limit under
    scaling, against its largest scaled reading."""
    print("profile limits: the continuum error of each cut set")
    for c, p, m, u, k in cases:
        cu = profile_at(c, p, m, u)
        lim = cont_error([x / c for x in cu])
        last = ratio(tuple(k * x for x in cu), k * c)
        print(f"    m = {m}  p = {p}  top {u}  c = {c}: limit {lim:.5f}, "
              f"x{k} {last:.4f}, limit - x{k} {lim - last:+.4f}")
        check(f"limit against x{k} at m = {m}, p = {p}",
              -0.0005 <= lim - last <= 0.002)


def section_zones(cases, seeds=8):
    """Z: the construction's mean error over seeds beside its bound B."""
    print("zone construction: mean E over seeds beside the bound B")
    for p, eps, D, t0 in cases:
        Es, ms = [], []
        for s in range(seeds):
            cuts, _ = zones(p, eps, D, t0, np.random.default_rng(s))
            ms.append(len(cuts) - 1)
            Es.append(cont_error(cuts))
        main, B = bound(p, eps, D, t0)
        print(f"    p = {p}  eps = {eps}  D = {D}  t0 = {t0}  m {min(ms)}.."
              f"{max(ms)}: E mean {np.mean(Es):.4f}, seeds {min(Es):.4f}.."
              f"{max(Es):.4f}; main {main:.4f}, B {B:.4f}")


def main():
    section_controls()
    if not all(CHECKS):
        print("\nCONTROL FAILED: nothing below is read.")
        raise SystemExit(1)
    section_limits([(4000, 1, 162, 0.8, 16), (4000, 1, 400, 0.8, 16),
                    (4000, 2, 400, 0.8, 16), (10000, 2, 1000, 0.9, 4)])
    section_zones([(1, 0.005, 0.1, 0.1), (1, 0.0025, 0.05, 0.05),
                   (2, 0.01, 0.1, 0.1), (2, 0.005, 0.1, 0.1)])
    print(f"{sum(CHECKS)}/{len(CHECKS)} checks passed")
    raise SystemExit(0 if all(CHECKS) else 1)


if __name__ == "__main__":
    main()