code wiki / (root) / nx_taste_psychophysics.nx

nx_taste_psychophysics.nx source

↩ module page · 65 lines · 3258 B

1// nx_taste_psychophysics.nx -- T1 of the TASTE-SCIENCE ladder: the 2// PERCEPTION rung. Where T0 maps one stimulus -> one receptor response, 3// T1 models how MULTIPLE tastes interact in a real mixture -- the central 4// psychophysics the static-score baseline (nx_food_science: just SUM the 5// axes) fundamentally cannot represent. 6// 7// Two real, famous phenomena: 8// 1. MIXTURE SUPPRESSION (divisive normalization): in a mixture, each 9// taste is suppressed by the others -- sugar mutes sourness and 10// bitterness (why lemonade needs so much sugar). The perceived 11// whole is SUB-ADDITIVE: less than the sum of the parts. 12// 2. UMAMI SYNERGY (Yamaguchi 1967): monosodium glutamate + a 5'- 13// ribonucleotide (IMP/GMP) produce umami FAR GREATER than the sum -- 14// a multiplicative super-additive term. The reason dashi (kombu + 15// bonito) tastes the way it does. 16// 17// MEASURED EXCEED (gate-checked): mixture total < linear sum (suppression) 18// and glutamate+nucleotide > linear sum (synergy). A model that just adds 19// axis scores gets BOTH wrong, in opposite directions. 20// 21// All intensities in milli (0..1000). Integer-exact, deterministic. 22// 23// genealogy_id: yamaguchi_1967_umami_synergy + bartoshuk_mixture_suppression 24// + heeger_1992_divisive_normalization 25 26import "nx_syscalls.nx" 27 28const NX_TP_SUPP_SCALE: i64 = 2000 // divisive-suppression scale (uM-equiv) 29const NX_TP_SYNERGY_COEFF: i64 = 8000 // umami synergy strength (Yamaguchi) 30 31// Divisive suppression: a component's perceived intensity, reduced by the 32// SUM of the other tastes present. sum_others = 0 -> unchanged. 33func nx_taste_suppress_one(intensity_i: i64, sum_others: i64) -> i64 { 34 if intensity_i <= 0 { return 0 } 35 return (intensity_i * NX_TP_SUPP_SCALE) / (NX_TP_SUPP_SCALE + sum_others) 36} 37 38// Umami synergy (Yamaguchi): U = G + N + k*G*N. Super-additive whenever 39// BOTH glutamate and a nucleotide are present; collapses to G alone when 40// the nucleotide is absent. 41func nx_taste_umami_synergy(glutamate_milli: i64, nucleotide_milli: i64) -> i64 { 42 let synergy: i64 = (NX_TP_SYNERGY_COEFF * glutamate_milli * nucleotide_milli) / 1000000 43 return glutamate_milli + nucleotide_milli + synergy 44} 45 46// Perceive a 5-taste mixture: write the cross-suppressed per-taste 47// intensities into out[0..5] = (sweet, sour, salty, bitter, umami). 48func nx_taste_mixture_perceive(sweet: i64, sour: i64, salty: i64, 49 bitter: i64, umami: i64, out: *i64) -> i64 { 50 let total: i64 = sweet + sour + salty + bitter + umami 51 out[0] = nx_taste_suppress_one(sweet, total - sweet) 52 out[1] = nx_taste_suppress_one(sour, total - sour) 53 out[2] = nx_taste_suppress_one(salty, total - salty) 54 out[3] = nx_taste_suppress_one(bitter, total - bitter) 55 out[4] = nx_taste_suppress_one(umami, total - umami) 56 return 0 57} 58 59// Total PERCEIVED intensity of a mixture (sub-additive vs the raw sum). 60func nx_taste_mixture_total(sweet: i64, sour: i64, salty: i64, 61 bitter: i64, umami: i64) -> i64 { 62 let out: *i64 = (sys_mmap(8 * 5)) as *i64 63 nx_taste_mixture_perceive(sweet, sour, salty, bitter, umami, out) 64 return out[0] + out[1] + out[2] + out[3] + out[4] 65}