FA-74501 / Experiment statistics / Open access
Per-arm sample size for conversion tests: Relative effects are treated as absolute · case 01
A 10 percent relative lift on a 5 percent baseline is planned as a jump to 15 percent.
ROOT CAUSE
p2 always adds mde to the baseline.
VERIFIED REPAIR
Multiply the baseline by (1 + mde) for relative effects.
Unsuccessful approach: Multiplying the baseline by mde alone gives the size of the change, not the new rate.
Case contract
p2 = p_base (1 + mde) when relative else p_base + mde; both rates must lie in (0, 1) and differ (else None). n per arm = ceil((z_{1-alpha/2} + z_power)^2 (p1(1-p1) + p2(1-p2)) / (p2 - p1)^2) using statistics.NormalDist quantiles.
Why this case matters
Underpowered experiments waste traffic; overestimates delay launches.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
import statistics
N = 1
observations = []
def solve(p_base, mde, alpha, power, relative):
nd = statistics.NormalDist()
p1 = p_base
p2 = p_base + mde
if not (0 < p1 < 1 and 0 < p2 < 1) or p1 == p2:
return None
za = nd.inv_cdf(1 - alpha / 2)
zb = nd.inv_cdf(power)
n = (za + zb) ** 2 * (p1 * (1 - p1) + p2 * (1 - p2)) / (p2 - p1) ** 2
return math.ceil(n)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('planning sample 1', [0.02, 0.1, 0.01, 0.8, False], 147),
('planning sample 2', [0.2, 0.2, 0.05, 0.9, True], 2249),
('planning sample 3', [0.05, 0.01, 0.1, 0.9, True], 3269669)],
[('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 6', [0.2, 0.1, 0.01, 0.9, True], 12336),
('planning sample 7', [0.5, 0.05, 0.1, 0.9, False], 1705),
('planning sample 14', [0.02, 0.2, 0.1, 0.8, True], 16625)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 11', [0.02, 0.05, 0.1, 0.9, False], 291),
('planning sample 12', [0.5, 0.2, 0.01, 0.8, True], 573),
('planning sample 25', [0.1, 0.2, 0.01, 0.9, True], 7277)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 16', [0.2, 0.05, 0.1, 0.8, True], 20149),
('planning sample 17', [0.1, 0.02, 0.1, 0.8, False], 3024),
('planning sample 48', [0.02, 0.05, 0.05, 0.8, True], 315204)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 3', [0.05, 0.01, 0.1, 0.9, True], 3269669),
('planning sample 21', [0.02, 0.1, 0.01, 0.9, False], 187),
('planning sample 22', [0.2, 0.05, 0.1, 0.9, True], 27910)]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| ten percent relative lift on five percent | 138 | 31231 | Failed |
| one point absolute lift | 14749 | 14749 | Passed |
| higher power needs more users | 1461 | 34244 | Failed |
| stricter alpha | 14588 | 14588 | Passed |
| lift beyond one is invalid | None | None | Passed |
| planning sample 1 | 147 | 147 | Passed |
| planning sample 2 | 106 | 2249 | Failed |
| planning sample 3 | 8898 | 3269669 | Failed |
SHA-256 / 038ec9d8bba701a2eabadd6a45732162cb3b09f604815c9de8e27a2ad26e3893
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
import statistics
N = 1
observations = []
def solve(p_base, mde, alpha, power, relative):
nd = statistics.NormalDist()
p1 = p_base
p2 = p_base * mde if relative else p_base + mde
if not (0 < p1 < 1 and 0 < p2 < 1) or p1 == p2:
return None
za = nd.inv_cdf(1 - alpha / 2)
zb = nd.inv_cdf(power)
n = (za + zb) ** 2 * (p1 * (1 - p1) + p2 * (1 - p2)) / (p2 - p1) ** 2
return math.ceil(n)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('planning sample 1', [0.02, 0.1, 0.01, 0.8, False], 147),
('planning sample 2', [0.2, 0.2, 0.05, 0.9, True], 2249),
('planning sample 3', [0.05, 0.01, 0.1, 0.9, True], 3269669)],
[('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 6', [0.2, 0.1, 0.01, 0.9, True], 12336),
('planning sample 7', [0.5, 0.05, 0.1, 0.9, False], 1705),
('planning sample 14', [0.02, 0.2, 0.1, 0.8, True], 16625)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 11', [0.02, 0.05, 0.1, 0.9, False], 291),
('planning sample 12', [0.5, 0.2, 0.01, 0.8, True], 573),
('planning sample 25', [0.1, 0.2, 0.01, 0.9, True], 7277)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 16', [0.2, 0.05, 0.1, 0.8, True], 20149),
('planning sample 17', [0.1, 0.02, 0.1, 0.8, False], 3024),
('planning sample 48', [0.02, 0.05, 0.05, 0.8, True], 315204)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 3', [0.05, 0.01, 0.1, 0.9, True], 3269669),
('planning sample 21', [0.02, 0.1, 0.01, 0.9, False], 187),
('planning sample 22', [0.2, 0.05, 0.1, 0.9, True], 27910)]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| ten percent relative lift on five percent | 204 | 31231 | Failed |
| one point absolute lift | 14749 | 14749 | Passed |
| higher power needs more users | 50 | 34244 | Failed |
| stricter alpha | 14588 | 14588 | Passed |
| lift beyond one is invalid | None | None | Passed |
| planning sample 1 | 147 | 147 | Passed |
| planning sample 2 | 82 | 2249 | Failed |
| planning sample 3 | 168 | 3269669 | Failed |
SHA-256 / 3568d2858ccc378cb2071a3e060f960af03935de47bb7b3399bf7e5e36f415ee
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
import statistics
N = 1
observations = []
def solve(p_base, mde, alpha, power, relative):
nd = statistics.NormalDist()
p1 = p_base
p2 = p_base * (1 + mde) if relative else p_base + mde
if not (0 < p1 < 1 and 0 < p2 < 1) or p1 == p2:
return None
za = nd.inv_cdf(1 - alpha / 2)
zb = nd.inv_cdf(power)
n = (za + zb) ** 2 * (p1 * (1 - p1) + p2 * (1 - p2)) / (p2 - p1) ** 2
return math.ceil(n)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('planning sample 1', [0.02, 0.1, 0.01, 0.8, False], 147),
('planning sample 2', [0.2, 0.2, 0.05, 0.9, True], 2249),
('planning sample 3', [0.05, 0.01, 0.1, 0.9, True], 3269669)],
[('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 6', [0.2, 0.1, 0.01, 0.9, True], 12336),
('planning sample 7', [0.5, 0.05, 0.1, 0.9, False], 1705),
('planning sample 14', [0.02, 0.2, 0.1, 0.8, True], 16625)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 11', [0.02, 0.05, 0.1, 0.9, False], 291),
('planning sample 12', [0.5, 0.2, 0.01, 0.8, True], 573),
('planning sample 25', [0.1, 0.2, 0.01, 0.9, True], 7277)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('stricter alpha', [0.5, 0.02, 0.01, 0.8, False], 14588),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 16', [0.2, 0.05, 0.1, 0.8, True], 20149),
('planning sample 17', [0.1, 0.02, 0.1, 0.8, False], 3024),
('planning sample 48', [0.02, 0.05, 0.05, 0.8, True], 315204)],
[('ten percent relative lift on five percent', [0.05, 0.1, 0.05, 0.8, True], 31231),
('one point absolute lift', [0.1, 0.01, 0.05, 0.8, False], 14749),
('higher power needs more users', [0.2, 0.05, 0.05, 0.9, True], 34244),
('lift beyond one is invalid', [0.5, 1.0, 0.05, 0.8, True], None),
('zero effect is invalid', [0.1, 0.0, 0.05, 0.8, False], None),
('planning sample 3', [0.05, 0.01, 0.1, 0.9, True], 3269669),
('planning sample 21', [0.02, 0.1, 0.01, 0.9, False], 187),
('planning sample 22', [0.2, 0.05, 0.1, 0.9, True], 27910)]]
for label, args, expected in fixtures[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| ten percent relative lift on five percent | 31231 | 31231 | Passed |
| one point absolute lift | 14749 | 14749 | Passed |
| higher power needs more users | 34244 | 34244 | Passed |
| stricter alpha | 14588 | 14588 | Passed |
| lift beyond one is invalid | None | None | Passed |
| planning sample 1 | 147 | 147 | Passed |
| planning sample 2 | 2249 | 2249 | Passed |
| planning sample 3 | 3269669 | 3269669 | Passed |
SHA-256 / 18be2ec2bcfc0daf1588cc8b9f05884266dcadeb68462ab4bc40a08f8bb9bd00
Verification & scope
A deterministic toy experiment-analysis model with a stipulated contract; results are rounded and are not a substitute for a validated statistics package. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:48:57.332264+00:00.
Case digest / 3b3a408051f3656057263ae2de0ba9f9b423294f16201b7b3932dc4576c93301