FA-63826 / Actuarial life tables / Open access
Median and quantile future lifetime: Target is the fraction dead instead of surviving · case 01
Non-median quantiles are reversed.
ROOT CAUSE
The target uses 1-p.
VERIFIED REPAIR
Target l_0 p.
Unsuccessful approach: Reading p as a percentage makes the target exceed l_0.
Case contract
Input l (from age x) and p (per mille surviving, 0<p<1000 else 'invalid'; also l_0<=0). Find the first t with l_t <= l_0 p/1000 and interpolate linearly: t-1 + (l_{t-1}-target)/(l_{t-1}-l_t). No crossing -> 'beyond table'. Rounded to 6.
Why this case matters
Life-table arithmetic compounds across ages, so one misplaced index or assumption shifts every downstream value.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
l=x['l']
if l[0]<=0 or not 0<x['p']<1000: return 'invalid'
target=Fraction(l[0]*(1000-x['p']),1000)
for t in range(1,len(l)):
if l[t]<=target:
frac=(l[t-1]-target)/(l[t-1]-l[t])
return round(float(t-1+frac),6)
return 'beyond table'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['median', {'l': [100000, 99387, 98676, 97881, 96960, 95885, 94662, 93295, 91792, 90121, 88233, 0], 'p': 500}, 10.433319], ['upper quartile survival', {'l': [100000, 99475, 98885, 98208, 97456, 96585, 95576, 94404, 93122, 91705, 90114, 0], 'p': 750}, 10.167721], ['crossing in first year', {'l': [1000, 301, 100, 0], 'p': 500}, 0.715308], ['crossing at last age', {'l': [1000, 900, 800, 399], 'p': 500}, 2.74813], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99796, 99551, 99257, 98907, 98508, 98032, 97478, 96832, 96111, 95312, 0], 'p': 101}, 10.894032]], [['median', {'l': [100000, 99728, 99400, 99011, 98531, 97972, 97317, 96577, 95722, 94733, 93633, 0], 'p': 500}, 10.466], ['upper quartile survival', {'l': [100000, 99497, 98924, 98231, 97459, 96611, 95636, 94522, 93247, 91843, 90240, 0], 'p': 750}, 10.168883], ['crossing in first year', {'l': [1000, 302, 100, 0], 'p': 500}, 0.716332], ['crossing at last age', {'l': [1000, 900, 800, 398], 'p': 500}, 2.746269], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99549, 99016, 98408, 97709, 96926, 96052, 95042, 93885, 92604, 91159, 0], 'p': 102}, 10.888108]], [['median', {'l': [100000, 99519, 98953, 98284, 97516, 96640, 95632, 94490, 93212, 91778, 90214, 0], 'p': 500}, 10.445762], ['upper quartile survival', {'l': [100000, 99696, 99292, 98815, 98275, 97687, 97001, 96211, 95276, 94196, 92976, 0], 'p': 750}, 10.19334], ['crossing in first year', {'l': [1000, 303, 100, 0], 'p': 500}, 0.71736], ['crossing at last age', {'l': [1000, 900, 800, 397], 'p': 500}, 2.744417], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99674, 99256, 98791, 98244, 97618, 96907, 96078, 95119, 94025, 92792, 0], 'p': 103}, 10.888999]], [['median', {'l': [100000, 99629, 99193, 98668, 98069, 97392, 96584, 95658, 94604, 93390, 92018, 0], 'p': 500}, 10.456628], ['upper quartile survival', {'l': [100000, 99416, 98748, 97985, 97109, 96096, 94934, 93641, 92205, 90578, 88748, 0], 'p': 750}, 10.154911], ['crossing in first year', {'l': [1000, 304, 100, 0], 'p': 500}, 0.718391], ['crossing at last age', {'l': [1000, 900, 800, 396], 'p': 500}, 2.742574], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99428, 98766, 98052, 97212, 96267, 95157, 93939, 92567, 91036, 89325, 0], 'p': 104}, 10.883571]], [['median', {'l': [100000, 99481, 98876, 98212, 97434, 96547, 95571, 94472, 93234, 91812, 90252, 0], 'p': 500}, 10.445996], ['upper quartile survival', {'l': [100000, 99429, 98814, 98062, 97236, 96330, 95319, 94161, 92874, 91437, 89822, 0], 'p': 750}, 10.165015], ['crossing in first year', {'l': [1000, 305, 100, 0], 'p': 500}, 0.719424], ['crossing at last age', {'l': [1000, 900, 800, 395], 'p': 500}, 2.740741], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99395, 98716, 97924, 97019, 95986, 94803, 93476, 91996, 90309, 88450, 0], 'p': 105}, 10.881289]]]
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 |
|---|---|---|---|
| median | 10.433319 | 10.433319 | Passed |
| upper quartile survival | 10.722574 | 10.167721 | Failed |
| crossing in first year | 0.715308 | 0.715308 | Passed |
| crossing at last age | 2.74813 | 2.74813 | Passed |
| never crosses | beyond table | beyond table | Passed |
| p zero rejected | invalid | invalid | Passed |
| p full rejected | invalid | invalid | Passed |
| low quantile | 10.056782 | 10.894032 | Failed |
SHA-256 / 0f258e79b6fd2945346a2dbe3ee932e4cb73b0a169e2d491f952dd9d651fd105
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
l=x['l']
if l[0]<=0 or not 0<x['p']<1000: return 'invalid'
target=Fraction(l[0]*x['p'],100)
for t in range(1,len(l)):
if l[t]<=target:
frac=(l[t-1]-target)/(l[t-1]-l[t])
return round(float(t-1+frac),6)
return 'beyond table'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['median', {'l': [100000, 99387, 98676, 97881, 96960, 95885, 94662, 93295, 91792, 90121, 88233, 0], 'p': 500}, 10.433319], ['upper quartile survival', {'l': [100000, 99475, 98885, 98208, 97456, 96585, 95576, 94404, 93122, 91705, 90114, 0], 'p': 750}, 10.167721], ['crossing in first year', {'l': [1000, 301, 100, 0], 'p': 500}, 0.715308], ['crossing at last age', {'l': [1000, 900, 800, 399], 'p': 500}, 2.74813], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99796, 99551, 99257, 98907, 98508, 98032, 97478, 96832, 96111, 95312, 0], 'p': 101}, 10.894032]], [['median', {'l': [100000, 99728, 99400, 99011, 98531, 97972, 97317, 96577, 95722, 94733, 93633, 0], 'p': 500}, 10.466], ['upper quartile survival', {'l': [100000, 99497, 98924, 98231, 97459, 96611, 95636, 94522, 93247, 91843, 90240, 0], 'p': 750}, 10.168883], ['crossing in first year', {'l': [1000, 302, 100, 0], 'p': 500}, 0.716332], ['crossing at last age', {'l': [1000, 900, 800, 398], 'p': 500}, 2.746269], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99549, 99016, 98408, 97709, 96926, 96052, 95042, 93885, 92604, 91159, 0], 'p': 102}, 10.888108]], [['median', {'l': [100000, 99519, 98953, 98284, 97516, 96640, 95632, 94490, 93212, 91778, 90214, 0], 'p': 500}, 10.445762], ['upper quartile survival', {'l': [100000, 99696, 99292, 98815, 98275, 97687, 97001, 96211, 95276, 94196, 92976, 0], 'p': 750}, 10.19334], ['crossing in first year', {'l': [1000, 303, 100, 0], 'p': 500}, 0.71736], ['crossing at last age', {'l': [1000, 900, 800, 397], 'p': 500}, 2.744417], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99674, 99256, 98791, 98244, 97618, 96907, 96078, 95119, 94025, 92792, 0], 'p': 103}, 10.888999]], [['median', {'l': [100000, 99629, 99193, 98668, 98069, 97392, 96584, 95658, 94604, 93390, 92018, 0], 'p': 500}, 10.456628], ['upper quartile survival', {'l': [100000, 99416, 98748, 97985, 97109, 96096, 94934, 93641, 92205, 90578, 88748, 0], 'p': 750}, 10.154911], ['crossing in first year', {'l': [1000, 304, 100, 0], 'p': 500}, 0.718391], ['crossing at last age', {'l': [1000, 900, 800, 396], 'p': 500}, 2.742574], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99428, 98766, 98052, 97212, 96267, 95157, 93939, 92567, 91036, 89325, 0], 'p': 104}, 10.883571]], [['median', {'l': [100000, 99481, 98876, 98212, 97434, 96547, 95571, 94472, 93234, 91812, 90252, 0], 'p': 500}, 10.445996], ['upper quartile survival', {'l': [100000, 99429, 98814, 98062, 97236, 96330, 95319, 94161, 92874, 91437, 89822, 0], 'p': 750}, 10.165015], ['crossing in first year', {'l': [1000, 305, 100, 0], 'p': 500}, 0.719424], ['crossing at last age', {'l': [1000, 900, 800, 395], 'p': 500}, 2.740741], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99395, 98716, 97924, 97019, 95986, 94803, 93476, 91996, 90309, 88450, 0], 'p': 105}, 10.881289]]]
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 |
|---|---|---|---|
| median | -652.528548 | 10.433319 | Failed |
| upper quartile survival | -1238.095238 | 10.167721 | Failed |
| crossing in first year | -5.722461 | 0.715308 | Failed |
| crossing at last age | -40.0 | 2.74813 | Failed |
| never crosses | -80.0 | beyond table | Failed |
| p zero rejected | invalid | invalid | Passed |
| p full rejected | invalid | invalid | Passed |
| low quantile | -4.901961 | 10.894032 | Failed |
SHA-256 / 1cb2ab66be91207c41978be8985c602f2d787cbec4f97494bd268996988cb4d2
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(x):
l=x['l']
if l[0]<=0 or not 0<x['p']<1000: return 'invalid'
target=Fraction(l[0]*x['p'],1000)
for t in range(1,len(l)):
if l[t]<=target:
frac=(l[t-1]-target)/(l[t-1]-l[t])
return round(float(t-1+frac),6)
return 'beyond table'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['median', {'l': [100000, 99387, 98676, 97881, 96960, 95885, 94662, 93295, 91792, 90121, 88233, 0], 'p': 500}, 10.433319], ['upper quartile survival', {'l': [100000, 99475, 98885, 98208, 97456, 96585, 95576, 94404, 93122, 91705, 90114, 0], 'p': 750}, 10.167721], ['crossing in first year', {'l': [1000, 301, 100, 0], 'p': 500}, 0.715308], ['crossing at last age', {'l': [1000, 900, 800, 399], 'p': 500}, 2.74813], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99796, 99551, 99257, 98907, 98508, 98032, 97478, 96832, 96111, 95312, 0], 'p': 101}, 10.894032]], [['median', {'l': [100000, 99728, 99400, 99011, 98531, 97972, 97317, 96577, 95722, 94733, 93633, 0], 'p': 500}, 10.466], ['upper quartile survival', {'l': [100000, 99497, 98924, 98231, 97459, 96611, 95636, 94522, 93247, 91843, 90240, 0], 'p': 750}, 10.168883], ['crossing in first year', {'l': [1000, 302, 100, 0], 'p': 500}, 0.716332], ['crossing at last age', {'l': [1000, 900, 800, 398], 'p': 500}, 2.746269], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99549, 99016, 98408, 97709, 96926, 96052, 95042, 93885, 92604, 91159, 0], 'p': 102}, 10.888108]], [['median', {'l': [100000, 99519, 98953, 98284, 97516, 96640, 95632, 94490, 93212, 91778, 90214, 0], 'p': 500}, 10.445762], ['upper quartile survival', {'l': [100000, 99696, 99292, 98815, 98275, 97687, 97001, 96211, 95276, 94196, 92976, 0], 'p': 750}, 10.19334], ['crossing in first year', {'l': [1000, 303, 100, 0], 'p': 500}, 0.71736], ['crossing at last age', {'l': [1000, 900, 800, 397], 'p': 500}, 2.744417], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99674, 99256, 98791, 98244, 97618, 96907, 96078, 95119, 94025, 92792, 0], 'p': 103}, 10.888999]], [['median', {'l': [100000, 99629, 99193, 98668, 98069, 97392, 96584, 95658, 94604, 93390, 92018, 0], 'p': 500}, 10.456628], ['upper quartile survival', {'l': [100000, 99416, 98748, 97985, 97109, 96096, 94934, 93641, 92205, 90578, 88748, 0], 'p': 750}, 10.154911], ['crossing in first year', {'l': [1000, 304, 100, 0], 'p': 500}, 0.718391], ['crossing at last age', {'l': [1000, 900, 800, 396], 'p': 500}, 2.742574], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99428, 98766, 98052, 97212, 96267, 95157, 93939, 92567, 91036, 89325, 0], 'p': 104}, 10.883571]], [['median', {'l': [100000, 99481, 98876, 98212, 97434, 96547, 95571, 94472, 93234, 91812, 90252, 0], 'p': 500}, 10.445996], ['upper quartile survival', {'l': [100000, 99429, 98814, 98062, 97236, 96330, 95319, 94161, 92874, 91437, 89822, 0], 'p': 750}, 10.165015], ['crossing in first year', {'l': [1000, 305, 100, 0], 'p': 500}, 0.719424], ['crossing at last age', {'l': [1000, 900, 800, 395], 'p': 500}, 2.740741], ['never crosses', {'l': [1000, 950, 900], 'p': 500}, 'beyond table'], ['p zero rejected', {'l': [1000, 500, 0], 'p': 0}, 'invalid'], ['p full rejected', {'l': [1000, 500, 0], 'p': 1000}, 'invalid'], ['low quantile', {'l': [100000, 99395, 98716, 97924, 97019, 95986, 94803, 93476, 91996, 90309, 88450, 0], 'p': 105}, 10.881289]]]
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 |
|---|---|---|---|
| median | 10.433319 | 10.433319 | Passed |
| upper quartile survival | 10.167721 | 10.167721 | Passed |
| crossing in first year | 0.715308 | 0.715308 | Passed |
| crossing at last age | 2.74813 | 2.74813 | Passed |
| never crosses | beyond table | beyond table | Passed |
| p zero rejected | invalid | invalid | Passed |
| p full rejected | invalid | invalid | Passed |
| low quantile | 10.894032 | 10.894032 | Passed |
SHA-256 / 377193cc6eecf2ccd6b495867c15c1f84d624f85f459076e7a2696f315e428c1
Verification & scope
A deterministic bounded teaching model of a stipulated toy actuarial contract. Rates are small synthetic tables, exact rationals are used where practical and floats are rounded at the output; it makes no claim of conformance with any published table, standard of practice or regulation. 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:47:18.277262+00:00.
Case digest / e652540cb2af6f39d20927a8cc099642c305becc65d28078c440a307d5dcf451