1 # Named problems — C# Unsga3.Problems. 2 # ZDT1 / ZDT2 / DTLZ2 stay the oracle problems. The rest of the unconstrained 3 # catalog (ZDT3 / ZDT4 / ZDT6, DTLZ1 / DTLZ3 / DTLZ4 / DTLZ7, Sphere / Ackley / 4 # Rosenbrock) matches the same C# Evaluate formulas. Minimize. 5 # OSY / TNK / C1-DTLZ1 match ConstrainedProblems.cs (g ≤ 0, positive = violation). 6 # Unconstrained kinds write an empty constraint vector (cv 0). No IGD here. 7 # `alpha` is DTLZ4's exponent (C# default 100). Other kinds ignore it. 8 9 import Base 10 import ./individual.bend as Ind 11 import ./bounds.bend as Bounds 12 import ./util.bend as Util 13 14 type Family is Data: 15 Zdt1{} 16 Zdt2{} 17 Zdt3{} 18 Zdt4{} 19 Zdt6{} 20 Dtlz1{} 21 Dtlz2{} 22 Dtlz3{} 23 Dtlz4{} 24 Dtlz7{} 25 Sphere{} 26 Ackley{} 27 Rosenbrock{} 28 Osy{} 29 Tnk{} 30 C1Dtlz1{} 31 32 type Problem is Data: 33 MkProblem{ 34 kind: Family 35 box: Bounds.Box 36 alpha: F32 37 } 38 39 def box_of(p: Problem) -> Bounds.Box: 40 match p: 41 case MkProblem{kind, box, alpha}: 42 box 43 44 def kind_of(p: Problem) -> Family: 45 match p: 46 case MkProblem{kind, box, alpha}: 47 kind 48 49 def alpha_of(p: Problem) -> F32: 50 match p: 51 case MkProblem{kind, box, alpha}: 52 alpha 53 54 def n_obj(p: Problem) -> Nat: 55 Bounds.n_obj(box_of(p)) 56 57 def n_var(p: Problem) -> Nat: 58 Bounds.n_var(box_of(p)) 59 60 def mk_prob(kind: Family, box: Bounds.Box) -> Problem: 61 MkProblem{kind, box, 0.0} 62 63 # ZDT1 / ZDT2 / ZDT3: n variables (C# default 30), 2 objectives, x ∈ [0,1]^n. 64 def zdt1(n: Nat) -> Problem: 65 mk_prob(Zdt1{}, Bounds.unit_n(n, 2n)) 66 67 def zdt2(n: Nat) -> Problem: 68 mk_prob(Zdt2{}, Bounds.unit_n(n, 2n)) 69 70 def zdt3(n: Nat) -> Problem: 71 mk_prob(Zdt3{}, Bounds.unit_n(n, 2n)) 72 73 # ZDT4: C# default n=10. x0 ∈ [0,1], x1.. ∈ [-5,5]. 74 def zdt4_ivs(n: Nat) -> List<&2, Bounds.Interval>: 75 match n: 76 case 0n: 77 Nil{} 78 case 1n+p: 79 Bounds.interval(0.0, 1.0) <> List.replicate(Bounds.Interval, p, Bounds.interval(F32.neg(5.0), 5.0)) 80 81 def zdt4(n: Nat) -> Problem: 82 mk_prob(Zdt4{}, Bounds.box(2n, zdt4_ivs(n))) 83 84 # ZDT6: C# default n=10, x ∈ [0,1]^n. 85 def zdt6(n: Nat) -> Problem: 86 mk_prob(Zdt6{}, Bounds.unit_n(n, 2n)) 87 88 # n = M + k - 1 (C# Dtlz*Problem). 89 def dtlz_n(m: Nat, k: Nat) -> Nat: 90 match m: 91 case 0n: 92 k 93 case 1n+pm: 94 match k: 95 case 0n: 96 pm 97 case 1n+pk: 98 Nat.add(pm, 1n+pk) 99 100 # DTLZ2: C# default M=3, k=10 ⇒ n=12, x ∈ [0,1]^n. 101 def dtlz2(+m: Nat, k: Nat) -> Problem: 102 mk_prob(Dtlz2{}, Bounds.unit_n(dtlz_n(m, k), m)) 103 104 # DTLZ1: C# default M=3, k=5 ⇒ n=7. 105 def dtlz1(+m: Nat, k: Nat) -> Problem: 106 mk_prob(Dtlz1{}, Bounds.unit_n(dtlz_n(m, k), m)) 107 108 # DTLZ3: C# default M=3, k=10. DTLZ2 map, DTLZ1 g. 109 def dtlz3(+m: Nat, k: Nat) -> Problem: 110 mk_prob(Dtlz3{}, Bounds.unit_n(dtlz_n(m, k), m)) 111 112 # DTLZ4: C# default α=100, k=10. 113 def dtlz4_alpha(+m: Nat, k: Nat, +alpha: F32) -> Problem: 114 MkProblem{Dtlz4{}, Bounds.unit_n(dtlz_n(m, k), m), alpha} 115 116 def dtlz4(+m: Nat, k: Nat) -> Problem: 117 dtlz4_alpha(m, k, 100.0) 118 119 # DTLZ7: C# default M=3, k=20 ⇒ n=22. 120 def dtlz7(+m: Nat, k: Nat) -> Problem: 121 mk_prob(Dtlz7{}, Bounds.unit_n(dtlz_n(m, k), m)) 122 123 # Single-objective. M=1. C# defaults: Sphere n=10, Ackley n=30, Rosenbrock n=10. 124 def sphere(n: Nat) -> Problem: 125 mk_prob(Sphere{}, Bounds.span_n(n, 1n, F32.neg(5.12), 5.12)) 126 127 def ackley(n: Nat) -> Problem: 128 mk_prob(Ackley{}, Bounds.span_n(n, 1n, F32.neg(32.768), 32.768)) 129 130 def rosenbrock(n: Nat) -> Problem: 131 mk_prob(Rosenbrock{}, Bounds.span_n(n, 1n, F32.neg(2.048), 2.048)) 132 133 # OSY: 6 variables, 2 objectives, 6 inequalities. pymoo 0.6 scaling. 134 def osy() -> Problem: 135 mk_prob( 136 Osy{}, 137 Bounds.box( 138 2n, 139 Bounds.interval(0.0, 10.0) 140 <> Bounds.interval(0.0, 10.0) 141 <> Bounds.interval(1.0, 5.0) 142 <> Bounds.interval(0.0, 6.0) 143 <> Bounds.interval(1.0, 5.0) 144 <> [Bounds.interval(0.0, 10.0)] 145 ) 146 ) 147 148 # TNK: [0, π] × [1e-30, π]. Disk constraint uses pymoo's factor of two. 149 def tnk() -> Problem: 150 mk_prob( 151 Tnk{}, 152 Bounds.box(2n, Bounds.interval(0.0, F32.pi()) <> [Bounds.interval(1.0e-30, F32.pi())]) 153 ) 154 155 # C1-DTLZ1: DTLZ1 plus one inequality. C# default M=3, k=5 (n = M+k-1). 156 def c1_dtlz1(+m: Nat, k: Nat) -> Problem: 157 mk_prob(C1Dtlz1{}, Bounds.unit_n(dtlz_n(m, k), m)) 158 159 def sum_from(xs: List<&2, F32>) -> F32: 160 match xs: 161 case Nil{}: 162 0.0 163 case h <> t: 164 F32.add(h, sum_from(t)) 165 166 def sum_tail(xs: List<&2, F32>) -> F32: 167 match xs: 168 case Nil{}: 169 0.0 170 case h <> t: 171 sum_from(t) 172 173 def list_tail(xs: List<&2, F32>) -> List<&2, F32>: 174 match xs: 175 case Nil{}: 176 Nil{} 177 case h <> t: 178 t 179 180 def zdt_den(n: Nat) -> F32: 181 match n: 182 case 0n: 183 1.0 184 case 1n: 185 1.0 186 case 1n+p: 187 F32.from_nat(p) 188 189 # C# Zdt1Problem.GLinear: 1 + 9 * sum(x[1..]) / (n - 1). 190 def zdt_g(+x: List<&2, F32>) -> F32: 191 +n = Util.list_len(F32, x) 192 F32.add(1.0, F32.div(F32.mul(9.0, sum_tail(x)), zdt_den(n))) 193 194 def zdt1_f(+x: List<&2, F32>) -> List<&2, F32>: 195 +x0 = Util.get_f32(x, 0n) 196 +g = zdt_g(x) 197 x0 <> [F32.mul(g, F32.sub(1.0, F32.sqrt(F32.div(x0, g))))] 198 199 def zdt2_f(+x: List<&2, F32>) -> List<&2, F32>: 200 +x0 = Util.get_f32(x, 0n) 201 +g = zdt_g(x) 202 +r = F32.div(x0, g) 203 x0 <> [F32.mul(g, F32.sub(1.0, F32.mul(r, r)))] 204 205 # C# Zdt3Problem: g * (1 - sqrt(r) - r * sin(10 π x0)), r = x0/g. 206 def zdt3_f(+x: List<&2, F32>) -> List<&2, F32>: 207 +x0 = Util.get_f32(x, 0n) 208 +g = zdt_g(x) 209 +r = F32.div(x0, g) 210 +s = F32.sin(F32.mul(F32.mul(10.0, F32.pi()), x0)) 211 x0 <> [F32.mul(g, F32.sub(F32.sub(1.0, F32.sqrt(r)), F32.mul(r, s)))] 212 213 def zdt4_term(+xi: F32) -> F32: 214 F32.sub(F32.square(xi), F32.mul(10.0, F32.cos(F32.mul(F32.mul(4.0, F32.pi()), xi)))) 215 216 def sum_zdt4(xs: List<&2, F32>) -> F32: 217 match xs: 218 case Nil{}: 219 0.0 220 case h <> t: 221 F32.add(zdt4_term(h), sum_zdt4(t)) 222 223 def pred(n: Nat) -> Nat: 224 match n: 225 case 0n: 226 0n 227 case 1n+p: 228 p 229 230 # C# Zdt4Problem: 1 + 10*(n-1) + Σ_{i≥1} (x_i² - 10 cos(4 π x_i)). 231 def zdt4_g(+x: List<&2, F32>) -> F32: 232 +n = Util.list_len(F32, x) 233 F32.add(F32.add(1.0, F32.mul(10.0, F32.from_nat(pred(n)))), sum_zdt4(list_tail(x))) 234 235 def zdt4_f(+x: List<&2, F32>) -> List<&2, F32>: 236 +x0 = Util.get_f32(x, 0n) 237 +g = zdt4_g(x) 238 x0 <> [F32.mul(g, F32.sub(1.0, F32.sqrt(F32.div(x0, g))))] 239 240 # C# Zdt6Problem: biased density. g = 1 + 9 * (mean of x[1..])^0.25. 241 def zdt6_f(+x: List<&2, F32>) -> List<&2, F32>: 242 +x0 = Util.get_f32(x, 0n) 243 +wave = F32.pow(F32.sin(F32.mul(F32.mul(6.0, F32.pi()), x0)), 6.0) 244 +f0 = F32.sub(1.0, F32.mul(F32.exp(F32.mul(F32.neg(4.0), x0)), wave)) 245 +n = Util.list_len(F32, x) 246 +g = F32.add(1.0, F32.mul(9.0, F32.pow(F32.div(sum_tail(x), zdt_den(n)), 0.25))) 247 +r = F32.div(f0, g) 248 f0 <> [F32.mul(g, F32.sub(1.0, F32.mul(r, r)))] 249 250 def sum_sq_half(xs: List<&2, F32>) -> F32: 251 match xs: 252 case Nil{}: 253 0.0 254 case h <> t: 255 F32.add(F32.square(F32.sub(h, 0.5)), sum_sq_half(t)) 256 257 def tail_from_m(+x: List<&2, F32>, m: Nat) -> List<&2, F32>: 258 List.drop(&2, F32, x, pred(m)) 259 260 # C# DtlzHelper.GDtlz2: sum (x[i] - 0.5)^2 for i >= M-1. 261 def dtlz2_g(+x: List<&2, F32>, m: Nat) -> F32: 262 sum_sq_half(tail_from_m(x, m)) 263 264 def dtlz1_term(+xi: F32) -> F32: 265 +d = F32.sub(xi, 0.5) 266 F32.sub(F32.square(d), F32.cos(F32.mul(F32.mul(20.0, F32.pi()), d))) 267 268 def sum_dtlz1(xs: List<&2, F32>) -> F32: 269 match xs: 270 case Nil{}: 271 0.0 272 case h <> t: 273 F32.add(dtlz1_term(h), sum_dtlz1(t)) 274 275 # C# DtlzHelper.GDtlz1: 100 * ((n - M + 1) + Σ (d² - cos(20 π d))), d = x-0.5. 276 # On a legal k≥1 box the tail length is n-M+1, which is that coefficient. 277 def dtlz1_g(+x: List<&2, F32>, m: Nat) -> F32: 278 +tail = tail_from_m(x, m) 279 F32.mul(100.0, F32.add(F32.from_nat(Util.list_len(F32, tail)), sum_dtlz1(tail))) 280 281 def half_pi() -> F32: 282 F32.div(F32.pi(), 2.0) 283 284 def prod_cos.go(+x: List<&2, F32>, fuel: Nat, +i: Nat, acc: F32) -> F32: 285 match fuel: 286 case 0n: 287 acc 288 case 1n+f: 289 prod_cos.go( 290 x, 291 f, 292 1n+i, 293 F32.mul(acc, F32.cos(F32.mul(Util.get_f32(x, i), half_pi()))) 294 ) 295 296 def prod_cos(+x: List<&2, F32>, n: Nat) -> F32: 297 prod_cos.go(x, n, 0n, 1.0) 298 299 def dtlz2_sin(+x: List<&2, F32>, +m: Nat, +i: Nat) -> F32: 300 F32.sin(F32.mul(Util.get_f32(x, pred(Nat.sub(m, i))), half_pi())) 301 302 def dtlz2_fi.tail(+x: List<&2, F32>, +m: Nat, +g: F32, +i: Nat, +body: F32, first: Bool) -> F32: 303 match first: 304 case True{}: 305 body 306 case False{}: 307 F32.mul(body, dtlz2_sin(x, m, i)) 308 309 # C# Dtlz2Problem: (1+g) * prod_{j0]. 310 def dtlz2_fi(+x: List<&2, F32>, +m: Nat, +g: F32, +i: Nat) -> F32: 311 dtlz2_fi.tail( 312 x, 313 m, 314 g, 315 i, 316 F32.mul(F32.add(1.0, g), prod_cos(x, pred(Nat.sub(m, i)))), 317 Nat.is_eq(i, 0n) 318 ) 319 320 def dtlz2_fs.go(+x: List<&2, F32>, +m: Nat, +g: F32, fuel: Nat, +i: Nat) -> List<&2, F32>: 321 match fuel: 322 case 0n: 323 Nil{} 324 case 1n+f: 325 dtlz2_fi(x, m, g, i) <> dtlz2_fs.go(x, m, g, f, 1n+i) 326 327 def dtlz2_f(+x: List<&2, F32>, +m: Nat) -> List<&2, F32>: 328 dtlz2_fs.go(x, m, dtlz2_g(x, m), m, 0n) 329 330 def prod_xs.go(+x: List<&2, F32>, fuel: Nat, +i: Nat, acc: F32) -> F32: 331 match fuel: 332 case 0n: 333 acc 334 case 1n+f: 335 prod_xs.go(x, f, 1n+i, F32.mul(acc, Util.get_f32(x, i))) 336 337 def prod_xs(+x: List<&2, F32>, n: Nat) -> F32: 338 prod_xs.go(x, n, 0n, 1.0) 339 340 def dtlz1_scale(+body: F32, +x: List<&2, F32>, +idx: Nat, first: Bool) -> F32: 341 match first: 342 case True{}: 343 body 344 case False{}: 345 F32.mul(body, F32.sub(1.0, Util.get_f32(x, idx))) 346 347 # C# Dtlz1Problem: 0.5*(1+g) * prod_{j0]. 348 def dtlz1_fi(+x: List<&2, F32>, +m: Nat, +g: F32, +i: Nat) -> F32: 349 +idx = pred(Nat.sub(m, i)) 350 dtlz1_scale(F32.mul(F32.mul(0.5, F32.add(1.0, g)), prod_xs(x, idx)), x, idx, Nat.is_eq(i, 0n)) 351 352 def dtlz1_fs.go(+x: List<&2, F32>, +m: Nat, +g: F32, fuel: Nat, +i: Nat) -> List<&2, F32>: 353 match fuel: 354 case 0n: 355 Nil{} 356 case 1n+f: 357 dtlz1_fi(x, m, g, i) <> dtlz1_fs.go(x, m, g, f, 1n+i) 358 359 def dtlz1_f(+x: List<&2, F32>, +m: Nat) -> List<&2, F32>: 360 dtlz1_fs.go(x, m, dtlz1_g(x, m), m, 0n) 361 362 # F32 cos(π/2) is -4.371139e-8, not 0. The DTLZ2 map multiplies that 363 # factor by (1+g). DTLZ3's g stays large on local fronts, so an in-bound 364 # x=1 (SBX / polynomial mutation clamp to the box) gets a strictly 365 # negative objective. Every feasible point has f≥0, so nothing dominates 366 # that row. It stays on the ND front and pins an ASF extreme at (1+g). 367 # DTLZ2's (1+g) is ~1, so the same raw trig is left alone (published 368 # fronts). Clamp only this map back to the mathematical range f≥0. 369 def nn_obj(+v: F32) -> F32: 370 Bool.pick(F32, F32.is_lt(v, 0.0), 0.0, v) 371 372 def nn_objs(xs: List<&2, F32>) -> List<&2, F32>: 373 match xs: 374 case Nil{}: 375 Nil{} 376 case h <> t: 377 nn_obj(h) <> nn_objs(t) 378 379 # C# Dtlz3Problem: DTLZ2 map with DTLZ1's g. 380 def dtlz3_f(+x: List<&2, F32>, +m: Nat) -> List<&2, F32>: 381 nn_objs(dtlz2_fs.go(x, m, dtlz1_g(x, m), m, 0n)) 382 383 # Fuel shrinks first so the recursive call terminates. The g-tail stays raw. 384 def map_prefix_pow.go(fuel: Nat, xs: List<&2, F32>, +alpha: F32) -> List<&2, F32>: 385 match fuel: 386 case 0n: 387 xs 388 case 1n+f: 389 match xs: 390 case Nil{}: 391 Nil{} 392 case h <> t: 393 F32.pow(h, alpha) <> map_prefix_pow.go(f, t, alpha) 394 395 # Raise the first `fuel` decision variables to α. 396 def map_prefix_pow(+xs: List<&2, F32>, fuel: Nat, +alpha: F32) -> List<&2, F32>: 397 map_prefix_pow.go(fuel, xs, alpha) 398 399 # C# Dtlz4Problem: DTLZ2 with x^α inside the sine/cosine map. g is still GDtlz2. 400 def dtlz4_f(+x: List<&2, F32>, +m: Nat, +alpha: F32) -> List<&2, F32>: 401 dtlz2_f(map_prefix_pow(x, pred(m), alpha), m) 402 403 def pos_or.fin(+x: F32, +fallback: F32, ok: Bool) -> F32: 404 match ok: 405 case True{}: 406 x 407 case False{}: 408 fallback 409 410 def pos_or(+x: F32, +fallback: F32) -> F32: 411 pos_or.fin(x, fallback, F32.is_gt(x, 0.0)) 412 413 # C# Dtlz7Problem g: 1 + 9 * sum(x[M-1..]) / (n - M + 1). 414 def dtlz7_g(+x: List<&2, F32>, m: Nat) -> F32: 415 +tail = tail_from_m(x, m) 416 +den = pos_or(F32.from_nat(Util.list_len(F32, tail)), 1.0) 417 F32.add(1.0, F32.div(F32.mul(9.0, sum_from(tail)), den)) 418 419 def dtlz7_h.go(+x: List<&2, F32>, +g1: F32, fuel: Nat, +i: Nat, acc: F32) -> F32: 420 match fuel: 421 case 0n: 422 acc 423 case 1n+f: 424 +fi = Util.get_f32(x, i) 425 +term = F32.mul(F32.div(fi, g1), F32.add(1.0, F32.sin(F32.mul(F32.mul(3.0, F32.pi()), fi)))) 426 dtlz7_h.go(x, g1, f, 1n+i, F32.add(acc, term)) 427 428 def dtlz7_prefix(+x: List<&2, F32>, fuel: Nat, +i: Nat) -> List<&2, F32>: 429 match fuel: 430 case 0n: 431 Nil{} 432 case 1n+f: 433 Util.get_f32(x, i) <> dtlz7_prefix(x, f, 1n+i) 434 435 # f[i]=x[i] for i, +m: Nat) -> List<&2, F32>: 437 match m: 438 case 0n: 439 Nil{} 440 case 1n+p: 441 +g = dtlz7_g(x, m) 442 +g1 = F32.add(1.0, g) 443 +h = F32.sub(F32.from_nat(m), dtlz7_h.go(x, g1, p, 0n, 0.0)) 444 List.append(&2, F32, dtlz7_prefix(x, p, 0n), [F32.mul(g1, h)]) 445 446 def sum_sq(xs: List<&2, F32>) -> F32: 447 match xs: 448 case Nil{}: 449 0.0 450 case h <> t: 451 F32.add(F32.square(h), sum_sq(t)) 452 453 def sphere_f(+x: List<&2, F32>) -> List<&2, F32>: 454 [sum_sq(x)] 455 456 type Acc2 is Data: 457 Acc2{ 458 s1: F32 459 s2: F32 460 } 461 462 def ackley_go(xs: List<&2, F32>, +s1: F32, +s2: F32) -> Acc2: 463 match xs: 464 case Nil{}: 465 Acc2{s1, s2} 466 case +h <> t: 467 ackley_go( 468 t, 469 F32.add(s1, F32.square(h)), 470 F32.add(s2, F32.cos(F32.mul(F32.mul(2.0, F32.pi()), h))) 471 ) 472 473 def ackley_den(n: Nat) -> F32: 474 match n: 475 case 0n: 476 1.0 477 case 1n+p: 478 F32.from_nat(1n+p) 479 480 def ackley_from(+n: Nat, acc: Acc2) -> List<&2, F32>: 481 match acc: 482 case Acc2{s1, s2}: 483 +den = ackley_den(n) 484 [F32.add( 485 F32.add( 486 F32.mul(F32.neg(20.0), F32.exp(F32.mul(F32.neg(0.2), F32.sqrt(F32.div(s1, den))))), 487 F32.neg(F32.exp(F32.div(s2, den))) 488 ), 489 F32.add(20.0, F32.exp(1.0)) 490 )] 491 492 # C# AckleyProblem. n=0 stays total (denominator 1) instead of dividing by zero. 493 def ackley_f(+x: List<&2, F32>) -> List<&2, F32>: 494 ackley_from(Util.list_len(F32, x), ackley_go(x, 0.0, 0.0)) 495 496 def rosen_step(rest: List<&2, F32>, +a: F32) -> F32: 497 match rest: 498 case Nil{}: 499 0.0 500 case +b <> t: 501 +diff = F32.sub(b, F32.square(a)) 502 +one = F32.sub(1.0, a) 503 F32.add(F32.add(F32.mul(100.0, F32.square(diff)), F32.square(one)), rosen_step(t, b)) 504 505 def rosenbrock_f(+x: List<&2, F32>) -> List<&2, F32>: 506 match x: 507 case Nil{}: 508 [0.0] 509 case a <> rest: 510 [rosen_step(rest, a)] 511 512 # Objectives plus g ≤ 0. Unconstrained kinds leave g empty. 513 type Scored is Data: 514 Scored{ 515 f: List<&2, F32> 516 g: List<&2, F32> 517 } 518 519 def plain(+f: List<&2, F32>) -> Scored: 520 Scored{f, Nil{}} 521 522 def scored_f(s: Scored) -> List<&2, F32>: 523 match s: 524 case Scored{f, g}: 525 f 526 527 def scored_g(s: Scored) -> List<&2, F32>: 528 match s: 529 case Scored{f, g}: 530 g 531 532 # C# OsyProblem. f0 negates the weighted squares (x5 is not in f0). 533 def osy_f(+x: List<&2, F32>) -> List<&2, F32>: 534 +x0 = Util.get_f32(x, 0n) 535 +x1 = Util.get_f32(x, 1n) 536 +x2 = Util.get_f32(x, 2n) 537 +x3 = Util.get_f32(x, 3n) 538 +x4 = Util.get_f32(x, 4n) 539 +pen = F32.add( 540 F32.mul(25.0, F32.square(F32.sub(x0, 2.0))), 541 F32.add( 542 F32.square(F32.sub(x1, 2.0)), 543 F32.add( 544 F32.square(F32.sub(x2, 1.0)), 545 F32.add(F32.square(F32.sub(x3, 4.0)), F32.square(F32.sub(x4, 1.0))) 546 ) 547 ) 548 ) 549 F32.neg(pen) <> [sum_sq(x)] 550 551 # pymoo divisors, then negated so a positive value is a violation. 552 def osy_g(+x: List<&2, F32>) -> List<&2, F32>: 553 +x0 = Util.get_f32(x, 0n) 554 +x1 = Util.get_f32(x, 1n) 555 +x2 = Util.get_f32(x, 2n) 556 +x3 = Util.get_f32(x, 3n) 557 +x4 = Util.get_f32(x, 4n) 558 +x5 = Util.get_f32(x, 5n) 559 F32.neg(F32.div(F32.sub(F32.add(x0, x1), 2.0), 2.0)) 560 <> F32.neg(F32.div(F32.sub(F32.sub(6.0, x0), x1), 6.0)) 561 <> F32.neg(F32.div(F32.add(F32.sub(2.0, x1), x0), 2.0)) 562 <> F32.neg(F32.div(F32.add(F32.sub(2.0, x0), F32.mul(3.0, x1)), 2.0)) 563 <> F32.neg(F32.div(F32.sub(F32.sub(4.0, F32.square(F32.sub(x2, 3.0))), x3), 4.0)) 564 <> [F32.neg(F32.div(F32.sub(F32.add(F32.square(F32.sub(x4, 3.0)), x5), 4.0), 4.0))] 565 566 def osy_score(+x: List<&2, F32>) -> Scored: 567 Scored{osy_f(x), osy_g(x)} 568 569 # C# TnkProblem. g0 = -(x²+y²-1-0.1 cos(16 atan(x/y))). g1 = 2((x-0.5)²+(y-0.5)²)-1. 570 def tnk_g(+x: List<&2, F32>) -> List<&2, F32>: 571 +x0 = Util.get_f32(x, 0n) 572 +x1 = Util.get_f32(x, 1n) 573 +circle = F32.sub( 574 F32.sub(F32.add(F32.square(x0), F32.square(x1)), 1.0), 575 F32.mul(0.1, F32.cos(F32.mul(16.0, F32.atan(F32.div(x0, x1))))) 576 ) 577 +dx = F32.sub(x0, 0.5) 578 +dy = F32.sub(x1, 0.5) 579 F32.neg(circle) <> [F32.sub(F32.mul(2.0, F32.add(F32.square(dx), F32.square(dy))), 1.0)] 580 581 def tnk_score(+x: List<&2, F32>) -> Scored: 582 Scored{Util.get_f32(x, 0n) <> [Util.get_f32(x, 1n)], tnk_g(x)} 583 584 # Last objective / 0.6, earlier objectives / 0.5. fuel starts at |f|. 585 def c1_w(fuel: Nat, fs: List<&2, F32>, +acc: F32) -> F32: 586 match fuel: 587 case 0n: 588 acc 589 case 1n: 590 match fs: 591 case Nil{}: 592 acc 593 case +h <> rest: 594 F32.add(acc, F32.div(h, 0.6)) 595 case 1n+f: 596 match fs: 597 case Nil{}: 598 acc 599 case +h <> rest: 600 c1_w(f, rest, F32.add(acc, F32.div(h, 0.5))) 601 602 def c1_g(+fs: List<&2, F32>) -> List<&2, F32>: 603 [F32.sub(c1_w(Util.list_len(F32, fs), fs, 0.0), 1.0)] 604 605 def c1_score(+x: List<&2, F32>, +m: Nat) -> Scored: 606 +f = dtlz1_f(x, m) 607 Scored{f, c1_g(f)} 608 609 def eval_kind(kind: Family, +x: List<&2, F32>, +m: Nat, +alpha: F32) -> Scored: 610 match kind: 611 case Zdt1{}: 612 plain(zdt1_f(x)) 613 case Zdt2{}: 614 plain(zdt2_f(x)) 615 case Zdt3{}: 616 plain(zdt3_f(x)) 617 case Zdt4{}: 618 plain(zdt4_f(x)) 619 case Zdt6{}: 620 plain(zdt6_f(x)) 621 case Dtlz1{}: 622 plain(dtlz1_f(x, m)) 623 case Dtlz2{}: 624 plain(dtlz2_f(x, m)) 625 case Dtlz3{}: 626 plain(dtlz3_f(x, m)) 627 case Dtlz4{}: 628 plain(dtlz4_f(x, m, alpha)) 629 case Dtlz7{}: 630 plain(dtlz7_f(x, m)) 631 case Sphere{}: 632 plain(sphere_f(x)) 633 case Ackley{}: 634 plain(ackley_f(x)) 635 case Rosenbrock{}: 636 plain(rosenbrock_f(x)) 637 case Osy{}: 638 osy_score(x) 639 case Tnk{}: 640 tnk_score(x) 641 case C1Dtlz1{}: 642 c1_score(x, m) 643 644 def apply_score(+ind: Ind.Individual, s: Scored) -> Ind.Individual: 645 match s: 646 case Scored{f, g}: 647 Ind.set_constraints(Ind.set_objectives(ind, f), g) 648 649 def evaluate(+p: Problem, +ind: Ind.Individual) -> Ind.Individual: 650 apply_score(ind, eval_kind(kind_of(p), Ind.variables(ind), n_obj(p), alpha_of(p))) 651 652 # Independent per individual. Mid-split + parallel recurse; same order as cons. 653 def evaluate_all(+p: Problem, +xs: List<&2, Ind.Individual>) -> List<&2, Ind.Individual>: 654 Util.map_halves(~Ind.Individual, ~Ind.Individual, ~Problem, ~evaluate, p, xs)