visv-Bro's picture
download
raw
3.03 kB
"""Smoke tests for botlib: exact identities, solver cross-checks, tiny EntUCB run."""
import numpy as np
import sys
sys.path.insert(0, __file__.rsplit("/", 1)[0])
from botlib import (make_instance, kantorovich, sinkhorn_log, entropic_value,
run_entucb)
def main():
rng = np.random.default_rng(0)
inst = make_instance(4, 5, seed=1, cost_kind="smooth")
N = inst.N
# 1) basis is rho-orthonormal
G = inst.B @ (inst.rho[:, None] * inst.B.T)
err_orth = np.max(np.abs(G - np.eye(N)))
print(f"[basis] max |B W B^T - I| = {err_orth:.2e}")
assert err_orth < 1e-10
# 2) Claim-1 mechanism: <c|pi> = theta* . a(pi) for random feasible plans
import ot as pot
errs = []
for _ in range(50):
# random feasible plan: mix of independent coupling and random sinkhorn plans
M = rng.standard_normal((inst.K, inst.Kp))
P = pot.sinkhorn(inst.mu, inst.nu, M - M.min(), reg=rng.uniform(0.05, 1.0))
lhs = inst.pairing(inst.c_vec, P)
rhs = float(inst.theta_star @ inst.embed(P))
errs.append(abs(lhs - rhs))
print(f"[claim1] max |<c|pi> - <theta*,a(pi)>| over 50 plans = {max(errs):.2e}")
assert max(errs) < 1e-10
# 3) Parseval: ||c||_L2(rho) = ||theta*||_2
n1 = np.sqrt(inst.inner(inst.c_vec, inst.c_vec))
n2 = np.linalg.norm(inst.theta_star)
print(f"[parseval] ||c||_rho = {n1:.12f} ||theta*||_2 = {n2:.12f}")
assert abs(n1 - n2) < 1e-10
# 4) our log-domain Sinkhorn vs POT (KL-vs-negentropy argmin equivalence)
eps = 0.1
P1, f, g = sinkhorn_log(inst.mu, inst.nu, inst.cost, eps)
P2 = pot.bregman.sinkhorn_log(inst.mu, inst.nu, inst.cost, reg=eps, numItermax=20000, stopThr=1e-14)
print(f"[sinkhorn] max |P_ours - P_pot| = {np.max(np.abs(P1 - P2)):.2e}")
assert np.max(np.abs(P1 - P2)) < 1e-8
# marginals
print(f"[sinkhorn] marginal err = {np.max(np.abs(P1.sum(1) - inst.mu)):.2e}, {np.max(np.abs(P1.sum(0) - inst.nu)):.2e}")
# 5) EOT value decreases to Kantorovich value as eps -> 0, gap >= 0
kv, _ = kantorovich(inst)
gaps = []
for eps in [0.5, 0.1, 0.02, 0.004]:
ev, _, _ = entropic_value(inst, eps)
gaps.append(ev - kv)
print(f"[eot->kant] kant={kv:.6f} gaps={['%.3e' % g for g in gaps]}")
assert all(g > -1e-9 for g in gaps) and gaps[-1] < gaps[0]
# 6) tiny EntUCB run end to end
res = run_entucb(inst, T=150, sigma=0.05, delta=0.1, lam=1.0,
Cbar=1.1 * np.linalg.norm(inst.theta_star),
eps_schedule=lambda t: 0.75 * t ** -0.75, eta=0.75, seed=3)
print(f"[entucb] T=150 kant_regret={res.kant_regret[-1]:.3f} ent_regret={res.ent_regret[-1]:.3f} "
f"bound_lit={res.bound_literal[-1]:.1f} cert_viol={res.cert_viol_frac:.3f} covered_all={res.covered_all}")
assert res.ent_regret[-1] <= res.bound_literal[-1]
# entropic regret should be nonnegative-ish up to certificate tolerance and grow slowly
print("smoke OK")
if __name__ == "__main__":
main()

Xet Storage Details

Size:
3.03 kB
·
Xet hash:
b0db61fed0d7d3a6e29dd8741fed74f5d40af9e5d9540fd6baac9f9e0a5a9ebe

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.