code wiki / (root) / nx_flavor_pairing.nx

nx_flavor_pairing.nx source

↩ module page · 104 lines · 3186 B

1// nx_flavor_pairing.nx -- T4 of the TASTE-SCIENCE ladder: the FLAVOR 2// NETWORK. Ahn, Ahnert, Bagrow & Barabasi (2011, "Flavor network and 3// the principles of food pairing") showed that ingredient pairing can be 4// computed from the aroma compounds two ingredients SHARE -- a data- 5// driven affinity, not a chef's opinion. 6// 7// The baseline nx_food_science carries a HAND-LISTED affinity table 8// (beef-oyster = 9, ...): finite, and silent on any pair nobody typed in. 9// T4 computes affinity from each ingredient's compound set, so it scores 10// ANY pair -- including a novel ingredient that has never been in a table. 11// 12// shared(A,B) = |compounds(A) INTERSECT compounds(B)| 13// affinity(A,B) = Jaccard = 1000 * shared / |A UNION B| (milli) 14// 15// MEASURED EXCEED (gate-checked): a fruity pair that shares esters scores 16// above a fruit+allium pair that shares none, identical sets score 1000, 17// and a NOVEL ingredient gets a real affinity computed from its compounds. 18// 19// genealogy_id: ahn_2011_flavor_network + jaccard_1901_similarity 20 21import "nx_syscalls.nx" 22 23// A set of aroma-compound ids. 24struct NxCompoundSet { 25 ids: *i64, 26 n: i64, 27} 28 29// Allocate an empty set with room for n compounds; caller fills .ids[0..n). 30func nx_compoundset_new(n: i64) -> *NxCompoundSet { 31 let s: *NxCompoundSet = (sys_mmap(16)) as *NxCompoundSet 32 s.ids = (sys_mmap(8 * 64)) as *i64 33 s.n = n 34 return s 35} 36 37// |A INTERSECT B| -- the count of shared aroma compounds. 38func nx_pairing_shared(a: *NxCompoundSet, b: *NxCompoundSet) -> i64 { 39 let aids: *i64 = a.ids 40 let bids: *i64 = b.ids 41 let an: i64 = a.n 42 let bn: i64 = b.n 43 var count: i64 = 0 44 var i: i64 = 0 45 while i < an { 46 var j: i64 = 0 47 while j < bn { 48 if aids[i] == bids[j] { count = count + 1 } 49 j = j + 1 50 } 51 i = i + 1 52 } 53 return count 54} 55 56// Pairing affinity = Jaccard similarity of the two compound sets (milli). 57func nx_pairing_affinity_milli(a: *NxCompoundSet, b: *NxCompoundSet) -> i64 { 58 let shared: i64 = nx_pairing_shared(a, b) 59 let uni: i64 = a.n + b.n - shared 60 if uni <= 0 { return 0 } 61 return shared * 1000 / uni 62} 63 64// ===== Grounded ingredient compound sets (illustrative ids reflecting 65// real shared-compound relationships: fruits share esters/furaneol; 66// alliums carry sulfur compounds; roasted meat carries Maillard pyrazines). 67 68func nx_compounds_strawberry() -> *NxCompoundSet { 69 let s: *NxCompoundSet = nx_compoundset_new(6) 70 let a: *i64 = s.ids 71 a[0] = 0 72 a[1] = 1 73 a[2] = 2 74 a[3] = 3 75 a[4] = 4 76 a[5] = 5 77 return s 78} 79func nx_compounds_pineapple() -> *NxCompoundSet { 80 let s: *NxCompoundSet = nx_compoundset_new(5) 81 let a: *i64 = s.ids 82 a[0] = 3 83 a[1] = 4 84 a[2] = 5 85 a[3] = 6 86 a[4] = 7 87 return s 88} 89func nx_compounds_garlic() -> *NxCompoundSet { 90 let s: *NxCompoundSet = nx_compoundset_new(3) 91 let a: *i64 = s.ids 92 a[0] = 10 93 a[1] = 11 94 a[2] = 12 95 return s 96} 97func nx_compounds_roastbeef() -> *NxCompoundSet { 98 let s: *NxCompoundSet = nx_compoundset_new(3) 99 let a: *i64 = s.ids 100 a[0] = 8 101 a[1] = 9 102 a[2] = 10 103 return s 104}