erring_order.py
Checks: SIZE: The cell tent bound theorem; property.
Cited on: SIZE.
To run it: save erring_order.py, with erring_cells.py, erring_label.py, erring_lower.py, erring_widths.py, erring_zones.py beside it; needs numpy.
"""
erring_order.py -- does putting the widths out of order buy the erring
labelling a constant: the least continuum error over every partition
against the least over nondecreasing widths, at the same m.
QUESTION. P cuts [0, 1) into m intervals, E(P) its continuum error
(erring_cells.py's cell_errs, overflow [1, 2)). Over nondecreasing
widths E(P) >= (1 - o(1))/(2 log m) is a theorem; lim sup kappa_m log m
<= 1/2. At the same m, does the least E over all P fall below the least
over sorted P by a ratio that stays away from 1 as m grows? The same
question for LB2'(P) = sum_a T_aa + 2 sum_{a<b} T_ba, the tent bound
with each pair charged at its later interval, which never sorts:
E >= LB2 >= LB2' at every P.
DERIVATION (the route this search tests; proved through (3), open
after). P in position order, c_0 = 0 not a boundary, c_m = 1 one.
(1) LB2 >= LB2' = sum_a T_aa + 2 sum_{a<b} T_ba: max >= either side,
so each pair is charged at its later interval b; nothing sorted.
(2) sum_{a<b} T_ba integrates h_b over t in [mid_b, mid_b + c_(b-1)),
and on a gap I_j (j >= 2, both ends boundaries) h_b is the larger
end tent, integral phi = w_b^2/4 if w_j >= w_b, else
w_j (2 w_b - w_j)/4 (erring_cells.py's tents closed form); either
way phi >= (w_b/4) min(w_b, w_j).
(3) I_j lies in b's sweep when j > b and c_j <= 2 c_(b-1), so
E >= sum_a T_aa + B/2,
S0_j = sum over b < j with 2 c_(b-1) >= c_j of w_b min(w_b, w_j),
B = sum_j (S0_j + o_j), o_j = w_j^2/2 when 2 c_(j-1) >= w_j, else
0: then the left half of j's own tent at c_j lies on [mid_j, c_j),
inside j's sweep and before every gap counted, integral w_j^2/8.
Sorted, S0_j + o_j is at least the sorted proof's S'_j - w_j^2/2.
(4) OPEN: B >= (1 - o(1))/log m once max w and the prefix [0, delta)
are handled as in erring_cells.py (6). In the continuum (w(t) the
width of t's interval) B = int S0(t)/w(t) dt, S0(t) = int over
[t/2, t] of min(w(s), w(t)). A flat Cauchy-Schwarz is expected
to fail (an unrun estimate: a sawtooth of steep ramps keeps B a
constant while int w/S0 grows past log m); the threshold form
was hoped not to: with theta = 2/log m, over [delta, 1], B >= theta
(1 - delta - |H|) + |H|^2/Lambda_H >= (1 - delta)^2/L when
Lambda_H = int over H of w/S0 is at most L and L >= (1 - delta)
log m, H the steep landings S0(t) < theta w(t); the target needs
L = (1 + o(1)) log m.
Against the potential S(t) = int_0^t w, S0 = S(t) - S(t/2) - X(t),
X(t) = int over [t/2, t] of (w(s) - w(t))_+: the S(t/2) part is
the sorted dyadic lemma; X is a spike's excess (one interval of
width W above the ramp around it adds W^2 to S but W w(t) to S0).
Owed: the damage X adds to Lambda_H is o(log m) whenever
B < 1/log m. Heuristically, over a ramp w ~ e^(Kt), a spike at s
saves about W/K as a landing and costs about W s as a source, a
loss once s > 1/K. (Superseded: b_slow.py's slow staircase, each
member c' times its window's square mass and every member in H
(X = 0 on it), raises Lambda_H to (2 - o(1)) log m at B < 1/log m wherever
the exponential profile keeps B below 1/log m by order 1/log^2 m,
so neither form reaches the constant and the X lemma is moot; E >= sum T_aa + B/2 stands.)
The prefix may be delta = 1/sqrt(log m): (1 - delta)^2 -> 1, the
window term 8 ceil(log2(1/delta)) is O(log(log m)), and with
eta = 2/sqrt(log m) and max w < eta (else E >= S_m/8 spends it)
every interval past delta has 2 c_(j-1) >= eta > w_j, so carries
o_j and w_j^2/B_j <= 2. In u = log w,
S0(t)/w(t) = int over [t/2, t] of exp(-(u(t) - u(s))_+) ds: scale
free, and a point below an earlier peak in its window pays the
peak's length.
DESIGN. A compass search per m on a width vector, each coordinate moved
by +-s times the mean, s halved when a sweep finds nothing, to s below
1e-7; then 12 hops, each a random multiplicative jolt of the best and a
descent, the best kept. SORT arm: the coordinates are the increments
d_1 = w_1 > 0, d_j = w_j - w_(j-1) >= 0, so ties are reachable; starts
geometric widths at ratios 1, 1.15, 1.4 and H_n's sorted widths. ALL
arm: the coordinates are the widths; starts the SORT optimum, three
shuffles of it and three random partitions (Dirichlet(1)). Then the
SORT arm restarts from the ALL optimum sorted, so a weak SORT search
cannot inflate the gap (4 hops there). Steps start at s = 0.25, at
0.05 in a hop; a jolt multiplies by exp(N(0, 0.3)); a move is kept when
it lowers f by more than 1e-15. The frozen design named a bracketed 1D
search on the cuts and the exponential cuts as SORT starts; the compass
on widths and the geometric starts replaced them before the first run.
Beside it, the integer lattice: every
composition of N = 14 into m parts, m = 2..7, least E over all of them
against least over the nondecreasing ones.
PREDICTIONS (written before the run). (P1) rho_m = E_all/E_sort < 1 at
small m: the overflow end favours a narrow last interval. (P2) The ALL
optimum's descents (w_(j+1) < w_j) sit within the last two positions,
m - j <= 2. (P3) rho_m rises with m. (P4) the LB2' arm's ratio >= 0.9.
KILL (of "order buys no constant"). rho_m <= 0.95 at each of the three
largest m of the E arm, rising across them by no more than 0.01, AND an
ALL optimum among them with a descent at m - j >= 3. A descent at the
top end alone kills nothing.
CONTROLS (read before any verdict). C1 cell_errs against E(H_n) =
(n^2 + 3n - 1)/(8n^2), n = 2..6. C2 the search recovers a planted
minimum: f = |w - w*|^2 at a random unsorted w*, m = 8, within 1e-6.
C3 at m = 2 both arms equal a 1D grid's least (20001 points, both
orders) to 1e-6. C4 LB2' <= E at every E-arm optimum (the LB2'
arm's optima are not checked). C5 E and LB2' at least sum T_aa + B/2 at
3000 random partitions, m = 2..29, Dirichlet with parameter 0.2, 1 or 5.
FINDINGS (copied from the printed run; toy scale, m <= 16). Controls
5/5: C1 max |diff| 6.66e-16, C2 2.63e-08, C3 0.21428571 on both arms and
the grid, C4 max LB2' - E -1.35e-03, C5 (DERIVATION (3), added after the
search ran, on its own generator) least margin 4.05e-15 over 3000
random partitions. KILL missed: rho at m = 8, 10, 12
is 1.0000, 0.9995, 0.9996. E arm, rho_m from m = 3 to 12: 1.0000,
0.9999, 0.9999, 0.9999, 1.0000, 0.9995, 0.9996; the ALL optimum's
descents are drops under 10% inside runs of similar widths (m = 10:
0.695 then 0.688, 0.756 then 0.703; m = 12: 0.552 then 0.499; widths x
m); the whole gap E_sort - E_all is at most 6.6e-5 (m = 10). P1 holds in
sign only (rho is 1 at m = 3), its mechanism wrong: the ALL optima at m
= 8, 10, 12 end in their widest interval. P2 is wrong: no top-end
effect. E_all * 2 log m climbs 0.3852 to 0.5893. The lattice ratio is 1
at m = 2..6; its 0.9672 at m = 7 is the grid's: the least E over every
composition of 14 into 7 parts equals m = 6's, 0.150510. LB2' arm: rho
0.9517, 0.9688, 0.9688, 0.9700, 0.9698, 0.9714 at m = 3, 4, 6, 8, 12,
16, flat from m = 4, its ALL optimum's one descent at the bottom end:
the first interval wider than the second (1.028/m to 2.494/m; at m = 16
the second, 2.969/m, wider than the third). Charging each pair at its
later interval leaves the first interval charged by its own cell alone;
whether the derivation's delta-prefix removes the whole gap is unrun. P4
holds. P3 is not readable at these m. Run: about 7 min wall-clock (the
output file's times; the per-m times printed sum to 413 s), about 30 MB
resident read mid-run by tasklist, neither printed by the script.
"""
import os
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
import itertools # noqa: E402
import math # noqa: E402
import time # noqa: E402
import numpy as np # noqa: E402
from erring_cells import cell_errs, tents # 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 cuts(w):
w = np.asarray(w, dtype=float)
c = np.concatenate([[0.0], np.cumsum(w / w.sum())])
c[-1] = 1.0
return c
def E(w):
return float(cell_errs(cuts(w)).sum())
def lb2p(w):
T = tents(cuts(w))
return float(np.trace(T) + 2 * np.tril(T, -1).sum())
def bsum(w):
c = cuts(w)
w = np.diff(c)
tot = 0.0
for b in range(len(w)):
for j in range(b + 1, len(w)):
if c[j + 1] <= 2 * c[b]:
tot += w[b] * min(w[b], w[j])
if 2 * c[b] >= w[b]:
tot += w[b] ** 2 / 2
return tot
def widths_all(x):
x = np.maximum(x, 1e-9)
return x / x.sum()
def widths_sort(x):
d = np.maximum(x, 0.0)
d[0] = max(d[0], 1e-9)
w = np.cumsum(d)
return w / w.sum()
def incr(w):
w = np.sort(np.asarray(w, dtype=float))
return np.concatenate([[w[0]], np.diff(w)])
def compass(f, x0, to_w, s0=0.25, tol=1e-7):
x = np.array(x0, dtype=float)
fx = f(to_w(x))
s = s0
m = len(x)
while s > tol:
moved = False
for i in range(m):
for sg in (1.0, -1.0):
y = x.copy()
y[i] = y[i] + sg * s * x.mean()
fy = f(to_w(y))
if fy < fx - 1e-15:
x, fx, moved = y, fy, True
break
if not moved:
s /= 2
return x, fx
def search(f, starts, to_w, rng, hops=12):
best = None
for x0 in starts:
x, fx = compass(f, x0, to_w)
if best is None or fx < best[1]:
best = (x, fx)
for _ in range(hops):
jolt = best[0] * np.exp(rng.normal(0, 0.3, len(best[0])))
x, fx = compass(f, jolt, to_w, s0=0.05)
if fx < best[1]:
best = (x, fx)
return to_w(best[0]), best[1]
def descents(w):
m = len(w)
return [m - j for j in range(1, m) if w[j] < w[j - 1] - 1e-9]
def h_sorted(n):
return np.sort(np.concatenate([[0.5], np.ones(n - 1), [0.5]]))
def arms(f, m, rng):
geo = [np.array([q ** j for j in range(m)]) for q in (1.0, 1.15, 1.4)]
sstarts = [incr(g / g.sum()) for g in geo]
if m >= 3:
sstarts.append(incr(h_sorted(m - 1)))
ws, fs = search(f, sstarts, widths_sort, rng)
astarts = [ws.copy()] + [rng.permutation(ws) for _ in range(3)] + \
[rng.dirichlet(np.ones(m)) for _ in range(3)]
wa, fa = search(f, astarts, widths_all, rng)
ws2, fs2 = search(f, [incr(wa)], widths_sort, rng, hops=4)
if fs2 < fs:
ws, fs = ws2, fs2
return ws, fs, wa, fa
def section_controls(rng):
print("CONTROLS")
worst = 0.0
for n in range(2, 7):
w = np.concatenate([[0.5], np.ones(n - 1), [0.5]])
worst = max(worst, abs(E(w) - (n * n + 3 * n - 1) / (8 * n * n)))
check("C1 cell_errs against E(H_n), n = 2..6", worst < 1e-12,
f"max |diff| {worst:.2e}")
tgt = rng.dirichlet(np.ones(8))
w, fx = search(lambda v: float(((v - tgt) ** 2).sum()),
[np.ones(8) / 8], widths_all, rng, hops=0)
check("C2 the search recovers a planted unsorted minimum, m = 8",
np.abs(w - tgt).max() < 1e-6, f"max |diff| {np.abs(w - tgt).max():.2e}")
s = np.linspace(1e-6, 1 - 1e-6, 20001)
grid = min(E([t, 1 - t]) for t in s)
gsort = min(E([t, 1 - t]) for t in s if t <= 0.5)
ws, fs, wa, fa = arms(E, 2, rng)
check("C3 m = 2: both arms against a 1D grid",
abs(fa - grid) < 1e-6 and abs(fs - gsort) < 1e-6,
f"ALL {fa:.8f} grid {grid:.8f}, SORT {fs:.8f} grid {gsort:.8f}")
return ws, fs, wa, fa
def section_reduction(rng):
print("REDUCTION: E >= LB2' >= sum T_aa + B/2 at random partitions")
worst, n = math.inf, 0
for _ in range(3000):
m = int(rng.integers(2, 30))
w = rng.dirichlet(np.ones(m) * rng.choice([0.2, 1.0, 5.0]))
lo = float(np.trace(tents(cuts(w)))) + bsum(w) / 2
worst = min(worst, lb2p(w) - lo, E(w) - lo)
n += 1
check("C5 LB2' and E at least sum T_aa + B/2, m = 2..29",
worst >= -1e-12, f"{n} partitions, least margin {worst:.2e}")
def section_lattice():
print("LATTICE: compositions of N = 14 into m parts")
N = 14
for m in range(2, 8):
best_all, best_sort, arg = math.inf, math.inf, None
for bars in itertools.combinations(range(1, N), m - 1):
w = np.diff(np.array((0,) + bars + (N,))) / N
e = E(w)
if e < best_all:
best_all, arg = e, w
if np.all(np.diff(w) >= 0) and e < best_sort:
best_sort = e
print(f" m {m}: E_all {best_all:.6f} at {np.round(arg * N).astype(int)}"
f", E_sort {best_sort:.6f}, ratio {best_all / best_sort:.4f}")
def section_arm(name, f, ms, rng, guard=None):
print(f"ARM {name}")
rows = []
for m in ms:
t0 = time.time()
ws, fs, wa, fa = arms(f, m, rng)
L = 2 * math.log(m)
rows.append((m, fs, fa, fa / fs, descents(wa)))
print(f" m {m:3d}: sort {fs:.6f} all {fa:.6f} rho {fa / fs:.4f}"
f" gap {fs - fa:.1e}"
f" all*2log m {fa * L:.4f} descents(m-j) {descents(wa)}"
f" [{time.time() - t0:.1f} s]")
print(" all widths x m: "
+ " ".join(f"{v * m:.3f}" for v in wa))
if guard is not None:
guard.append((wa, ws))
return rows
def main():
rng = np.random.default_rng(1693)
section_controls(rng)
section_reduction(np.random.default_rng(5))
section_lattice()
opt = []
rows = section_arm("E", E, [3, 4, 5, 6, 8, 10, 12], rng, opt)
worst = max(lb2p(w) - E(w) for pair in opt for w in pair)
check("C4 LB2' <= E at every E-arm optimum", worst <= 1e-12,
f"max LB2' - E {worst:.2e}")
section_arm("LB2'", lb2p, [3, 4, 6, 8, 12, 16], rng)
last = rows[-3:]
kill = (all(r[3] <= 0.95 for r in last)
and last[-1][3] - last[0][3] <= 0.01
and any(any(d >= 3 for d in r[4]) for r in last))
print(f"KILL of 'order buys no constant': {'FIRED' if kill else 'missed'}"
f" (rho at m = {[r[0] for r in last]}: "
f"{[round(r[3], 4) for r in last]})")
print(f"checks: {sum(CHECKS)}/{len(CHECKS)}")
raise SystemExit(0 if all(CHECKS) else 1)
if __name__ == "__main__":
main()