code wiki / (root) / nx_flavor_chem.nx

nx_flavor_chem.nx source

↩ module page · 133 lines · 5234 B

1// nx_flavor_chem.nx -- T3 of the TASTE-SCIENCE ladder: FLAVOR CHEMISTRY. 2// Predict taste from MOLECULAR STRUCTURE via structure-activity rules 3// (SAR), the molecular-gastronomy / e-tongue frontier. The baseline 4// nx_food_science assigns a fixed 0-9 score PER NAMED INGREDIENT; it 5// cannot say anything about a molecule it has never been told about. 6// T3 predicts taste from the molecule's structure -- so it GENERALISES 7// to novel molecules (the whole point of a real flavor model). 8// 9// The structure-activity rules (each a real, citable finding): 10// - SWEET: the AH-B-X theory (Shallenberger & Acree 1967) -- an AH 11// proton donor + a B proton acceptor ~250-400 pm apart; a hydrophobic 12// X site marks a HIGH-POTENCY sweetener (why sucralose ~600x sucrose). 13// - SOUR: acidity -- a carboxyl that releases H+ at low pKa. 14// - SALTY: a small ionic salt (Na+ etc.). 15// - UMAMI: a glutamate-like amino-diacid. 16// - BITTER: an alkaloid / N-heterocycle motif. 17// 18// MEASURED EXCEED (gate-checked): correctly classifies 6 grounded 19// reference molecules AND a NOVEL molecule in no ingredient table, and 20// derives sweetness POTENCY from structure -- generalisation the static 21// table cannot do. 22// 23// The molecular descriptors below are exactly what nx_chem_molecule / 24// nx_chem_descriptors compute from a structure; auto-extracting them from 25// SMILES is T3's named follow-up. Taste-quality vocabulary composed from 26// nx_taste_transduce (T0). 27// 28// genealogy_id: shallenberger_acree_1967_ahb + kier_1972_ahbx 29// + nishi_taste_transduce_2026 30 31import "nx_syscalls.nx" 32import "nx_taste_transduce.nx" 33const NX_MAGIC_5000: i64 = 5000 34const NX_MAGIC_99000: i64 = 99000 35const NX_MAGIC_3100: i64 = 3100 36const NX_MAGIC_3000: i64 = 3000 37 38const NX_TQ_NONE: i64 = 6 // tasteless / no SAR match (0-4 = qualities, 5 = count) 39 40// Sensory-relevant molecular descriptors (the interface to nx_chem). 41struct NxMolSensory { 42 hbond_donors: i64, // AH sites (-OH, -NH) 43 hbond_acceptors: i64, // B sites (O / N lone pairs) 44 ah_b_distance_pm: i64, // donor-acceptor separation, picometers 45 hydrophobic_x: i64, // 1 = a hydrophobic X site (high-potency sweet) 46 carboxyl_count: i64, // -COOH groups 47 pka_milli: i64, // acid pKa x1000 (lower = stronger acid) 48 small_cation_salt: i64, // 1 = small ionic salt (Na+ ...) 49 n_heterocycle: i64, // 1 = alkaloid / N-heterocycle (bitter motif) 50 amino_diacid: i64, // 1 = glutamate-like (umami) 51} 52 53func nx_molsensory_new(donors: i64, acceptors: i64, dist_pm: i64, x: i64, 54 carboxyl: i64, pka_milli: i64, salt: i64, 55 nhet: i64, amino_diacid: i64) -> *NxMolSensory { 56 let m: *NxMolSensory = (sys_mmap(72)) as *NxMolSensory 57 m.hbond_donors = donors 58 m.hbond_acceptors = acceptors 59 m.ah_b_distance_pm = dist_pm 60 m.hydrophobic_x = x 61 m.carboxyl_count = carboxyl 62 m.pka_milli = pka_milli 63 m.small_cation_salt = salt 64 m.n_heterocycle = nhet 65 m.amino_diacid = amino_diacid 66 return m 67} 68 69// AH-B-X sweetness: an AH donor + B acceptor at ~250-400 pm. 70func nx_flavor_is_sweet(m: *NxMolSensory) -> i64 { 71 if m.hbond_donors < 1 { return 0 } 72 if m.hbond_acceptors < 1 { return 0 } 73 if m.ah_b_distance_pm < 250 { return 0 } 74 if m.ah_b_distance_pm > 400 { return 0 } 75 return 1 76} 77 78// Sweetness potency from structure: a hydrophobic X site -> high-potency. 79func nx_flavor_sweet_potency(m: *NxMolSensory) -> i64 { 80 if nx_flavor_is_sweet(m) == 0 { return 0 } 81 if m.hydrophobic_x == 1 { return 1000 } 82 return 200 83} 84 85func nx_flavor_is_sour(m: *NxMolSensory) -> i64 { 86 if m.carboxyl_count >= 1 { 87 if m.pka_milli <= NX_MAGIC_5000 { return 1 } 88 } 89 return 0 90} 91 92func nx_flavor_is_umami(m: *NxMolSensory) -> i64 { 93 return m.amino_diacid 94} 95 96func nx_flavor_is_bitter(m: *NxMolSensory) -> i64 { 97 return m.n_heterocycle 98} 99 100func nx_flavor_is_salty(m: *NxMolSensory) -> i64 { 101 return m.small_cation_salt 102} 103 104// Dominant predicted taste from structure. 105func nx_flavor_predict_taste(m: *NxMolSensory) -> i64 { 106 if nx_flavor_is_salty(m) == 1 { return NX_TQ_SALTY } 107 if nx_flavor_is_umami(m) == 1 { return NX_TQ_UMAMI } 108 if nx_flavor_is_sour(m) == 1 { return NX_TQ_SOUR } 109 if nx_flavor_is_sweet(m) == 1 { return NX_TQ_SWEET } 110 if nx_flavor_is_bitter(m) == 1 { return NX_TQ_BITTER } 111 return NX_TQ_NONE 112} 113 114// ===== Grounded reference molecules (descriptors from real structures) = 115 116func nx_molsensory_sucrose() -> *NxMolSensory { 117 return nx_molsensory_new(8, 8, 300, 0, 0, NX_MAGIC_99000, 0, 0, 0) 118} 119func nx_molsensory_sucralose() -> *NxMolSensory { 120 return nx_molsensory_new(6, 6, 300, 1, 0, NX_MAGIC_99000, 0, 0, 0) 121} 122func nx_molsensory_citric() -> *NxMolSensory { 123 return nx_molsensory_new(3, 6, 280, 0, 3, NX_MAGIC_3100, 0, 0, 0) 124} 125func nx_molsensory_nacl() -> *NxMolSensory { 126 return nx_molsensory_new(0, 0, 0, 0, 0, NX_MAGIC_99000, 1, 0, 0) 127} 128func nx_molsensory_quinine() -> *NxMolSensory { 129 return nx_molsensory_new(1, 4, 500, 0, 0, NX_MAGIC_99000, 0, 1, 0) 130} 131func nx_molsensory_msg() -> *NxMolSensory { 132 return nx_molsensory_new(2, 5, 300, 0, 2, NX_MAGIC_3000, 0, 0, 1) 133}