FA-63341 / Actuarial life tables / Open access
Life table survivors from mortality rates: Survivors are rounded from the cumulative product instead of step by step · case 01
Published l_x values drift from the stepwise-rounded table.
ROOT CAUSE
Survivors are computed from the exact product of survival rates instead of the previous rounded l.
VERIFIED REPAIR
Round each l_{k+1} from the previous rounded l_k.
Unsuccessful approach: Truncating each step introduces a downward bias.
Case contract
Input q (per 100000, last must be 100000 else 'open table'; any outside [0,100000] -> 'invalid q') and radix (<=0 -> 'invalid radix'). l_0 = radix; l_{k+1} = l_k*(1-q_k) rounded half-up at every age; d_k = l_k - l_{k+1}. Return [l, d].
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):
qs=[Fraction(q,100000) for q in x['q']]
if x['radix']<=0: return 'invalid radix'
if any(q<0 or q>1 for q in qs): return 'invalid q'
if not qs or qs[-1]!=1: return 'open table'
l=[x['radix']]
for q in qs:
l.append(math.floor(x['radix']*math.prod(1-v for v in qs[:len(l)])+Fraction(1,2)))
d=[a-b for a,b in zip(l,l[1:])]
return [l,d]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['closed table 0', {'q': [1033, 1165, 1336, 1514, 1709, 100000], 'radix': 69457}, [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]]], ['closed table 1', {'q': [593, 714, 824, 930, 1064, 1208, 1363, 100000], 'radix': 81539}, [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]]], ['closed table 2', {'q': [515, 615, 705, 814, 949, 1107, 1282, 100000], 'radix': 23619}, [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]]], ['zero mortality ages', {'q': [0, 0, 796, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]]], ['open table rejected', {'q': [555, 636, 741, 860, 978], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [223, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 3}, [[3, 2, 0], [1, 2]]], ['odd radix deaths reconcile', {'q': [15657, 22915, 37291, 19541, 14273, 100000], 'radix': 361}, [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 8}, [[8, 5, 3, 2, 0], [3, 2, 1, 2]]]], [['closed table 0', {'q': [1224, 1411, 1580, 1785, 2044, 2304, 2625, 2976, 3345, 100000], 'radix': 73425}, [[73425, 72526, 71503, 70373, 69117, 67704, 66144, 64408, 62491, 60401, 0], [899, 1023, 1130, 1256, 1413, 1560, 1736, 1917, 2090, 60401]]], ['closed table 1', {'q': [1483, 1675, 1889, 2148, 2445, 2784, 3127, 100000], 'radix': 95565}, [[95565, 94148, 92571, 90822, 88871, 86698, 84284, 81648, 0], [1417, 1577, 1749, 1951, 2173, 2414, 2636, 81648]]], ['closed table 2', {'q': [947, 1104, 1253, 1408, 1590, 1811, 2059, 2344, 2661, 100000], 'radix': 84753}, [[84753, 83950, 83023, 81983, 80829, 79544, 78103, 76495, 74702, 72714, 0], [803, 927, 1040, 1154, 1285, 1441, 1608, 1793, 1988, 72714]]], ['zero mortality ages', {'q': [0, 0, 577, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99423, 0], [0, 0, 577, 99423]]], ['open table rejected', {'q': [401, 494, 603, 675, 758], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [861, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 5}, [[5, 3, 0], [2, 3]]], ['odd radix deaths reconcile', {'q': [34607, 36625, 31203, 24229, 24851, 100000], 'radix': 951}, [[951, 622, 394, 271, 205, 154, 0], [329, 228, 123, 66, 51, 154]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 9}, [[9, 6, 4, 2, 0], [3, 2, 2, 2]]]], [['closed table 0', {'q': [843, 986, 1140, 1296, 1486, 1699, 1937, 2195, 100000], 'radix': 30585}, [[30585, 30327, 30028, 29686, 29301, 28866, 28376, 27826, 27215, 0], [258, 299, 342, 385, 435, 490, 550, 611, 27215]]], ['closed table 1', {'q': [1278, 1465, 1677, 1884, 2120, 100000], 'radix': 34567}, [[34567, 34125, 33625, 33061, 32438, 31750, 0], [442, 500, 564, 623, 688, 31750]]], ['closed table 2', {'q': [699, 786, 882, 1036, 1165, 1332, 1497, 1680, 100000], 'radix': 78197}, [[78197, 77650, 77040, 76361, 75570, 74690, 73695, 72592, 71372, 0], [547, 610, 679, 791, 880, 995, 1103, 1220, 71372]]], ['zero mortality ages', {'q': [0, 0, 651, 100000], 'radix': 10000}, [[10000, 10000, 10000, 9935, 0], [0, 0, 65, 9935]]], ['open table rejected', {'q': [293, 338, 418, 503, 565], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [246, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 7}, [[7, 4, 0], [3, 4]]], ['odd radix deaths reconcile', {'q': [24577, 26111, 12881, 35469, 14789, 100000], 'radix': 563}, [[563, 425, 314, 274, 177, 151, 0], [138, 111, 40, 97, 26, 151]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 10}, [[10, 7, 5, 3, 0], [3, 2, 2, 3]]]], [['closed table 0', {'q': [1676, 1888, 2164, 2470, 2790, 3140, 3538, 3970, 100000], 'radix': 15185}, [[15185, 14930, 14648, 14331, 13977, 13587, 13160, 12694, 12190, 0], [255, 282, 317, 354, 390, 427, 466, 504, 12190]]], ['closed table 1', {'q': [2017, 2262, 2573, 2928, 3329, 3764, 4215, 4722, 5308, 100000], 'radix': 18031}, [[18031, 17667, 17267, 16823, 16330, 15786, 15192, 14552, 13865, 13129, 0], [364, 400, 444, 493, 544, 594, 640, 687, 736, 13129]]], ['closed table 2', {'q': [1538, 1732, 1939, 2177, 2450, 2788, 3129, 3535, 100000], 'radix': 93691}, [[93691, 92250, 90652, 88894, 86959, 84829, 82464, 79884, 77060, 0], [1441, 1598, 1758, 1935, 2130, 2365, 2580, 2824, 77060]]], ['zero mortality ages', {'q': [0, 0, 490, 100000], 'radix': 1000}, [[1000, 1000, 1000, 995, 0], [0, 0, 5, 995]]], ['open table rejected', {'q': [226, 259, 292, 356, 432], 'radix': 100000}, 'open table'], ['early certain death then open', {'q': [598, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 10000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 9}, [[9, 5, 0], [4, 5]]], ['odd radix deaths reconcile', {'q': [33513, 18981, 38831, 14099, 33629, 100000], 'radix': 617}, [[617, 410, 332, 203, 174, 115, 0], [207, 78, 129, 29, 59, 115]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 11}, [[11, 7, 5, 3, 0], [4, 2, 2, 3]]]], [['closed table 0', {'q': [992, 1146, 1306, 1489, 1674, 100000], 'radix': 62363}, [[62363, 61744, 61036, 60239, 59342, 58349, 0], [619, 708, 797, 897, 993, 58349]]], ['closed table 1', {'q': [2141, 2429, 2766, 3097, 3484, 3938, 4441, 4974, 5590, 100000], 'radix': 50071}, [[50071, 48999, 47809, 46487, 45047, 43478, 41766, 39911, 37926, 35806, 0], [1072, 1190, 1322, 1440, 1569, 1712, 1855, 1985, 2120, 35806]]], ['closed table 2', {'q': [1626, 1851, 2115, 2406, 2724, 3092, 3463, 3919, 100000], 'radix': 12425}, [[12425, 12223, 11997, 11743, 11460, 11148, 10803, 10429, 10020, 0], [202, 226, 254, 283, 312, 345, 374, 409, 10020]]], ['zero mortality ages', {'q': [0, 0, 245, 100000], 'radix': 1000}, [[1000, 1000, 1000, 998, 0], [0, 0, 2, 998]]], ['open table rejected', {'q': [345, 413, 466, 555, 658], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [601, 100000, 50000], 'radix': 100000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 11}, [[11, 6, 0], [5, 6]]], ['odd radix deaths reconcile', {'q': [23365, 16837, 21555, 13823, 21221, 100000], 'radix': 421}, [[421, 323, 269, 211, 182, 143, 0], [98, 54, 58, 29, 39, 143]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 12}, [[12, 8, 5, 3, 0], [4, 3, 2, 3]]]]]
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 |
|---|---|---|---|
| closed table 0 | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | Passed |
| closed table 1 | [[81539, 81055, 80477, 79814, 79071, 78230, 77285, 76232, 0], [484, 578, 663, 743, 841, 945, 1053, 76232]] | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | Failed |
| closed table 2 | [[23619, 23497, 23353, 23188, 22999, 22781, 22529, 22240, 0], [122, 144, 165, 189, 218, 252, 289, 22240]] | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | Failed |
| zero mortality ages | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | Passed |
| open table rejected | open table | open table | Passed |
| early certain death then open | open table | open table | Passed |
| negative rate rejected | invalid q | invalid q | Passed |
| zero radix rejected | invalid radix | invalid radix | Passed |
| negative radix rejected | invalid radix | invalid radix | Passed |
| half-death rounding tie | [[3, 2, 0], [1, 2]] | [[3, 2, 0], [1, 2]] | Passed |
| odd radix deaths reconcile | [[361, 304, 235, 147, 118, 102, 0], [57, 69, 88, 29, 16, 102]] | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | Failed |
| small radix rounding | [[8, 5, 4, 2, 0], [3, 1, 2, 2]] | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | Failed |
SHA-256 / 473afc18f88bfbc92ce9ab9049668eae32aee4138d2a254d263ddb57935523be
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):
qs=[Fraction(q,100000) for q in x['q']]
if x['radix']<=0: return 'invalid radix'
if any(q<0 or q>1 for q in qs): return 'invalid q'
if not qs or qs[-1]!=1: return 'open table'
l=[x['radix']]
for q in qs:
l.append(int(l[-1]*(1-q)))
d=[a-b for a,b in zip(l,l[1:])]
return [l,d]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['closed table 0', {'q': [1033, 1165, 1336, 1514, 1709, 100000], 'radix': 69457}, [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]]], ['closed table 1', {'q': [593, 714, 824, 930, 1064, 1208, 1363, 100000], 'radix': 81539}, [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]]], ['closed table 2', {'q': [515, 615, 705, 814, 949, 1107, 1282, 100000], 'radix': 23619}, [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]]], ['zero mortality ages', {'q': [0, 0, 796, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]]], ['open table rejected', {'q': [555, 636, 741, 860, 978], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [223, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 3}, [[3, 2, 0], [1, 2]]], ['odd radix deaths reconcile', {'q': [15657, 22915, 37291, 19541, 14273, 100000], 'radix': 361}, [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 8}, [[8, 5, 3, 2, 0], [3, 2, 1, 2]]]], [['closed table 0', {'q': [1224, 1411, 1580, 1785, 2044, 2304, 2625, 2976, 3345, 100000], 'radix': 73425}, [[73425, 72526, 71503, 70373, 69117, 67704, 66144, 64408, 62491, 60401, 0], [899, 1023, 1130, 1256, 1413, 1560, 1736, 1917, 2090, 60401]]], ['closed table 1', {'q': [1483, 1675, 1889, 2148, 2445, 2784, 3127, 100000], 'radix': 95565}, [[95565, 94148, 92571, 90822, 88871, 86698, 84284, 81648, 0], [1417, 1577, 1749, 1951, 2173, 2414, 2636, 81648]]], ['closed table 2', {'q': [947, 1104, 1253, 1408, 1590, 1811, 2059, 2344, 2661, 100000], 'radix': 84753}, [[84753, 83950, 83023, 81983, 80829, 79544, 78103, 76495, 74702, 72714, 0], [803, 927, 1040, 1154, 1285, 1441, 1608, 1793, 1988, 72714]]], ['zero mortality ages', {'q': [0, 0, 577, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99423, 0], [0, 0, 577, 99423]]], ['open table rejected', {'q': [401, 494, 603, 675, 758], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [861, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 5}, [[5, 3, 0], [2, 3]]], ['odd radix deaths reconcile', {'q': [34607, 36625, 31203, 24229, 24851, 100000], 'radix': 951}, [[951, 622, 394, 271, 205, 154, 0], [329, 228, 123, 66, 51, 154]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 9}, [[9, 6, 4, 2, 0], [3, 2, 2, 2]]]], [['closed table 0', {'q': [843, 986, 1140, 1296, 1486, 1699, 1937, 2195, 100000], 'radix': 30585}, [[30585, 30327, 30028, 29686, 29301, 28866, 28376, 27826, 27215, 0], [258, 299, 342, 385, 435, 490, 550, 611, 27215]]], ['closed table 1', {'q': [1278, 1465, 1677, 1884, 2120, 100000], 'radix': 34567}, [[34567, 34125, 33625, 33061, 32438, 31750, 0], [442, 500, 564, 623, 688, 31750]]], ['closed table 2', {'q': [699, 786, 882, 1036, 1165, 1332, 1497, 1680, 100000], 'radix': 78197}, [[78197, 77650, 77040, 76361, 75570, 74690, 73695, 72592, 71372, 0], [547, 610, 679, 791, 880, 995, 1103, 1220, 71372]]], ['zero mortality ages', {'q': [0, 0, 651, 100000], 'radix': 10000}, [[10000, 10000, 10000, 9935, 0], [0, 0, 65, 9935]]], ['open table rejected', {'q': [293, 338, 418, 503, 565], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [246, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 7}, [[7, 4, 0], [3, 4]]], ['odd radix deaths reconcile', {'q': [24577, 26111, 12881, 35469, 14789, 100000], 'radix': 563}, [[563, 425, 314, 274, 177, 151, 0], [138, 111, 40, 97, 26, 151]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 10}, [[10, 7, 5, 3, 0], [3, 2, 2, 3]]]], [['closed table 0', {'q': [1676, 1888, 2164, 2470, 2790, 3140, 3538, 3970, 100000], 'radix': 15185}, [[15185, 14930, 14648, 14331, 13977, 13587, 13160, 12694, 12190, 0], [255, 282, 317, 354, 390, 427, 466, 504, 12190]]], ['closed table 1', {'q': [2017, 2262, 2573, 2928, 3329, 3764, 4215, 4722, 5308, 100000], 'radix': 18031}, [[18031, 17667, 17267, 16823, 16330, 15786, 15192, 14552, 13865, 13129, 0], [364, 400, 444, 493, 544, 594, 640, 687, 736, 13129]]], ['closed table 2', {'q': [1538, 1732, 1939, 2177, 2450, 2788, 3129, 3535, 100000], 'radix': 93691}, [[93691, 92250, 90652, 88894, 86959, 84829, 82464, 79884, 77060, 0], [1441, 1598, 1758, 1935, 2130, 2365, 2580, 2824, 77060]]], ['zero mortality ages', {'q': [0, 0, 490, 100000], 'radix': 1000}, [[1000, 1000, 1000, 995, 0], [0, 0, 5, 995]]], ['open table rejected', {'q': [226, 259, 292, 356, 432], 'radix': 100000}, 'open table'], ['early certain death then open', {'q': [598, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 10000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 9}, [[9, 5, 0], [4, 5]]], ['odd radix deaths reconcile', {'q': [33513, 18981, 38831, 14099, 33629, 100000], 'radix': 617}, [[617, 410, 332, 203, 174, 115, 0], [207, 78, 129, 29, 59, 115]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 11}, [[11, 7, 5, 3, 0], [4, 2, 2, 3]]]], [['closed table 0', {'q': [992, 1146, 1306, 1489, 1674, 100000], 'radix': 62363}, [[62363, 61744, 61036, 60239, 59342, 58349, 0], [619, 708, 797, 897, 993, 58349]]], ['closed table 1', {'q': [2141, 2429, 2766, 3097, 3484, 3938, 4441, 4974, 5590, 100000], 'radix': 50071}, [[50071, 48999, 47809, 46487, 45047, 43478, 41766, 39911, 37926, 35806, 0], [1072, 1190, 1322, 1440, 1569, 1712, 1855, 1985, 2120, 35806]]], ['closed table 2', {'q': [1626, 1851, 2115, 2406, 2724, 3092, 3463, 3919, 100000], 'radix': 12425}, [[12425, 12223, 11997, 11743, 11460, 11148, 10803, 10429, 10020, 0], [202, 226, 254, 283, 312, 345, 374, 409, 10020]]], ['zero mortality ages', {'q': [0, 0, 245, 100000], 'radix': 1000}, [[1000, 1000, 1000, 998, 0], [0, 0, 2, 998]]], ['open table rejected', {'q': [345, 413, 466, 555, 658], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [601, 100000, 50000], 'radix': 100000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 11}, [[11, 6, 0], [5, 6]]], ['odd radix deaths reconcile', {'q': [23365, 16837, 21555, 13823, 21221, 100000], 'radix': 421}, [[421, 323, 269, 211, 182, 143, 0], [98, 54, 58, 29, 39, 143]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 12}, [[12, 8, 5, 3, 0], [4, 3, 2, 3]]]]]
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 |
|---|---|---|---|
| closed table 0 | [[69457, 68739, 67938, 67030, 66015, 64886, 0], [718, 801, 908, 1015, 1129, 64886]] | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | Failed |
| closed table 1 | [[81539, 81055, 80476, 79812, 79069, 78227, 77282, 76228, 0], [484, 579, 664, 743, 842, 945, 1054, 76228]] | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | Failed |
| closed table 2 | [[23619, 23497, 23352, 23187, 22998, 22779, 22526, 22237, 0], [122, 145, 165, 189, 219, 253, 289, 22237]] | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | Failed |
| zero mortality ages | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | Passed |
| open table rejected | open table | open table | Passed |
| early certain death then open | open table | open table | Passed |
| negative rate rejected | invalid q | invalid q | Passed |
| zero radix rejected | invalid radix | invalid radix | Passed |
| negative radix rejected | invalid radix | invalid radix | Passed |
| half-death rounding tie | [[3, 1, 0], [2, 1]] | [[3, 2, 0], [1, 2]] | Failed |
| odd radix deaths reconcile | [[361, 304, 234, 146, 117, 100, 0], [57, 70, 88, 29, 17, 100]] | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | Failed |
| small radix rounding | [[8, 5, 3, 1, 0], [3, 2, 2, 1]] | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | Failed |
SHA-256 / d93ce7932dc72395b16f4caf71ccfc23fe76cb0c8ef3768b535ce77df852e452
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):
qs=[Fraction(q,100000) for q in x['q']]
if x['radix']<=0: return 'invalid radix'
if any(q<0 or q>1 for q in qs): return 'invalid q'
if not qs or qs[-1]!=1: return 'open table'
l=[x['radix']]
for q in qs:
l.append(math.floor(l[-1]*(1-q)+Fraction(1,2)))
d=[a-b for a,b in zip(l,l[1:])]
return [l,d]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['closed table 0', {'q': [1033, 1165, 1336, 1514, 1709, 100000], 'radix': 69457}, [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]]], ['closed table 1', {'q': [593, 714, 824, 930, 1064, 1208, 1363, 100000], 'radix': 81539}, [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]]], ['closed table 2', {'q': [515, 615, 705, 814, 949, 1107, 1282, 100000], 'radix': 23619}, [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]]], ['zero mortality ages', {'q': [0, 0, 796, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]]], ['open table rejected', {'q': [555, 636, 741, 860, 978], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [223, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 3}, [[3, 2, 0], [1, 2]]], ['odd radix deaths reconcile', {'q': [15657, 22915, 37291, 19541, 14273, 100000], 'radix': 361}, [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 8}, [[8, 5, 3, 2, 0], [3, 2, 1, 2]]]], [['closed table 0', {'q': [1224, 1411, 1580, 1785, 2044, 2304, 2625, 2976, 3345, 100000], 'radix': 73425}, [[73425, 72526, 71503, 70373, 69117, 67704, 66144, 64408, 62491, 60401, 0], [899, 1023, 1130, 1256, 1413, 1560, 1736, 1917, 2090, 60401]]], ['closed table 1', {'q': [1483, 1675, 1889, 2148, 2445, 2784, 3127, 100000], 'radix': 95565}, [[95565, 94148, 92571, 90822, 88871, 86698, 84284, 81648, 0], [1417, 1577, 1749, 1951, 2173, 2414, 2636, 81648]]], ['closed table 2', {'q': [947, 1104, 1253, 1408, 1590, 1811, 2059, 2344, 2661, 100000], 'radix': 84753}, [[84753, 83950, 83023, 81983, 80829, 79544, 78103, 76495, 74702, 72714, 0], [803, 927, 1040, 1154, 1285, 1441, 1608, 1793, 1988, 72714]]], ['zero mortality ages', {'q': [0, 0, 577, 100000], 'radix': 100000}, [[100000, 100000, 100000, 99423, 0], [0, 0, 577, 99423]]], ['open table rejected', {'q': [401, 494, 603, 675, 758], 'radix': 10000}, 'open table'], ['early certain death then open', {'q': [861, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 100000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 5}, [[5, 3, 0], [2, 3]]], ['odd radix deaths reconcile', {'q': [34607, 36625, 31203, 24229, 24851, 100000], 'radix': 951}, [[951, 622, 394, 271, 205, 154, 0], [329, 228, 123, 66, 51, 154]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 9}, [[9, 6, 4, 2, 0], [3, 2, 2, 2]]]], [['closed table 0', {'q': [843, 986, 1140, 1296, 1486, 1699, 1937, 2195, 100000], 'radix': 30585}, [[30585, 30327, 30028, 29686, 29301, 28866, 28376, 27826, 27215, 0], [258, 299, 342, 385, 435, 490, 550, 611, 27215]]], ['closed table 1', {'q': [1278, 1465, 1677, 1884, 2120, 100000], 'radix': 34567}, [[34567, 34125, 33625, 33061, 32438, 31750, 0], [442, 500, 564, 623, 688, 31750]]], ['closed table 2', {'q': [699, 786, 882, 1036, 1165, 1332, 1497, 1680, 100000], 'radix': 78197}, [[78197, 77650, 77040, 76361, 75570, 74690, 73695, 72592, 71372, 0], [547, 610, 679, 791, 880, 995, 1103, 1220, 71372]]], ['zero mortality ages', {'q': [0, 0, 651, 100000], 'radix': 10000}, [[10000, 10000, 10000, 9935, 0], [0, 0, 65, 9935]]], ['open table rejected', {'q': [293, 338, 418, 503, 565], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [246, 100000, 50000], 'radix': 10000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 7}, [[7, 4, 0], [3, 4]]], ['odd radix deaths reconcile', {'q': [24577, 26111, 12881, 35469, 14789, 100000], 'radix': 563}, [[563, 425, 314, 274, 177, 151, 0], [138, 111, 40, 97, 26, 151]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 10}, [[10, 7, 5, 3, 0], [3, 2, 2, 3]]]], [['closed table 0', {'q': [1676, 1888, 2164, 2470, 2790, 3140, 3538, 3970, 100000], 'radix': 15185}, [[15185, 14930, 14648, 14331, 13977, 13587, 13160, 12694, 12190, 0], [255, 282, 317, 354, 390, 427, 466, 504, 12190]]], ['closed table 1', {'q': [2017, 2262, 2573, 2928, 3329, 3764, 4215, 4722, 5308, 100000], 'radix': 18031}, [[18031, 17667, 17267, 16823, 16330, 15786, 15192, 14552, 13865, 13129, 0], [364, 400, 444, 493, 544, 594, 640, 687, 736, 13129]]], ['closed table 2', {'q': [1538, 1732, 1939, 2177, 2450, 2788, 3129, 3535, 100000], 'radix': 93691}, [[93691, 92250, 90652, 88894, 86959, 84829, 82464, 79884, 77060, 0], [1441, 1598, 1758, 1935, 2130, 2365, 2580, 2824, 77060]]], ['zero mortality ages', {'q': [0, 0, 490, 100000], 'radix': 1000}, [[1000, 1000, 1000, 995, 0], [0, 0, 5, 995]]], ['open table rejected', {'q': [226, 259, 292, 356, 432], 'radix': 100000}, 'open table'], ['early certain death then open', {'q': [598, 100000, 50000], 'radix': 1000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 10000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 9}, [[9, 5, 0], [4, 5]]], ['odd radix deaths reconcile', {'q': [33513, 18981, 38831, 14099, 33629, 100000], 'radix': 617}, [[617, 410, 332, 203, 174, 115, 0], [207, 78, 129, 29, 59, 115]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 11}, [[11, 7, 5, 3, 0], [4, 2, 2, 3]]]], [['closed table 0', {'q': [992, 1146, 1306, 1489, 1674, 100000], 'radix': 62363}, [[62363, 61744, 61036, 60239, 59342, 58349, 0], [619, 708, 797, 897, 993, 58349]]], ['closed table 1', {'q': [2141, 2429, 2766, 3097, 3484, 3938, 4441, 4974, 5590, 100000], 'radix': 50071}, [[50071, 48999, 47809, 46487, 45047, 43478, 41766, 39911, 37926, 35806, 0], [1072, 1190, 1322, 1440, 1569, 1712, 1855, 1985, 2120, 35806]]], ['closed table 2', {'q': [1626, 1851, 2115, 2406, 2724, 3092, 3463, 3919, 100000], 'radix': 12425}, [[12425, 12223, 11997, 11743, 11460, 11148, 10803, 10429, 10020, 0], [202, 226, 254, 283, 312, 345, 374, 409, 10020]]], ['zero mortality ages', {'q': [0, 0, 245, 100000], 'radix': 1000}, [[1000, 1000, 1000, 998, 0], [0, 0, 2, 998]]], ['open table rejected', {'q': [345, 413, 466, 555, 658], 'radix': 1000}, 'open table'], ['early certain death then open', {'q': [601, 100000, 50000], 'radix': 100000}, 'open table'], ['negative rate rejected', {'q': [100, -5, 100000], 'radix': 1000}, 'invalid q'], ['zero radix rejected', {'q': [100, 100000], 'radix': 0}, 'invalid radix'], ['negative radix rejected', {'q': [100, 100000], 'radix': -1000}, 'invalid radix'], ['half-death rounding tie', {'q': [50000, 100000], 'radix': 11}, [[11, 6, 0], [5, 6]]], ['odd radix deaths reconcile', {'q': [23365, 16837, 21555, 13823, 21221, 100000], 'radix': 421}, [[421, 323, 269, 211, 182, 143, 0], [98, 54, 58, 29, 39, 143]]], ['small radix rounding', {'q': [33333, 33333, 50000, 100000], 'radix': 12}, [[12, 8, 5, 3, 0], [4, 3, 2, 3]]]]]
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 |
|---|---|---|---|
| closed table 0 | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | [[69457, 68740, 67939, 67031, 66016, 64888, 0], [717, 801, 908, 1015, 1128, 64888]] | Passed |
| closed table 1 | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | [[81539, 81055, 80476, 79813, 79071, 78230, 77285, 76232, 0], [484, 579, 663, 742, 841, 945, 1053, 76232]] | Passed |
| closed table 2 | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | [[23619, 23497, 23352, 23187, 22998, 22780, 22528, 22239, 0], [122, 145, 165, 189, 218, 252, 289, 22239]] | Passed |
| zero mortality ages | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | [[100000, 100000, 100000, 99204, 0], [0, 0, 796, 99204]] | Passed |
| open table rejected | open table | open table | Passed |
| early certain death then open | open table | open table | Passed |
| negative rate rejected | invalid q | invalid q | Passed |
| zero radix rejected | invalid radix | invalid radix | Passed |
| negative radix rejected | invalid radix | invalid radix | Passed |
| half-death rounding tie | [[3, 2, 0], [1, 2]] | [[3, 2, 0], [1, 2]] | Passed |
| odd radix deaths reconcile | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | [[361, 304, 234, 147, 118, 101, 0], [57, 70, 87, 29, 17, 101]] | Passed |
| small radix rounding | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | [[8, 5, 3, 2, 0], [3, 2, 1, 2]] | Passed |
SHA-256 / e0672c3742c5c9804549451fce3aa8625b62ebaede73fabd296667c722f6959f
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:13.513722+00:00.
Case digest / 336115728e884269d922fee26c56370738e90ad3c8f111f4789972a86cd9b8a4