nx_logadd_lib.nx
buildroot/runtime/nx_logadd_lib.nx
about
nx_logadd_lib.nx -- FIXED-POINT LOG-DOMAIN ARITHMETIC, no floats anywhere: the primitive a linear-chain CRF's
forward-backward pass needs (log-sum-exp) and the probability it reads back (exp of a non-positive log-domain value).
Scale LA_S = 1024 units per nat (Q10). la_lse(a, b) = ln(e^a + e^b) in Q10; la_exp_neg(x), x <= 0 -> a Q10 probability.
The two tables are built ONCE from first principles rather than typed: e^(-1/S) in Q30 from its Taylor series (the
third term is already below one unit at this scale, so the constant is exact to the last bit), the powers e^(-d/S) by
recurrence, and softplus(d) = S*ln(1 + e^(-d/S)) through the estate's ONE integer logarithm (nx_intlog ilog2_1024) --
composed, never re-implemented. The horizon LA past which softplus rounds to zero is DERIVED at init by scanning the
table, never declared. Minus infinity is LA_NEG_INF, the same sentinel the aspect model's Viterbi already uses, so a
masked transition reads identically in both decoders. license_tier: ORIGINAL No hw writes (Rule 26). LIB.
dependencies 2 imports · 2 importers
imports: nx_syscalls.nxnx_intlog.nx
imported by: nx_absa_seq.nxnx_logadd_gate.nx
structs
| none |
consts
| 13 | const LA_S: i64 = 1024 // Q10: units per nat |
| 14 | const LA_Q30: i64 = 1073741824 // 2^30, the exp table's unit |
| 15 | const LA_Q30_SHIFT: i64 = 30 |
| 16 | const LA_Q30_HALF: i64 = 536870912 // rounding half for a Q30 product |
| 17 | const LA_Q20_SHIFT: i64 = 20 |
| 18 | const LA_Q20_HALF: i64 = 524288 |
| 19 | const LA_LN2_Q20: i64 = 726817 // ln 2 in Q20 (0.6931471806 * 2^20 = 726,817.0), turns a Q10 log2 into a Q10 ln |
| 20 | const LA_LOG2_Q30_Q10: i64 = 30720 // ilog2_1024(2^30) = 30 * 1024: subtracted so ln(1+p) reads from a Q30 (1+p) |
| 21 | const LA_NEG_INF: i64 = 0 - 1000000000 // log-domain minus infinity (== SP_NEG in nx_absa_seq) |
| 22 | const LA_NEG_INF_GUARD: i64 = 0 - 500000000 // anything at or below this is treated as minus infinity (an -inf plus a finite) |
| 23 | const LA_TABLE_CAP: i64 = 16384 // table entries; the derived horizon is ~S*ln(2S), a little under half of this |
| 24 | const LA_I64_BYTES: i64 = 8 |
| 25 | const LA_TAYLOR_TERMS: i64 = 6 // terms of e^(-1/S) at Q30: the 3rd is already < 1 unit, six is exact by any measure |
functions
| 34 | func la_exp_unit_q30() -> i64 |
| 46 | func la_init() -> i64 |
| 77 | func la_horizon() -> i64 { return la_horizon_v } called by 1: main |
| 78 | func la_clamp_count() -> i64 { return la_clamps } called by 1: main |
| 79 | func la_is_neg_inf(x: i64) -> i64 { if x <= LA_NEG_INF_GUARD { return 1 } return 0 } |
| 81 | func la_lse(a: i64, b: i64) -> i64 |
| 91 | func la_exp_neg(x: i64) -> i64 |
| 98 | func la_exp_neg_q30(x: i64) -> i64 called by 1: main |