fix: ±inf z-scores, logistic→erf in phi_accrual, sample variance (N-1), ablation delta clamp
Bugs found by: bolt (1785275678), nestor (1785275558). 49/49 tests.
This commit is contained in:
parent
bc82e7a9ad
commit
0029837cd7
|
|
@ -460,7 +460,7 @@ def shuffle_test(messages: list[dict], n_shuffles: int = 100,
|
||||||
if std_s > 0:
|
if std_s > 0:
|
||||||
z = (obs - mean_s) / std_s
|
z = (obs - mean_s) / std_s
|
||||||
else:
|
else:
|
||||||
z = 0.0 if obs == mean_s else float('inf')
|
z = 0.0 if obs == mean_s else (10.0 if obs > mean_s else -10.0)
|
||||||
result["z_scores"][node] = round(z, 4)
|
result["z_scores"][node] = round(z, 4)
|
||||||
result["significant"][node] = abs(z) > 2.0
|
result["significant"][node] = abs(z) > 2.0
|
||||||
|
|
||||||
|
|
@ -504,12 +504,16 @@ def ablation_sensitivity(messages: list[dict], target: str,
|
||||||
for node in set(z_full) | set(z_abl):
|
for node in set(z_full) | set(z_abl):
|
||||||
if node == target:
|
if node == target:
|
||||||
continue
|
continue
|
||||||
deltas[node] = round(z_abl.get(node, 0) - z_full.get(node, 0), 4)
|
d = z_abl.get(node, 0) - z_full.get(node, 0)
|
||||||
|
# Clamp to avoid ±inf from zero-variance singletons
|
||||||
|
d = max(-100.0, min(100.0, d))
|
||||||
|
deltas[node] = round(d, 4)
|
||||||
|
|
||||||
gainers = {k: v for k, v in deltas.items() if v > 2.0}
|
gainers = {k: v for k, v in deltas.items() if v > 2.0}
|
||||||
losers = {k: v for k, v in deltas.items() if v < -2.0}
|
losers = {k: v for k, v in deltas.items() if v < -2.0}
|
||||||
|
|
||||||
mean_delta = sum(deltas.values()) / max(len(deltas), 1)
|
finite_deltas = [v for v in deltas.values() if math.isfinite(v)]
|
||||||
|
mean_delta = sum(finite_deltas) / max(len(finite_deltas), 1)
|
||||||
if mean_delta > 0.5:
|
if mean_delta > 0.5:
|
||||||
verdict = "suppressor"
|
verdict = "suppressor"
|
||||||
elif mean_delta < -0.5:
|
elif mean_delta < -0.5:
|
||||||
|
|
@ -842,7 +846,9 @@ def phi_accrual(messages: list, t_now: float = None,
|
||||||
# Compute inter-arrival intervals
|
# Compute inter-arrival intervals
|
||||||
intervals = [times[i+1] - times[i] for i in range(len(times) - 1)]
|
intervals = [times[i+1] - times[i] for i in range(len(times) - 1)]
|
||||||
mean_ival = sum(intervals) / len(intervals)
|
mean_ival = sum(intervals) / len(intervals)
|
||||||
variance = sum((x - mean_ival) ** 2 for x in intervals) / len(intervals)
|
# Sample variance (N-1) — population variance underestimates for small N
|
||||||
|
n_iv = len(intervals)
|
||||||
|
variance = sum((x - mean_ival) ** 2 for x in intervals) / max(n_iv - 1, 1)
|
||||||
std_ival = math.sqrt(variance) if variance > 0 else mean_ival * 0.1
|
std_ival = math.sqrt(variance) if variance > 0 else mean_ival * 0.1
|
||||||
|
|
||||||
liveness.mean_interval = mean_ival
|
liveness.mean_interval = mean_ival
|
||||||
|
|
@ -859,15 +865,10 @@ def phi_accrual(messages: list, t_now: float = None,
|
||||||
liveness.phi = 10.0 if t_diff > mean_ival * 1.5 else 0.0
|
liveness.phi = 10.0 if t_diff > mean_ival * 1.5 else 0.0
|
||||||
liveness.state = 'gray' if liveness.phi >= 3.0 else 'green'
|
liveness.state = 'gray' if liveness.phi >= 3.0 else 'green'
|
||||||
else:
|
else:
|
||||||
# Normal CDF approximation (error function)
|
# Normal CDF via error function (exact, not logistic approx)
|
||||||
# P(X <= t_diff) where X ~ N(mean, std)
|
# P(X <= t_diff) where X ~ N(mean, std)
|
||||||
z = (t_diff - mean_ival) / std_ival
|
z = (t_diff - mean_ival) / std_ival
|
||||||
# Approximate CDF using logistic approximation
|
cdf = 0.5 * (1.0 + math.erf(z / math.sqrt(2.0)))
|
||||||
# F(z) ≈ 1 / (1 + exp(-1.7 * z))
|
|
||||||
try:
|
|
||||||
cdf = 1.0 / (1.0 + math.exp(-1.7 * z))
|
|
||||||
except OverflowError:
|
|
||||||
cdf = 1.0 if z > 0 else 0.0
|
|
||||||
|
|
||||||
# φ = -log10(1 - F(t_diff))
|
# φ = -log10(1 - F(t_diff))
|
||||||
if cdf >= 1.0 - 1e-15:
|
if cdf >= 1.0 - 1e-15:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue