SabaPivot's picture
download
raw
18.5 kB
#!/usr/bin/env python3
"""Claim-matched numerical audit for Improved Stochastic Optimization of LogSumExp.
This script uses only finite distributions. It checks the Safe-KL LogSumExp
approximation, its exact gradients and stochastic oracle, curvature bounds,
CVaR limits, mini-batch bias, and projected-SGD convergence scaling.
"""
from __future__ import annotations
import argparse
import csv
import json
import math
from pathlib import Path
import numpy as np
def logmeanexp(values: np.ndarray) -> float:
maximum = float(np.max(values))
return maximum + math.log(float(np.mean(np.exp(values - maximum))))
def sigmoid(values: np.ndarray) -> np.ndarray:
positive = values >= 0
output = np.empty_like(values, dtype=float)
output[positive] = 1.0 / (1.0 + np.exp(-values[positive]))
exp_values = np.exp(values[~positive])
output[~positive] = exp_values / (1.0 + exp_values)
return output
def safe_weights(values: np.ndarray, alpha: float, rho: float) -> np.ndarray:
return sigmoid(values - alpha + math.log(rho)) / rho
def solve_alpha(values: np.ndarray, rho: float) -> float:
lower = float(np.min(values)) - 80.0
upper = float(np.max(values)) + 80.0
for _ in range(160):
middle = 0.5 * (lower + upper)
if float(np.mean(safe_weights(values, middle, rho))) > 1.0:
lower = middle
else:
upper = middle
return 0.5 * (lower + upper)
def safe_lse(values: np.ndarray, rho: float) -> tuple[float, float, np.ndarray]:
alpha = solve_alpha(values, rho)
softplus = np.logaddexp(0.0, values - alpha + math.log(rho))
objective = alpha - 1.0 + float(np.mean(softplus)) / rho
weights = safe_weights(values, alpha, rho)
return objective, alpha, weights
def finite_difference_gradient(values: np.ndarray, rho: float, step: float) -> np.ndarray:
gradient = np.empty_like(values)
for index in range(values.size):
plus = values.copy()
minus = values.copy()
plus[index] += step
minus[index] -= step
gradient[index] = (safe_lse(plus, rho)[0] - safe_lse(minus, rho)[0]) / (2.0 * step)
return gradient
def approximation_audit(rng: np.random.Generator) -> tuple[dict, list[dict]]:
rhos = [0.3, 0.1, 0.03, 0.01, 0.003, 0.001]
rows: list[dict] = []
upper_violations = []
proposition_lower_violations = []
maximum_based_lower_violations = []
proposition_lower_cells = 0
maximum_based_lower_cells = 0
monotonic_violations = 0
gradient_errors = []
gradient_sum_errors = []
maximum_density_ratios = []
for trial in range(24):
values = rng.normal(0.0, 2.0, size=37)
values[0] += 5.0
exact = logmeanexp(values)
kappa = math.exp(logmeanexp(2.0 * values) - 2.0 * exact)
maximum = float(np.max(values))
trial_approximations = []
for rho in rhos:
approximation, alpha, weights = safe_lse(values, rho)
trial_approximations.append(approximation)
upper_violations.append(max(0.0, approximation - exact))
proposition_lower_bound = None
maximum_based_lower_bound = None
if rho < 1.0 / kappa:
proposition_lower_bound = exact + rho / 2.0 + math.log(1.0 - rho * kappa)
proposition_lower_violations.append(
max(0.0, proposition_lower_bound - approximation)
)
proposition_lower_cells += 1
if rho < math.exp(exact - maximum):
maximum_based_lower_bound = exact - rho * math.exp(maximum - exact)
maximum_based_lower_violations.append(
max(0.0, maximum_based_lower_bound - approximation)
)
maximum_based_lower_cells += 1
maximum_density_ratios.append(float(np.max(weights) * rho))
rows.append(
{
"trial": trial,
"rho": rho,
"logmeanexp": exact,
"safe_lse": approximation,
"gap": exact - approximation,
"gap_over_rho": (exact - approximation) / rho,
"kappa": kappa,
"proposition_lower_bound": proposition_lower_bound,
"maximum_based_lower_bound": maximum_based_lower_bound,
"alpha": alpha,
"weight_sum": float(np.mean(weights)),
"max_density": float(np.max(weights)),
"density_cap": 1.0 / rho,
}
)
# F_rho increases when rho decreases.
if any(
trial_approximations[index + 1] + 1e-12 < trial_approximations[index]
for index in range(len(rhos) - 1)
):
monotonic_violations += 1
check_values = rng.normal(0.0, 1.5, size=31)
check_values[:2] += np.array([4.0, -3.0])
for rho in [0.2, 0.05, 0.01]:
_, _, weights = safe_lse(check_values, rho)
analytic = weights / check_values.size
numeric = finite_difference_gradient(check_values, rho, 1e-5)
gradient_errors.append(float(np.max(np.abs(analytic - numeric))))
gradient_sum_errors.append(abs(float(np.sum(analytic)) - 1.0))
median_gap_by_rho = {
str(rho): float(np.median([row["gap"] for row in rows if row["rho"] == rho]))
for rho in rhos
}
rate = float(
np.polyfit(
np.log(np.asarray(rhos[-4:])),
np.log(np.asarray([median_gap_by_rho[str(rho)] for rho in rhos[-4:]])),
1,
)[0]
)
summary = {
"cells": len(rows),
"rho_values": rhos,
"maximum_upper_bound_violation": max(upper_violations),
"proposition_lower_bound_cells": proposition_lower_cells,
"maximum_proposition_lower_bound_violation": max(
proposition_lower_violations, default=0.0
),
"maximum_based_lower_bound_cells": maximum_based_lower_cells,
"maximum_maximum_based_lower_bound_violation": max(
maximum_based_lower_violations, default=0.0
),
"monotonicity_violations": monotonic_violations,
"median_gap_by_rho": median_gap_by_rho,
"small_rho_loglog_gap_rate": rate,
"maximum_finite_difference_gradient_error": max(gradient_errors),
"maximum_gradient_sum_error": max(gradient_sum_errors),
"maximum_density_to_cap_ratio": max(maximum_density_ratios),
}
return summary, rows
def curvature_audit() -> tuple[dict, list[dict]]:
rows = []
for rho in [0.5, 0.2, 0.1, 0.05, 0.01]:
t = np.linspace(1e-5, 1.0 / rho - 1e-5, 200_001)
f_second = 1.0 / (t * (1.0 - rho * t))
s = np.linspace(-16.0, 16.0, 200_001)
exp_s = np.exp(s)
conjugate_second = exp_s / (1.0 + rho * exp_s) ** 2
rows.append(
{
"rho": rho,
"minimum_safe_kl_curvature": float(np.min(f_second)),
"claimed_strong_convexity_floor": rho,
"minimum_over_claimed_floor": float(np.min(f_second) / rho),
"maximum_conjugate_curvature": float(np.max(conjugate_second)),
"claimed_smoothness_ceiling": 1.0 / rho,
"maximum_over_claimed_ceiling": float(np.max(conjugate_second) * rho),
}
)
summary = {
"cells": len(rows),
"all_strong_convexity_checks_pass": all(
row["minimum_safe_kl_curvature"] >= row["claimed_strong_convexity_floor"]
for row in rows
),
"all_smoothness_checks_pass": all(
row["maximum_conjugate_curvature"] <= row["claimed_smoothness_ceiling"]
for row in rows
),
"observed_minimum_curvature_ratio": min(
row["minimum_over_claimed_floor"] for row in rows
),
"observed_maximum_smoothness_ratio": max(
row["maximum_over_claimed_ceiling"] for row in rows
),
}
return summary, rows
def cvar_audit(rng: np.random.Generator) -> tuple[dict, list[dict]]:
rho = 0.1
values = rng.normal(0.0, 1.0, size=1000)
values[-20:] += 5.0
top_count = int(round(rho * values.size))
cvar = float(np.mean(np.sort(values)[-top_count:]))
rows = []
for temperature in [1.0, 0.5, 0.2, 0.1, 0.05, 0.02]:
scaled, _, _ = safe_lse(values / temperature, rho)
adjusted = temperature * scaled - temperature * (math.log(rho) - 1.0)
rows.append(
{
"temperature": temperature,
"cvar": cvar,
"adjusted_safe_objective": adjusted,
"gap": adjusted - cvar,
"proposition_gap_ceiling": temperature / rho,
"inside_bounds": cvar - 1e-10 <= adjusted <= cvar + temperature / rho + 1e-10,
}
)
summary = {
"rho": rho,
"cells": len(rows),
"cvar": cvar,
"all_proposition_bounds_pass": all(row["inside_bounds"] for row in rows),
"gap_at_temperature_1": rows[0]["gap"],
"gap_at_temperature_0.02": rows[-1]["gap"],
"gap_reduction_factor": rows[0]["gap"] / rows[-1]["gap"],
}
return summary, rows
def oracle_audit(rng: np.random.Generator) -> tuple[dict, list[dict]]:
population = rng.normal(0.0, 1.0, size=4096)
population[-40:] = rng.normal(6.0, 0.2, size=40)
theta = 0.0
losses = 0.5 * (theta - population) ** 2
loss_gradients = theta - population
rho = 0.05
_, alpha, weights = safe_lse(losses, rho)
contributions = weights * loss_gradients
exact_safe_gradient = float(np.mean(contributions))
exact_lse_gradient = float(
np.sum(np.exp(losses - np.max(losses)) * loss_gradients)
/ np.sum(np.exp(losses - np.max(losses)))
)
rows = []
for batch_size in [4, 16, 64, 256]:
repetitions = 4000
indices = rng.integers(0, population.size, size=(repetitions, batch_size))
proposed = np.mean(contributions[indices], axis=1)
batch_losses = losses[indices]
batch_gradients = loss_gradients[indices]
shifted = batch_losses - np.max(batch_losses, axis=1, keepdims=True)
batch_probabilities = np.exp(shifted)
baseline = np.sum(batch_probabilities * batch_gradients, axis=1) / np.sum(
batch_probabilities, axis=1
)
rows.append(
{
"batch_size": batch_size,
"repetitions": repetitions,
"exact_safe_gradient": exact_safe_gradient,
"proposed_mean": float(np.mean(proposed)),
"proposed_absolute_bias": abs(float(np.mean(proposed)) - exact_safe_gradient),
"proposed_rmse": float(
np.sqrt(np.mean((proposed - exact_safe_gradient) ** 2))
),
"exact_logsumexp_gradient": exact_lse_gradient,
"batch_logsumexp_mean": float(np.mean(baseline)),
"batch_logsumexp_absolute_bias": abs(
float(np.mean(baseline)) - exact_lse_gradient
),
}
)
sample_sizes = np.asarray([32, 128, 512, 2048, 8192])
rmse_rows = []
for sample_size in sample_sizes:
repetitions = 500
estimates = np.empty(repetitions)
for repetition in range(repetitions):
indices = rng.integers(0, population.size, size=int(sample_size))
estimates[repetition] = float(np.mean(contributions[indices]))
rmse_rows.append(
{
"samples": int(sample_size),
"repetitions": repetitions,
"rmse": float(
np.sqrt(np.mean((estimates - exact_safe_gradient) ** 2))
),
}
)
slope = float(
np.polyfit(
np.log(sample_sizes),
np.log([row["rmse"] for row in rmse_rows]),
1,
)[0]
)
summary = {
"rho": rho,
"alpha": alpha,
"exact_safe_gradient": exact_safe_gradient,
"exact_logsumexp_gradient": exact_lse_gradient,
"batch_comparison_cells": len(rows),
"oracle_scaling_cells": len(rmse_rows),
"oracle_rmse_loglog_slope": slope,
"smallest_proposed_bias": min(row["proposed_absolute_bias"] for row in rows),
"largest_batch_logsumexp_bias": max(
row["batch_logsumexp_absolute_bias"] for row in rows
),
"oracle_scaling": rmse_rows,
}
return summary, rows + rmse_rows
def minimized_safe_objective(theta: float, population: np.ndarray, rho: float) -> float:
losses = 0.5 * (theta - population) ** 2
return safe_lse(losses, rho)[0]
def golden_section_minimize(population: np.ndarray, rho: float) -> tuple[float, float]:
left, right = -2.0, 2.0
ratio = (math.sqrt(5.0) - 1.0) / 2.0
x1 = right - ratio * (right - left)
x2 = left + ratio * (right - left)
f1 = minimized_safe_objective(x1, population, rho)
f2 = minimized_safe_objective(x2, population, rho)
for _ in range(100):
if f1 < f2:
right, x2, f2 = x2, x1, f1
x1 = right - ratio * (right - left)
f1 = minimized_safe_objective(x1, population, rho)
else:
left, x1, f1 = x1, x2, f2
x2 = left + ratio * (right - left)
f2 = minimized_safe_objective(x2, population, rho)
theta = 0.5 * (left + right)
return theta, minimized_safe_objective(theta, population, rho)
def convergence_audit(rng: np.random.Generator) -> tuple[dict, list[dict]]:
population = rng.normal(-0.2, 0.8, size=2048)
population[-30:] = rng.normal(3.5, 0.15, size=30)
rho = 0.1
theta_star, objective_star = golden_section_minimize(population, rho)
rows = []
budgets = [256, 1024, 4096, 16384]
repetitions = 64
for budget in budgets:
theta = np.full(repetitions, 1.5)
initial_losses = 0.5 * (1.5 - population) ** 2
alpha = np.full(repetitions, solve_alpha(initial_losses, rho))
theta_sum = np.zeros(repetitions)
alpha_sum = np.zeros(repetitions)
step = 0.45 / math.sqrt(budget)
for _ in range(budget):
indices = rng.integers(0, population.size, size=repetitions)
samples = population[indices]
losses = 0.5 * (theta - samples) ** 2
weights = sigmoid(losses - alpha + math.log(rho)) / rho
gradient_theta = weights * (theta - samples)
gradient_alpha = 1.0 - weights
theta = np.clip(theta - step * gradient_theta, -2.0, 2.0)
alpha = np.clip(alpha - step * gradient_alpha, -5.0, 20.0)
theta_sum += theta
alpha_sum += alpha
average_theta = theta_sum / budget
excess = np.asarray(
[
minimized_safe_objective(value, population, rho) - objective_star
for value in average_theta
]
)
rows.append(
{
"iterations": budget,
"repetitions": repetitions,
"step_size": step,
"median_surrogate_excess": float(np.median(excess)),
"mean_surrogate_excess": float(np.mean(excess)),
"q90_surrogate_excess": float(np.quantile(excess, 0.9)),
"median_theta_error": float(np.median(np.abs(average_theta - theta_star))),
}
)
slope = float(
np.polyfit(
np.log(np.asarray(budgets)),
np.log(np.asarray([row["median_surrogate_excess"] for row in rows])),
1,
)[0]
)
summary = {
"rho": rho,
"theta_star": theta_star,
"objective_star": objective_star,
"cells": len(rows),
"repetitions_per_cell": repetitions,
"median_excess_loglog_slope": slope,
"median_excess_reduction_factor": (
rows[0]["median_surrogate_excess"] / rows[-1]["median_surrogate_excess"]
),
"all_median_excess_values_decrease": all(
rows[index + 1]["median_surrogate_excess"]
< rows[index]["median_surrogate_excess"]
for index in range(len(rows) - 1)
),
}
return summary, rows
def write_csv(path: Path, rows: list[dict]) -> None:
keys: list[str] = []
for row in rows:
for key in row:
if key not in keys:
keys.append(key)
with path.open("w", newline="") as handle:
writer = csv.DictWriter(handle, fieldnames=keys)
writer.writeheader()
writer.writerows(rows)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--output-dir", type=Path, default=Path("wave3-output"))
args = parser.parse_args()
args.output_dir.mkdir(parents=True, exist_ok=True)
rng = np.random.default_rng(20260726)
approximation, approximation_rows = approximation_audit(rng)
curvature, curvature_rows = curvature_audit()
cvar, cvar_rows = cvar_audit(rng)
oracle, oracle_rows = oracle_audit(rng)
convergence, convergence_rows = convergence_audit(rng)
results = {
"paper": {
"title": "Improved Stochastic Optimization of LogSumExp",
"openreview": "TzQElzflxR",
"arxiv": "2509.24894",
"official_repository": "https://github.com/egorgladin/logsumexp-approx",
},
"approximation_and_gradient": approximation,
"curvature": curvature,
"cvar": cvar,
"stochastic_oracle": oracle,
"projected_sgd": convergence,
"limitations": [
"Finite-distribution mechanism audit, not a proof of the paper's theorems.",
"Does not rerun the paper's neural OT, California Housing, or MNIST training.",
],
}
(args.output_dir / "results.json").write_text(
json.dumps(results, indent=2, sort_keys=True) + "\n"
)
write_csv(args.output_dir / "approximation_sweep.csv", approximation_rows)
write_csv(args.output_dir / "curvature_sweep.csv", curvature_rows)
write_csv(args.output_dir / "cvar_sweep.csv", cvar_rows)
write_csv(args.output_dir / "oracle_sweep.csv", oracle_rows)
write_csv(args.output_dir / "convergence_sweep.csv", convergence_rows)
print(json.dumps(results, indent=2, sort_keys=True))
if __name__ == "__main__":
main()

Xet Storage Details

Size:
18.5 kB
·
Xet hash:
6bcd5c880dce2b36c170df35f790eaba9d29526652a489f74f83004ce55d83f4

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