algorithm.bend
unified-nsga-iii@0.2.0.0 · 504 lines · 0 definitions
1# Unsga3Algorithm.Run — C# Unsga3.Algorithm.Unsga3Algorithm.
2# init → evaluate → loop: tournament parents → SBX (η=30, p=1.0) →
3# polynomial mutation (η=20, p=1/n) → evaluate → survival Select
4# with persistent Normalization. Defaults match C# operator defaults.
5# Mating tournament for A/B is PymooCompatible. RankNicheDistance (C#
6# ctor default) is rank → niche count → perpendicular distance → coin.
7# Survival niching in Run threads rng (C# Select(..., rng): random among
8# equal min-count niches). Empty niches
9# take closest; extras are random among near-best on that ray. Uniform
10# inNiche[Next] with this LCG collapsed oracle ZDT2.
11# Duplicate keys round each decision variable to 12 decimal places
12# (round(x*1e12)/1e12). That is not C# ToString("G12") (12 significant
13# digits). Small variables diverge; see src/g12_key.bend.
14# The seen set is the survivor population, then accepted offspring
15# (C# CreateOffspring hashes population.Members). After pop*40 pair
16# attempts, up to max(pop*20, 1) mutations must still be a new key;
17# only then are duplicates accepted.
18# Odd N: the last parent enters SBX, paired with parent 0. Even N
19# pair lookups stay the monotonic cursor (reset to pair 0 once the
20# cursor passes the last in-range start).
21
22import Base
23import unified-nsga-iii@0.2.0.0/individual.bend as Ind
24import unified-nsga-iii@0.2.0.0/non_dominated_sort.bend as NDS
25import unified-nsga-iii@0.2.0.0/normalization.bend as Norm
26import unified-nsga-iii@0.2.0.0/reference_directions.bend as Refs
27import unified-nsga-iii@0.2.0.0/survival.bend as Surv
28import unified-nsga-iii@0.2.0.0/bounds.bend as Bounds
29import unified-nsga-iii@0.2.0.0/rng.bend as Rng
30import unified-nsga-iii@0.2.0.0/sbx.bend as Sbx
31import unified-nsga-iii@0.2.0.0/polynomial_mutation.bend as Poly
32import unified-nsga-iii@0.2.0.0/problems.bend as Prob
33import unified-nsga-iii@0.2.0.0/tournament.bend as Tour
34import unified-nsga-iii@0.2.0.0/util.bend as Util
35
36type Config is Data:
37 MkConfig{
38 dirs: List<&2, List<&2, F32>>
39 pop_size: Nat
40 gens: Nat
41 seed: Nat
42 mode: Tour.Mode
43 elim_dups: Bool
44 }
45
46def mk(+dirs: List<&2, List<&2, F32>>, +pop_size: Nat, +gens: Nat, +seed: Nat, +mode: Tour.Mode, +elim_dups: Bool) -> Config:
47 MkConfig{dirs, pop_size, gens, seed, mode, elim_dups}
48
49def cfg_dirs(c: Config) -> List<&2, List<&2, F32>>:
50 match c:
51 case MkConfig{dirs, pop_size, gens, seed, mode, elim_dups}:
52 dirs
53
54def cfg_pop(c: Config) -> Nat:
55 match c:
56 case MkConfig{dirs, pop_size, gens, seed, mode, elim_dups}:
57 pop_size
58
59def cfg_gens(c: Config) -> Nat:
60 match c:
61 case MkConfig{dirs, pop_size, gens, seed, mode, elim_dups}:
62 gens
63
64def cfg_seed(c: Config) -> Nat:
65 match c:
66 case MkConfig{dirs, pop_size, gens, seed, mode, elim_dups}:
67 seed
68
69def cfg_mode(c: Config) -> Tour.Mode:
70 match c:
71 case MkConfig{dirs, pop_size, gens, seed, mode, elim_dups}:
72 mode
73
74def cfg_elim(c: Config) -> Bool:
75 match c:
76 case MkConfig{dirs, pop_size, gens, seed, mode, elim_dups}:
77 elim_dups
78
79# Smoke (not oracle): ZDT1 n=30, partitions=4 → 5 refs, pop=8, gens=3, seed=1.
80def smoke_zdt1() -> Config:
81 mk(Refs.das_dennis(2n, 4n), 8n, 3n, 1n, Tour.pymoo(), True{})
82
83# C# OracleCompare / EQUIVALENCE-style settings (not the smoke default).
84def oracle_zdt1() -> Config:
85 mk(Refs.das_dennis(2n, 12n), 52n, 100n, 1n, Tour.pymoo(), True{})
86
87def oracle_dtlz2() -> Config:
88 mk(Refs.das_dennis(3n, 12n), 92n, 150n, 1n, Tour.pymoo(), True{})
89
90def get_ind(+pop: List<&2, Ind.Individual>, i: Nat) -> Ind.Individual:
91 Maybe.default(&2, Ind.Individual, List.get(&2, Ind.Individual, pop, i), Ind.from_variables(Nil{}, 1n))
92
93def rand_xs.go(+box: Bounds.Box, fuel: Nat, +i: Nat, rng: Rng.State, acc: List<&2, F32>) -> Rng.State & List<&2, F32>:
94 match fuel:
95 case 0n:
96 (rng, List.reverse(&2, F32, acc))
97 case 1n+f:
98 +iv = Bounds.at(box, i)
99 +rng2 = Rng.next_double(rng)
100 +lo = Bounds.lo(iv)
101 +hi = Bounds.hi(iv)
102 rand_xs.go(
103 box,
104 f,
105 1n+i,
106 rng2,
107 F32.add(lo, F32.mul(F32.sub(hi, lo), Rng.last_double(rng2))) <> acc
108 )
109
110def rand_xs(+box: Bounds.Box, rng: Rng.State) -> Rng.State & List<&2, F32>:
111 rand_xs.go(box, Bounds.n_var(box), 0n, rng, Nil{})
112
113type IHold is Data:
114 IH{
115 rng: Rng.State
116 acc: List<&2, Ind.Individual>
117 }
118
119def init_next.xs(+n_obj: Nat, acc: List<&2, Ind.Individual>, pair: Rng.State & List<&2, F32>) -> IHold:
120 (rng, xs) = pair
121 IH{rng, Ind.from_variables(xs, n_obj) <> acc}
122
123def init_next(+box: Bounds.Box, +n_obj: Nat, hold: IHold) -> IHold:
124 match hold:
125 case IH{rng, acc}:
126 init_next.xs(n_obj, acc, rand_xs(box, rng))
127
128def init_loop(fuel: Nat, hold: IHold, +box: Bounds.Box, +n_obj: Nat) -> Rng.State & List<&2, Ind.Individual>:
129 match fuel hold:
130 case 0n IH{rng, acc}:
131 (rng, List.reverse(&2, Ind.Individual, acc))
132 case 1n+f h:
133 init_loop(f, init_next(box, n_obj, h), box, n_obj)
134
135def init_pop(+prob: Prob.Problem, n: Nat, rng: Rng.State) -> Rng.State & List<&2, Ind.Individual>:
136 init_loop(n, IH{rng, Nil{}}, Prob.box_of(prob), Prob.n_obj(prob))
137
138def vars_of.go(xs: List<&2, Ind.Individual>, acc: List<&2, List<&2, F32>>) -> List<&2, List<&2, F32>>:
139 match xs:
140 case Nil{}:
141 List.reverse(&2, List<&2, F32>, acc)
142 case +ind <> rest:
143 vars_of.go(rest, Util.g12_vec(Ind.variables(ind)) <> acc)
144
145def vars_of(xs: List<&2, Ind.Individual>) -> List<&2, List<&2, F32>>:
146 vars_of.go(xs, Nil{})
147
148def try_add.go(+acc: List<&2, Ind.Individual>, +child: Ind.Individual, +seen: List<&2, List<&2, F32>>, +key: List<&2, F32>, skip: Bool) -> List<&2, Ind.Individual> & List<&2, List<&2, F32>> & Bool:
149 match skip:
150 case True{}:
151 (acc, seen, False{})
152 case False{}:
153 (List.append(&2, Ind.Individual, acc, [child]), key <> seen, True{})
154
155def try_add(+acc: List<&2, Ind.Individual>, +child: Ind.Individual, +seen: List<&2, List<&2, F32>>, elim: Bool) -> List<&2, Ind.Individual> & List<&2, List<&2, F32>> & Bool:
156 +key = Util.g12_vec(Ind.variables(child))
157 try_add.go(acc, child, seen, key, Bool.and(elim, Util.has_f32s(key, seen)))
158
159def pred_or_zero(n: Nat) -> Nat:
160 match n:
161 case 0n:
162 0n
163 case 1n+p:
164 p
165
166def dec_if(n: Nat, hit: Bool) -> Nat:
167 match hit:
168 case True{}:
169 pred_or_zero(n)
170 case False{}:
171 n
172
173# in_range: pair+1 < n → use `pair` (even-N sequence, and odd N before
174# the last parent). last: pair+1 == n → keep `pair` so parent N-1
175# enters SBX. Otherwise reset to 0 (cursor already past the end).
176def wrap_pair.keep(+pair: Nat, last: Bool) -> Nat:
177 match last:
178 case True{}:
179 pair
180 case False{}:
181 0n
182
183def wrap_pair.go(+pair: Nat, in_range: Bool, last: Bool) -> Nat:
184 match in_range:
185 case True{}:
186 pair
187 case False{}:
188 wrap_pair.keep(pair, last)
189
190def wrap_pair(+pair: Nat, +n: Nat) -> Nat:
191 wrap_pair.go(pair, Nat.is_lt(1n+pair, n), Nat.is_eq(1n+pair, n))
192
193# Second parent. In range: pair+1. At the end (odd N, first parent is
194# N-1): parent 0. Even N never asks for a mate past N-1.
195def pair_mate.go(+i: Nat, ok: Bool) -> Nat:
196 match ok:
197 case True{}:
198 i
199 case False{}:
200 0n
201
202def pair_mate(+p0: Nat, +n: Nat) -> Nat:
203 pair_mate.go(1n+p0, Nat.is_lt(1n+p0, n))
204
205# C# fallback cap: max(pop * 20, 1). Unique-key stage before duplicates.
206def dup_fallback_cap(n: Nat) -> Nat:
207 Util.nat_max(Nat.mul(n, 20n), 1n)
208
209type FHold is Data:
210 FH{
211 rng: Rng.State
212 acc: List<&2, Ind.Individual>
213 }
214
215def fallback_next.m(acc: List<&2, Ind.Individual>, pair: Rng.State & Ind.Individual) -> FHold:
216 (rng, extra) = pair
217 FH{rng, List.append(&2, Ind.Individual, acc, [extra])}
218
219def fallback_next(+parents: List<&2, Ind.Individual>, +prob: Prob.Problem, +mut_p: F32, hold: FHold) -> FHold:
220 match hold:
221 case FH{rng, acc}:
222 +n = Util.list_len(Ind.Individual, parents)
223 +rng_i = Rng.next_nat(rng, n)
224 fallback_next.m(acc, Poly.mutate(Poly.default(), get_ind(parents, Rng.last_nat(rng_i)), Prob.box_of(prob), rng_i, mut_p))
225
226def fallback_loop(need: Nat, hold: FHold, +parents: List<&2, Ind.Individual>, +prob: Prob.Problem, +mut_p: F32) -> Rng.State & List<&2, Ind.Individual>:
227 match need hold:
228 case 0n FH{rng, acc}:
229 (rng, acc)
230 case 1n+k h:
231 fallback_loop(k, fallback_next(parents, prob, mut_p, h), parents, prob, mut_p)
232
233def offs_add2.hit(+need1: Nat, trip: List<&2, Ind.Individual> & List<&2, List<&2, F32>> & Bool) -> Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>:
234 (acc2, seen2, hit2) = trip
235 (dec_if(need1, hit2), acc2, seen2)
236
237def offs_add2.rest(+need1: Nat, +elim: Bool, +c2: Ind.Individual, acc1: List<&2, Ind.Individual>, seen1: List<&2, List<&2, F32>>, done: Bool) -> Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>:
238 match done:
239 case True{}:
240 (0n, acc1, seen1)
241 case False{}:
242 offs_add2.hit(need1, try_add(acc1, c2, seen1, elim))
243
244def offs_add2(
245 +need: Nat,
246 acc: List<&2, Ind.Individual>,
247 seen: List<&2, List<&2, F32>>,
248 +elim: Bool,
249 +c2: Ind.Individual,
250 trip: List<&2, Ind.Individual> & List<&2, List<&2, F32>> & Bool
251) -> Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>:
252 (acc1, seen1, hit1) = trip
253 +need1 = dec_if(need, hit1)
254 offs_add2.rest(need1, elim, c2, acc1, seen1, Nat.is_eq(need1, 0n))
255
256def offs_pair.done(rng3: Rng.State, trip: Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>) -> Rng.State & Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>:
257 (need2, acc2, seen2) = trip
258 (rng3, need2, acc2, seen2)
259
260def offs_pair.m2(+need: Nat, +acc: List<&2, Ind.Individual>, +seen: List<&2, List<&2, F32>>, +elim: Bool, +m1: Ind.Individual, pair: Rng.State & Ind.Individual) -> Rng.State & Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>:
261 (rng3, m2) = pair
262 offs_pair.done(rng3, offs_add2(need, acc, seen, elim, m2, try_add(acc, m1, seen, elim)))
263
264def offs_pair.m1(+need: Nat, +acc: List<&2, Ind.Individual>, +seen: List<&2, List<&2, F32>>, +prob: Prob.Problem, +mut_p: F32, +elim: Bool, +c2: Ind.Individual, pair: Rng.State & Ind.Individual) -> Rng.State & Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>:
265 (rng2, m1) = pair
266 offs_pair.m2(need, acc, seen, elim, m1, Poly.mutate(Poly.default(), c2, Prob.box_of(prob), rng2, mut_p))
267
268def offs_pair.sbx(+need: Nat, +acc: List<&2, Ind.Individual>, +seen: List<&2, List<&2, F32>>, +prob: Prob.Problem, +mut_p: F32, +elim: Bool, trip: Rng.State & Ind.Individual & Ind.Individual) -> Rng.State & Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>:
269 (rng1, c1, c2) = trip
270 offs_pair.m1(need, acc, seen, prob, mut_p, elim, c2, Poly.mutate(Poly.default(), c1, Prob.box_of(prob), rng1, mut_p))
271
272def offs_pair(
273 +parents: List<&2, Ind.Individual>,
274 +pair: Nat,
275 +need: Nat,
276 +acc: List<&2, Ind.Individual>,
277 +seen: List<&2, List<&2, F32>>,
278 rng: Rng.State,
279 +prob: Prob.Problem,
280 +mut_p: F32,
281 +elim: Bool
282) -> Rng.State & Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>:
283 +n = Util.list_len(Ind.Individual, parents)
284 +p0 = wrap_pair(pair, n)
285 offs_pair.sbx(
286 need,
287 acc,
288 seen,
289 prob,
290 mut_p,
291 elim,
292 Sbx.crossover(Sbx.default(), get_ind(parents, p0), get_ind(parents, pair_mate(p0, n)), Prob.box_of(prob), rng)
293 )
294
295type OHold is Data:
296 OH{
297 need: Nat
298 pair: Nat
299 rng: Rng.State
300 acc: List<&2, Ind.Individual>
301 seen: List<&2, List<&2, F32>>
302 }
303
304def offs_next.q(+pair0: Nat, quad: Rng.State & Nat & List<&2, Ind.Individual> & List<&2, List<&2, F32>>) -> OHold:
305 (rng, need, acc, seen) = quad
306 OH{need, Nat.add(pair0, 2n), rng, acc, seen}
307
308def offs_next(+parents: List<&2, Ind.Individual>, +prob: Prob.Problem, +mut_p: F32, +elim: Bool, hold: OHold) -> OHold:
309 match hold:
310 case OH{need, +pair, rng, acc, seen}:
311 offs_next.q(pair, offs_pair(parents, pair, need, acc, seen, rng, prob, mut_p, elim))
312
313# Middle loop: mutation that must still be a new key. `need` is the
314# remaining slots. Cap is max(pop*20, 1), counted in `fuel`.
315type UHold is Data:
316 UH{
317 rng: Rng.State
318 acc: List<&2, Ind.Individual>
319 seen: List<&2, List<&2, F32>>
320 need: Nat
321 }
322
323def u_have(hold: UHold) -> Rng.State & List<&2, Ind.Individual>:
324 match hold:
325 case UH{rng, acc, seen, need}:
326 (rng, acc)
327
328def u_fallback(+parents: List<&2, Ind.Individual>, +prob: Prob.Problem, +mut_p: F32, hold: UHold) -> Rng.State & List<&2, Ind.Individual>:
329 match hold:
330 case UH{rng, acc, seen, need}:
331 fallback_loop(need, FH{rng, acc}, parents, prob, mut_p)
332
333def u_step.hold(+rng: Rng.State, +need: Nat, trip: List<&2, Ind.Individual> & List<&2, List<&2, F32>> & Bool) -> UHold:
334 (acc2, seen2, hit) = trip
335 UH{rng, acc2, seen2, dec_if(need, hit)}
336
337def u_step.child(+elim: Bool, +acc: List<&2, Ind.Individual>, +seen: List<&2, List<&2, F32>>, +need: Nat, +rng: Rng.State, +child: Ind.Individual) -> UHold:
338 u_step.hold(rng, need, try_add(acc, child, seen, elim))
339
340def u_step(+elim: Bool, +acc: List<&2, Ind.Individual>, +seen: List<&2, List<&2, F32>>, +need: Nat, pair: Rng.State & Ind.Individual) -> UHold:
341 (rng, child) = pair
342 u_step.child(elim, acc, seen, need, rng, child)
343
344def u_next(+parents: List<&2, Ind.Individual>, +prob: Prob.Problem, +mut_p: F32, +elim: Bool, hold: UHold) -> UHold:
345 match hold:
346 case UH{rng, acc, seen, need}:
347 +n = Util.list_len(Ind.Individual, parents)
348 +rng_i = Rng.next_nat(rng, n)
349 u_step(elim, acc, seen, need, Poly.mutate(Poly.default(), get_ind(parents, Rng.last_nat(rng_i)), Prob.box_of(prob), rng_i, mut_p))
350
351def u_loop(
352 fuel: Nat,
353 hold: UHold,
354 +parents: List<&2, Ind.Individual>,
355 +prob: Prob.Problem,
356 +mut_p: F32,
357 +elim: Bool
358) -> Rng.State & List<&2, Ind.Individual>:
359 match fuel hold:
360 case 0n h:
361 u_fallback(parents, prob, mut_p, h)
362 case 1n+f UH{rng, acc, seen, 0n}:
363 u_have(UH{rng, acc, seen, 0n})
364 case 1n+f UH{rng, acc, seen, 1n+k}:
365 u_loop(f, u_next(parents, prob, mut_p, elim, UH{rng, acc, seen, 1n+k}), parents, prob, mut_p, elim)
366
367def offs_done(+parents: List<&2, Ind.Individual>, +prob: Prob.Problem, +mut_p: F32, +elim: Bool, +n: Nat, hold: OHold) -> Rng.State & List<&2, Ind.Individual>:
368 match hold:
369 case OH{nrest, pair, rng, acc, seen}:
370 u_loop(dup_fallback_cap(n), UH{rng, acc, seen, nrest}, parents, prob, mut_p, elim)
371
372def offs_have(hold: OHold) -> Rng.State & List<&2, Ind.Individual>:
373 match hold:
374 case OH{need, pair, rng, acc, seen}:
375 (rng, acc)
376
377def offs_loop(
378 fuel: Nat,
379 hold: OHold,
380 +parents: List<&2, Ind.Individual>,
381 +prob: Prob.Problem,
382 +mut_p: F32,
383 +elim: Bool,
384 +n: Nat
385) -> Rng.State & List<&2, Ind.Individual>:
386 match fuel hold:
387 case 0n h:
388 offs_done(parents, prob, mut_p, elim, n, h)
389 case 1n+f OH{0n, pair, rng, acc, seen}:
390 offs_have(OH{0n, pair, rng, acc, seen})
391 case 1n+f OH{1n+k, pair, rng, acc, seen}:
392 offs_loop(f, offs_next(parents, prob, mut_p, elim, OH{1n+k, pair, rng, acc, seen}), parents, prob, mut_p, elim, n)
393
394# Hash the survivor population (not the mating sample). An individual
395# who lost every tournament is still a duplicate of P.
396def seen_start(+survivors: List<&2, Ind.Individual>, elim: Bool) -> List<&2, List<&2, F32>>:
397 match elim:
398 case True{}:
399 vars_of(survivors)
400 case False{}:
401 Nil{}
402
403def create_offspring(
404 +prob: Prob.Problem,
405 +survivors: List<&2, Ind.Individual>,
406 +parents: List<&2, Ind.Individual>,
407 +n: Nat,
408 rng: Rng.State,
409 +elim: Bool
410) -> Rng.State & List<&2, Ind.Individual>:
411 offs_loop(
412 Nat.mul(n, 40n),
413 OH{n, 0n, rng, Nil{}, seen_start(survivors, elim)},
414 parents,
415 prob,
416 Poly.typical_probability(Prob.n_var(prob)),
417 elim,
418 n
419 )
420
421def nd_front(+pop: List<&2, Ind.Individual>) -> List<&2, Ind.Individual>:
422 Surv.gather(pop, Surv.front0(NDS.sort(pop)))
423
424def emit_front(xs: List<&2, Ind.Individual>) -> IO(Unit):
425 match xs:
426 case Nil{}:
427 do IO<Unit>:
428 return Unit{}
429 case ind <> rest:
430 do IO<Unit>:
431 IO.print(Util.show_f32s(Ind.objectives(ind)))
432 emit_front(rest)
433
434type GHold is Data:
435 GH{
436 st: Norm.State
437 xs: List<&2, Ind.Individual>
438 rng: Rng.State
439 }
440
441def one_gen.prep(+rng: Rng.State, pair: Norm.State & List<&2, Ind.Individual>) -> GHold:
442 (st, xs) = pair
443 GH{st, xs, rng}
444
445def one_gen.s(+prob: Prob.Problem, +cfg: Config, trip: Norm.State & List<&2, Ind.Individual> & Rng.State) -> GHold:
446 (st, xs, rng) = trip
447 one_gen.prep(rng, Tour.prepare(xs, cfg_dirs(cfg), st))
448
449def one_gen.o(+prob: Prob.Problem, +cfg: Config, +pop: List<&2, Ind.Individual>, +state: Norm.State, trip: Rng.State & List<&2, Ind.Individual>) -> GHold:
450 (rng2, offs) = trip
451 one_gen.s(
452 prob,
453 cfg,
454 Surv.select_st(
455 List.append(&2, Ind.Individual, pop, Prob.evaluate_all(prob, offs)),
456 cfg_pop(cfg),
457 cfg_dirs(cfg),
458 state,
459 rng2
460 )
461 )
462
463def one_gen.p(+prob: Prob.Problem, +cfg: Config, +pop: List<&2, Ind.Individual>, +state: Norm.State, trip: Rng.State & List<&2, Ind.Individual>) -> GHold:
464 (rng1, parents) = trip
465 one_gen.o(prob, cfg, pop, state, create_offspring(prob, pop, parents, cfg_pop(cfg), rng1, cfg_elim(cfg)))
466
467def one_gen(+prob: Prob.Problem, +cfg: Config, hold: GHold) -> GHold:
468 match hold:
469 case GH{st, +xs, rng}:
470 one_gen.p(prob, cfg, xs, st, Tour.select_parents(cfg_mode(cfg), xs, cfg_pop(cfg), rng))
471
472def gen_pop(hold: GHold) -> List<&2, Ind.Individual>:
473 match hold:
474 case GH{st, xs, rng}:
475 xs
476
477def gen_loop(fuel: Nat, hold: GHold, +prob: Prob.Problem, +cfg: Config) -> List<&2, Ind.Individual>:
478 match fuel hold:
479 case 0n h:
480 gen_pop(h)
481 case 1n+g h:
482 gen_loop(g, one_gen(prob, cfg, h), prob, cfg)
483
484def run.after_prep(+prob: Prob.Problem, +cfg: Config, rng: Rng.State, pair: Norm.State & List<&2, Ind.Individual>) -> List<&2, Ind.Individual>:
485 (st1, pop1) = pair
486 gen_loop(cfg_gens(cfg), GH{st1, pop1, rng}, prob, cfg)
487
488def run.go(+prob: Prob.Problem, +cfg: Config, pair: Rng.State & List<&2, Ind.Individual>) -> List<&2, Ind.Individual>:
489 (rng, raw) = pair
490 run.after_prep(prob, cfg, rng, Tour.prepare(Prob.evaluate_all(prob, raw), cfg_dirs(cfg), Norm.new_state(Prob.n_obj(prob))))
491
492def run.sized(+prob: Prob.Problem, +cfg: Config, n: Nat) -> List<&2, Ind.Individual>:
493 match n:
494 case 0n:
495 Nil{}
496 case 1n+p:
497 run.go(prob, cfg, init_pop(prob, 1n+p, Rng.from_nat(cfg_seed(cfg))))
498
499def run(+prob: Prob.Problem, +cfg: Config) -> List<&2, Ind.Individual>:
500 +n = cfg_pop(cfg)
501 Util.list_take_pad(Ind.Individual, Ind.from_objectives(Nil{}), run.sized(prob, cfg, n), n)
502
503def run_zdt1_smoke() -> List<&2, Ind.Individual>:
504 run(Prob.zdt1(30n), smoke_zdt1())