nx_doe.nx source
↩ module page · 97 lines · 2978 B
1// nx_doe.nx -- FOOD-SCIENCE SUITE / DESIGN OF EXPERIMENTS rung. Closes the
2// DOE behind-axis (where Minitab/JMP lead): 2^k full-factorial and 2^(k-p)
3// fractional-factorial designs, with main effects and interaction effects
4// computed from the coded (-1/+1) sign matrix -- the same estimator Yates'
5// algorithm produces, done in exact integer arithmetic.
6//
7// Standard (Yates) run order: factor j's coded level in run i is +1 iff
8// bit j of i is set, else -1. A main effect = (2/N) * sum(sign_j(i)*y_i);
9// an interaction effect uses the product of the two factors' signs. All
10// INTEGER (responses integer; effects are exact when N | 2*sum).
11//
12// THE exceed vs the field: not depth over Minitab, but the SAME factorial
13// estimator, sovereign + composable with the rest of the food-science
14// suite (optimize a ferment or a formulation and feed the winner straight
15// into the flavor/nutrition organs).
16//
17// grounded: box_hunter_hunter_2k_factorial + yates_algorithm_effects
18// genealogy_id: design_of_experiments + nishi_food_science_suite
19
20import "nx_syscalls.nx"
21
22// Runs in a 2^k full factorial.
23func doe_num_runs(k: i64) -> i64 {
24 if k < 0 { return 0 }
25 if k > 20 { return 0 }
26 return 1 << k
27}
28
29// Runs in a 2^(k-p) fractional factorial.
30func doe_frac_runs(k: i64, p: i64) -> i64 {
31 if p < 0 { return 0 }
32 if p > k { return 0 }
33 return doe_num_runs(k - p)
34}
35
36// Coded level (-1 / +1) of factor j in standard-order run i.
37func doe_sign(run_i: i64, factor_j: i64) -> i64 {
38 if ((run_i >> factor_j) & 1) == 1 { return 1 }
39 return 0 - 1
40}
41
42// Grand mean of the responses.
43func doe_grand_mean(y: *i64, nruns: i64) -> i64 {
44 if nruns <= 0 { return 0 }
45 var s: i64 = 0
46 var i: i64 = 0
47 while i < nruns {
48 s = s + y[i]
49 i = i + 1
50 }
51 return s / nruns
52}
53
54// Main effect of factor j = (2/N) * sum over runs of sign_j(i) * y_i.
55func doe_main_effect(y: *i64, nruns: i64, factor_j: i64) -> i64 {
56 if nruns <= 0 { return 0 }
57 var s: i64 = 0
58 var i: i64 = 0
59 while i < nruns {
60 s = s + doe_sign(i, factor_j) * y[i]
61 i = i + 1
62 }
63 return s * 2 / nruns
64}
65
66// Two-factor interaction effect (product of the two factors' signs).
67func doe_interaction_effect(y: *i64, nruns: i64, fa: i64, fb: i64) -> i64 {
68 if nruns <= 0 { return 0 }
69 var s: i64 = 0
70 var i: i64 = 0
71 while i < nruns {
72 s = s + doe_sign(i, fa) * doe_sign(i, fb) * y[i]
73 i = i + 1
74 }
75 return s * 2 / nruns
76}
77
78func doe_abs(v: i64) -> i64 {
79 if v < 0 { return 0 - v }
80 return v
81}
82
83// Index of the factor (0..k-1) with the largest-magnitude main effect.
84func doe_dominant_factor(y: *i64, nruns: i64, k: i64) -> i64 {
85 var best_j: i64 = -1
86 var best_mag: i64 = -1
87 var j: i64 = 0
88 while j < k {
89 let m: i64 = doe_abs(doe_main_effect(y, nruns, j))
90 if m > best_mag {
91 best_mag = m
92 best_j = j
93 }
94 j = j + 1
95 }
96 return best_j
97}