nx_flavor_design.nx source
↩ module page · 127 lines · 4243 B
1// nx_flavor_design.nx -- T5 of the TASTE-SCIENCE ladder: GENERATIVE
2// FLAVOR DESIGN. The taste-ladder analogue of ferment R5: given a TARGET
3// perceived taste profile and a palette of ingredients, GENERATE the
4// combination whose PERCEIVED profile (after T1 mixture suppression) best
5// matches the target -- creating a flavor the palette has none of on its
6// own.
7//
8// The baseline nx_food_science picks from a FIXED slot frame by summing
9// static axis scores. T5 searches the palette (singles AND blends) and
10// scores each candidate by its PERCEIVED profile (real psychophysics),
11// targeting an arbitrary goal -- so it can DESIGN toward "balanced sweet-
12// sour" or any target, and the best answer is often a BLEND that exists in
13// no single ingredient.
14//
15// MEASURED EXCEED (gate-checked): for a balanced target, the best 2-
16// ingredient BLEND (a new combination) beats every single ingredient,
17// scored on perceived (suppressed) profiles -- generation the static
18// frame cannot do.
19//
20// Composes T1 (nx_taste_psychophysics) for mixture perception.
21//
22// genealogy_id: nishi_taste_psychophysics_t1 + generate_and_test_design
23
24import "nx_syscalls.nx"
25import "nx_taste_psychophysics.nx"
26const K_MAGIC_999999999: i64 = 999999999
27
28// Row c of a flat K*5 palette of raw taste vectors.
29func _fd_row(palette: *i64, c: i64) -> *i64 {
30 return (palette as i64 + c * 40) as *i64
31}
32
33// L1 perceptual distance between two 5-taste vectors (lower = closer).
34func nx_flavor_distance(a: *i64, b: *i64) -> i64 {
35 var d: i64 = 0
36 var i: i64 = 0
37 while i < 5 {
38 var diff: i64 = a[i] - b[i]
39 if diff < 0 { diff = 0 - diff }
40 d = d + diff
41 i = i + 1
42 }
43 return d
44}
45
46// Perceived profile of a raw taste vector (T1 cross-suppression) into out[5].
47func nx_flavor_perceived(raw: *i64, out: *i64) -> i64 {
48 nx_taste_mixture_perceive(raw[0], raw[1], raw[2], raw[3], raw[4], out)
49 return 0
50}
51
52// Blend two raw vectors (component sum) into out[5].
53func nx_flavor_blend(a: *i64, b: *i64, out: *i64) -> i64 {
54 var i: i64 = 0
55 while i < 5 {
56 out[i] = a[i] + b[i]
57 i = i + 1
58 }
59 return 0
60}
61
62// Index of the single palette ingredient whose PERCEIVED profile is closest
63// to the target.
64func nx_flavor_design_best(target: *i64, palette: *i64, k: i64) -> i64 {
65 let perc: *i64 = (sys_mmap(40)) as *i64
66 var best_i: i64 = 0 - 1
67 var best_d: i64 = K_MAGIC_999999999
68 var c: i64 = 0
69 while c < k {
70 let raw: *i64 = _fd_row(palette, c)
71 nx_flavor_perceived(raw, perc)
72 let d: i64 = nx_flavor_distance(target, perc)
73 if d < best_d {
74 best_d = d
75 best_i = c
76 }
77 c = c + 1
78 }
79 return best_i
80}
81
82// Distance of the best single ingredient (for comparison with a blend).
83func nx_flavor_design_best_distance(target: *i64, palette: *i64, k: i64) -> i64 {
84 let perc: *i64 = (sys_mmap(40)) as *i64
85 var best_d: i64 = K_MAGIC_999999999
86 var c: i64 = 0
87 while c < k {
88 let raw: *i64 = _fd_row(palette, c)
89 nx_flavor_perceived(raw, perc)
90 let d: i64 = nx_flavor_distance(target, perc)
91 if d < best_d { best_d = d }
92 c = c + 1
93 }
94 return best_d
95}
96
97// GENERATE: the best 2-ingredient blend matching the target. Writes the
98// chosen indices to out_i[0], out_j[0]; returns the blend's distance.
99func nx_flavor_design_best_blend(target: *i64, palette: *i64, k: i64,
100 out_i: *i64, out_j: *i64) -> i64 {
101 let blend: *i64 = (sys_mmap(40)) as *i64
102 let perc: *i64 = (sys_mmap(40)) as *i64
103 var best_d: i64 = K_MAGIC_999999999
104 var bi: i64 = 0 - 1
105 var bj: i64 = 0 - 1
106 var i: i64 = 0
107 while i < k {
108 var j: i64 = i + 1
109 while j < k {
110 let ri: *i64 = _fd_row(palette, i)
111 let rj: *i64 = _fd_row(palette, j)
112 nx_flavor_blend(ri, rj, blend)
113 nx_flavor_perceived(blend, perc)
114 let d: i64 = nx_flavor_distance(target, perc)
115 if d < best_d {
116 best_d = d
117 bi = i
118 bj = j
119 }
120 j = j + 1
121 }
122 i = i + 1
123 }
124 out_i[0] = bi
125 out_j[0] = bj
126 return best_d
127}