code wiki / (root) / nx_doe.nx

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}