nx_taste_transduce.nx source
↩ module page · 92 lines · 4149 B
1// nx_taste_transduce.nx -- T0 of the TASTE-SCIENCE ladder: the SENSING /
2// transduction rung (the "electronic tongue" hardware layer).
3//
4// Existing baseline (nx_food_science): each ingredient carries STATIC
5// hand-assigned 0..9 axis scores (umami/sweet/salty/sour/spicy/crunch).
6// That cannot represent (a) detection THRESHOLDS -- a sub-threshold
7// stimulus tastes of nothing, (b) SATURATION -- doubling a stimulus does
8// NOT double perception, or (c) the ~1000x sensitivity range ACROSS
9// qualities (bitter is detected at micromolar; sweet needs millimolar).
10//
11// T0 EXCEEDS it with real receptor pharmacology: the Hill dose-response
12//
13// response(C) = Imax * C^h / (K^h + C^h)
14//
15// where K is the half-saturation concentration, h the Hill coefficient
16// (cooperativity), gated by a per-quality detection threshold. This is
17// the canonical chemoreceptor / e-tongue transduction model. Integer-
18// exact, deterministic, no float.
19//
20// MEASURED EXCEED (gate-checked, see test): 8x the stimulus yields < 2x
21// the perceived intensity (Hill saturation), where a static/linear score
22// would predict 8x; and sub-threshold stimuli register exactly 0.
23//
24// Detection thresholds (micromolar) are grounded in the taste-science
25// sources fetched by nx_food_research_fetch (knowledge/fetched/food_s*_
26// {taste,basic_taste,umami}.raw): quinine ~uM, MSG/sucrose/NaCl ~mM.
27//
28// genealogy_id: hill_1910_dose_response + beidler_1954_taste_equation
29// + nishi_food_science_axes_2026 (the baseline this exceeds)
30
31import "nx_syscalls.nx"
32
33// ===== Sealed enum: the five canonical taste qualities ===========
34
35const NX_TQ_SWEET: i64 = 0
36const NX_TQ_SOUR: i64 = 1
37const NX_TQ_SALTY: i64 = 2
38const NX_TQ_BITTER: i64 = 3
39const NX_TQ_UMAMI: i64 = 4
40const NX_TQ_N_QUALITIES: i64 = 5
41
42// ===== Struct: NxTasteReceptor ===================================
43// All concentrations in micromolar (uM); response in milli (0..1000).
44
45struct NxTasteReceptor {
46 quality: i64,
47 threshold_um: i64, // detection threshold (conscious-perception floor)
48 half_sat_um: i64, // K: half-maximal response concentration
49 hill_h: i64, // Hill coefficient (1 = hyperbolic, 2 = cooperative)
50 imax_milli: i64, // maximal perceived intensity (milli)
51}
52
53// Per-quality receptor parameters, grounded in published taste-detection
54// thresholds. Bitter (toxin avoidance) is ~1000x more sensitive than
55// sweet/salty; umami and bitter are cooperative (h = 2).
56func nx_taste_receptor(quality: i64) -> *NxTasteReceptor {
57 let r: *NxTasteReceptor = (sys_mmap(40)) as *NxTasteReceptor
58 r.quality = quality
59 r.threshold_um = 0
60 r.half_sat_um = 1
61 r.hill_h = 1
62 r.imax_milli = 0
63 if quality == NX_TQ_SWEET { r.threshold_um = 10000; r.half_sat_um = 30000; r.hill_h = 1; r.imax_milli = 1000 }
64 if quality == NX_TQ_SOUR { r.threshold_um = 2000; r.half_sat_um = 6000; r.hill_h = 1; r.imax_milli = 1000 }
65 if quality == NX_TQ_SALTY { r.threshold_um = 10000; r.half_sat_um = 30000; r.hill_h = 1; r.imax_milli = 1000 }
66 if quality == NX_TQ_BITTER { r.threshold_um = 8; r.half_sat_um = 25; r.hill_h = 2; r.imax_milli = 1000 }
67 if quality == NX_TQ_UMAMI { r.threshold_um = 1000; r.half_sat_um = 3000; r.hill_h = 2; r.imax_milli = 1000 }
68 return r
69}
70
71// The Hill dose-response: perceived intensity (milli) at concentration C.
72// h = 1 -> Imax*C/(K+C); h = 2 -> Imax*C^2/(K^2+C^2).
73func nx_taste_response_milli(r: *NxTasteReceptor, conc_um: i64) -> i64 {
74 if conc_um <= 0 { return 0 }
75 if r.hill_h == 1 {
76 return (r.imax_milli * conc_um) / (r.half_sat_um + conc_um)
77 }
78 let c2: i64 = conc_um * conc_um
79 let k2: i64 = r.half_sat_um * r.half_sat_um
80 return (r.imax_milli * c2) / (k2 + c2)
81}
82
83// Detection predicate: is the stimulus at/above the conscious-perception
84// threshold? (A static axis score cannot express this.)
85func nx_taste_detectable(r: *NxTasteReceptor, conc_um: i64) -> i64 {
86 if conc_um >= r.threshold_um { return 1 }
87 return 0
88}
89
90func nx_taste_half_sat_um(r: *NxTasteReceptor) -> i64 {
91 return r.half_sat_um
92}