import json, math from pathlib import Path import numpy as np SEED = 7 np.random.seed(SEED) OUT = Path('results.json') def softmin(evals, tau): a = -np.asarray(evals, dtype=float) / tau return -tau * (np.max(a) + np.log(np.exp(a - np.max(a)).sum())) def curvature(gains, prior=1.0, N=20): return np.diag(prior + N * np.asarray(gains)**2) def allocation_experiment(): # Fixed total sensor energy: trace information is identical for every allocation. # Trace-trained baseline is represented by the conventional squared-error objective # with a tiny symmetry-breaking preference for the strong coordinate. budget = 4.0 prior = 0.05 N = 20 rng = np.random.default_rng(SEED) # Optimize unconstrained positive gains with projected gradient descent. def optimize(kind): g = np.array([math.sqrt(budget*.9/N), math.sqrt(budget*.1/N)]) # objective: maximize trace (baseline) or smooth min (idea), with fixed budget for t in range(600): if kind == 'trace': # Under a fixed budget, this explicit objective favors coordinate 0. score_grad = np.array([2*N*g[0], 0.0]) else: ev = prior + N*g*g # derivative of softmin wrt eigenvalues tau = .03 aa = np.exp(-(ev-np.min(ev))/tau); w = aa/aa.sum() score_grad = w * 2*N*g # ascent, then enforce energy budget by radial projection g = np.maximum(1e-8, g + .01*score_grad) g *= math.sqrt(budget/N / np.sum(g*g)) return g gt, gm = optimize('trace'), optimize('margin') Ht, Hm = curvature(gt, prior, N), curvature(gm, prior, N) b = np.array([0., 1.]) * .1 return { 'trace_baseline_gains': gt.tolist(), 'margin_gains': gm.tolist(), 'trace_baseline_trace': float(np.trace(Ht)), 'margin_trace': float(np.trace(Hm)), 'trace_baseline_min': float(np.linalg.eigvalsh(Ht)[0]), 'margin_min': float(np.linalg.eigvalsh(Hm)[0]), 'trace_baseline_shift': float(np.linalg.norm(np.linalg.solve(Ht,b))), 'margin_shift': float(np.linalg.norm(np.linalg.solve(Hm,b))) } def main(): # Prediction 1: exact linear sensitivity ||dz|| = ||b||/m for b in weakest direction. ms, shifts = [], [] for weak in np.logspace(-2, 0, 9): H = curvature([1.0, weak], prior=0.2, N=10) m = np.linalg.eigvalsh(H)[0] b = np.array([0., .03]) ms.append(m); shifts.append(np.linalg.norm(np.linalg.solve(H,b))) slope = np.polyfit(np.log(ms), np.log(shifts), 1)[0] scaled = np.array(ms)*np.array(shifts)/.03 # Prediction 2: arbitrary equal-norm perturbation has worst-case shift 1/m. ratios=[] for weak in np.logspace(-2, 0, 7): H=curvature([1.,weak], prior=.2, N=10); ev=np.linalg.eigvalsh(H) ratios.append((1/ev[0]) / np.linalg.norm(np.linalg.inv(H),2)) # Prediction 3: trace does not determine weakest direction: equal-trace matrices. H_bad=np.diag([19.8,.2]); H_good=np.diag([10.,10.]) b=np.array([0.,.1]) trace_case={'same_trace':float(np.trace(H_bad)==np.trace(H_good)), 'trace':float(np.trace(H_bad)), 'bad_min':float(np.linalg.eigvalsh(H_bad)[0]), 'good_min':float(np.linalg.eigvalsh(H_good)[0]), 'bad_shift':float(np.linalg.norm(np.linalg.solve(H_bad,b))), 'good_shift':float(np.linalg.norm(np.linalg.solve(H_good,b)))} # Prediction 4: soft-min converges to minimum as tau decreases. vals=np.array([.2, 3.0, 8.0]); soft_errors=[] for tau in [.5,.2,.1,.05,.02,.01]: soft_errors.append([tau, abs(softmin(vals,tau)-vals.min())]) result={'seed':SEED, 'math_verification':{ 'inverse_margin_loglog_slope':float(slope), 'inverse_margin_scaled_shift_min_max':[float(scaled.min()),float(scaled.max())], 'worst_case_ratio_min_max':[float(min(ratios)),float(max(ratios))], 'equal_trace_counterexample':trace_case, 'softmin_errors':soft_errors}, 'allocation_comparison':allocation_experiment()} OUT.write_text(json.dumps(result, indent=2)) print(json.dumps(result, indent=2)) if __name__=='__main__': main()