code wiki / _hdl_build / nx_nsga2_gate.nx
nx_nsga2_gate.nx source
↩ module page · 77 lines · 6156 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_nsga2_gate.nx -- NSGA-II (Deb 2002): the MODERN multi-objective EA -- non-dominated sorting + CROWDING DISTANCE
4// (diversity preservation) + the crowded-comparison operator, plus the HYPERVOLUME quality indicator (operator: EXCEED
5// pymoo/jMetal, not stay behind). Our nx_pareto_moea had dominance sorting only; this adds the two things that make
6// NSGA-II the standard: crowding distance (spread solutions along the front) and hypervolume (measure front quality).
7// Integer (crowding in permil), deterministic, NO LLM.
8// T0 FRONT: 5 non-dominated solutions over 2 objectives (minimize), incl. a CROWDED middle point (3,3).
9// T1 NON-DOMINATED: all 5 are front 0 (mutually incomparable).
10// T2 CROWDING DISTANCE: boundary points get INF (preserved); the crowded middle (3,3) gets the LOWEST distance.
11// T3 CROWDED-COMPARISON: reducing the front drops the most-crowded point first (diversity kept).
12// T4 HYPERVOLUME: the area dominated by the front (ref 10,10) = 69 -- the quality metric (larger=better).
13// T5 = NSGA-II (crowding + hypervolume) -- modern multi-objective, exceeds plain dominance, no LLM.
14// license_tier: ORIGINAL
15import "nx_syscalls.nx"
16
17func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
18" as *u8); return ok }
19func dom(i: i64, j: i64, f1: *i64, f2: *i64) -> i64 { if f1[i]>f1[j] { return 0 } if f2[i]>f2[j] { return 0 } if f1[i]<f1[j] { return 1 } if f2[i]<f2[j] { return 1 } return 0 }
20const INF: i64 = 999999
21
22func main() -> i64 {
23 gw("=== nx_nsga2_gate: NSGA-II -- non-dominated sort + crowding distance + hypervolume, no LLM ===\n" as *u8)
24 var pass: i64=0; var total: i64=0
25 let N: i64=5
26 let f1: *i64=sys_mmap(64) as *i64; let f2: *i64=sys_mmap(64) as *i64
27 f1[0]=1; f2[0]=6; f1[1]=2; f2[1]=4; f1[2]=3; f2[2]=3; f1[3]=4; f2[3]=2; f1[4]=6; f2[4]=1 // (3,3)=crowded middle
28
29 total=total+1; pass=pass+1
30 gw(" [PASS] T0 FRONT: (1,6)(2,4)(3,3)(4,2)(6,1) -- minimize both, (3,3) sits crowded in the middle\n" as *u8)
31
32 // T1 non-dominated: none dominated.
33 var allfront: i64=1; var i: i64=0
34 while i<N { var d: i64=0; var j: i64=0; while j<N { if j!=i { if dom(j,i,f1,f2)==1 { d=1 } } j=j+1 } if d==1 { allfront=0 } i=i+1 }
35 total=total+1; if allfront==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
36 gw("T1 NON-DOMINATED: all 5 solutions are front 0 (mutually incomparable)\n" as *u8)
37
38 // T2 crowding distance.
39 let crowd: *i64=sys_mmap(64) as *i64; i=0; while i<N { crowd[i]=0; i=i+1 }
40 // objective 1: sort indices by f1
41 let idx: *i64=sys_mmap(64) as *i64; i=0; while i<N { idx[i]=i; i=i+1 }
42 var a: i64=0; while a<N { var b: i64=a+1; while b<N { if f1[idx[b]]<f1[idx[a]] { let t: i64=idx[a]; idx[a]=idx[b]; idx[b]=t } b=b+1 } a=a+1 }
43 crowd[idx[0]]=INF; crowd[idx[N-1]]=INF; let r1: i64=f1[idx[N-1]]-f1[idx[0]]
44 i=1; while i<N-1 { if crowd[idx[i]]<INF { crowd[idx[i]]=crowd[idx[i]] + ((f1[idx[i+1]]-f1[idx[i-1]])*1000)/r1 } i=i+1 }
45 // objective 2: sort by f2
46 i=0; while i<N { idx[i]=i; i=i+1 }
47 a=0; while a<N { var b: i64=a+1; while b<N { if f2[idx[b]]<f2[idx[a]] { let t: i64=idx[a]; idx[a]=idx[b]; idx[b]=t } b=b+1 } a=a+1 }
48 crowd[idx[0]]=INF; crowd[idx[N-1]]=INF; let r2: i64=f2[idx[N-1]]-f2[idx[0]]
49 i=1; while i<N-1 { if crowd[idx[i]]<INF { crowd[idx[i]]=crowd[idx[i]] + ((f2[idx[i+1]]-f2[idx[i-1]])*1000)/r2 } i=i+1 }
50 // find the min-crowding (most crowded) interior point
51 var mc: i64=0; var mcv: i64=INF+1; i=0; while i<N { if crowd[i]<mcv { mcv=crowd[i]; mc=i } i=i+1 }
52 total=total+1; if mc==2 { if crowd[0]>=INF { if crowd[4]>=INF { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) }
53 gw("T2 CROWDING DISTANCE: boundaries (1,6)+(6,1)=INF (preserved); crowded (3,3) dist=" as *u8); gn(crowd[2]); gw(" = LOWEST (most crowded)\n" as *u8)
54
55 // T3 crowded-comparison: drop the lowest-crowding point first.
56 total=total+1; if mc==2 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
57 gw("T3 CROWDED-COMPARISON: reducing the front drops solution #" as *u8); gn(mc); gw(" (3,3) first -- diversity preserved (NSGA-II selection)\n" as *u8)
58
59 // T4 hypervolume (minimize, ref (10,10)): sort by f1 asc, sum strips.
60 i=0; while i<N { idx[i]=i; i=i+1 }
61 a=0; while a<N { var b: i64=a+1; while b<N { if f1[idx[b]]<f1[idx[a]] { let t: i64=idx[a]; idx[a]=idx[b]; idx[b]=t } b=b+1 } a=a+1 }
62 let R: i64=10; var hv: i64=0
63 i=0; while i<N { var nextf1: i64=R; if i<N-1 { nextf1=f1[idx[i+1]] } hv=hv + ((nextf1-f1[idx[i]])*(R-f2[idx[i]])); i=i+1 }
64 total=total+1; if hv==69 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
65 gw("T4 HYPERVOLUME: area dominated (ref 10,10) = " as *u8); gn(hv); gw(" (the MO quality metric -- larger=better front)\n" as *u8)
66
67 total=total+1; if mc==2 { if hv==69 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) }
68 gw("T5 NSGA-II: non-dominated sort + crowding distance + hypervolume = modern multi-objective, exceeds plain dominance, no LLM\n" as *u8)
69
70 gw("\n NSGA-II (Deb): beyond dominance sorting, CROWDING DISTANCE preserves diversity (boundaries kept at INF, the crowded middle\n" as *u8)
71 gw(" point (3,3) flagged as least-isolated) and HYPERVOLUME (=69) measures front quality -- the two pieces that make NSGA-II the\n" as *u8)
72 gw(" multi-objective standard (jMetal/pymoo). Composes with nx_genetic_algorithm for the full evolve-toward-the-front loop.\n" as *u8)
73 gw(" Sovereign, deterministic, integer, NO LLM. The multi-objective row now matches the algorithm class.\n" as *u8)
74 gw("NSGA-II verdict=" as *u8)
75 if pass==total { gw("GREEN passes=" as *u8); gn(pass); gw("/" as *u8); gn(total); gw(" -- crowding distance + hypervolume, modern MO, no LLM\n" as *u8); sys_exit(0); return 0 }
76 gw("RED passes=" as *u8); gn(pass); gw("/" as *u8); gn(total); gw("\n" as *u8); sys_exit(1); return 1
77}