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}