1 # Named problems — C# Unsga3.Problems Zdt1 / Zdt2 / Dtlz2. 2 # Bounds + Evaluate match the attached C# IProblem / problem files. 3 # Minimize. Unconstrained (NumberOfConstraints = 0). 4 5 import Base 6 import ./individual.bend as Ind 7 import ./bounds.bend as Bounds 8 import ./util.bend as Util 9 10 type Family is Data: 11 Zdt1{} 12 Zdt2{} 13 Dtlz2{} 14 15 type Problem is Data: 16 MkProblem{ 17 kind: Family 18 box: Bounds.Box 19 } 20 21 def box_of(p: Problem) -> Bounds.Box: 22 match p: 23 case MkProblem{kind, box}: 24 box 25 26 def kind_of(p: Problem) -> Family: 27 match p: 28 case MkProblem{kind, box}: 29 kind 30 31 def n_obj(p: Problem) -> Nat: 32 Bounds.n_obj(box_of(p)) 33 34 def n_var(p: Problem) -> Nat: 35 Bounds.n_var(box_of(p)) 36 37 # ZDT1 / ZDT2: n variables (C# default 30), 2 objectives, x ∈ [0,1]^n. 38 def zdt1(n: Nat) -> Problem: 39 MkProblem{Zdt1{}, Bounds.unit_n(n, 2n)} 40 41 def zdt2(n: Nat) -> Problem: 42 MkProblem{Zdt2{}, Bounds.unit_n(n, 2n)} 43 44 # n = M + k - 1 (C# Dtlz2Problem). 45 def dtlz_n(m: Nat, k: Nat) -> Nat: 46 match m: 47 case 0n: 48 k 49 case 1n+pm: 50 match k: 51 case 0n: 52 pm 53 case 1n+pk: 54 Nat.add(pm, 1n+pk) 55 56 # DTLZ2: C# default M=3, k=10 ⇒ n=12, x ∈ [0,1]^n. 57 def dtlz2(+m: Nat, k: Nat) -> Problem: 58 MkProblem{Dtlz2{}, Bounds.unit_n(dtlz_n(m, k), m)} 59 60 def sum_from(xs: List<&2, F32>) -> F32: 61 match xs: 62 case Nil{}: 63 0.0 64 case h <> t: 65 F32.add(h, sum_from(t)) 66 67 def sum_tail(xs: List<&2, F32>) -> F32: 68 match xs: 69 case Nil{}: 70 0.0 71 case h <> t: 72 sum_from(t) 73 74 def zdt_den(n: Nat) -> F32: 75 match n: 76 case 0n: 77 1.0 78 case 1n: 79 1.0 80 case 1n+p: 81 F32.from_nat(p) 82 83 # C# Zdt1Problem.GLinear: 1 + 9 * sum(x[1..]) / (n - 1). 84 def zdt_g(+x: List<&2, F32>) -> F32: 85 +n = Util.list_len(F32, x) 86 F32.add(1.0, F32.div(F32.mul(9.0, sum_tail(x)), zdt_den(n))) 87 88 def zdt1_f(+x: List<&2, F32>) -> List<&2, F32>: 89 +x0 = Util.get_f32(x, 0n) 90 +g = zdt_g(x) 91 x0 <> [F32.mul(g, F32.sub(1.0, F32.sqrt(F32.div(x0, g))))] 92 93 def zdt2_f(+x: List<&2, F32>) -> List<&2, F32>: 94 +x0 = Util.get_f32(x, 0n) 95 +g = zdt_g(x) 96 +r = F32.div(x0, g) 97 x0 <> [F32.mul(g, F32.sub(1.0, F32.mul(r, r)))] 98 99 def pred(n: Nat) -> Nat: 100 match n: 101 case 0n: 102 0n 103 case 1n+p: 104 p 105 106 def sum_sq_half(xs: List<&2, F32>) -> F32: 107 match xs: 108 case Nil{}: 109 0.0 110 case h <> t: 111 F32.add(F32.square(F32.sub(h, 0.5)), sum_sq_half(t)) 112 113 # C# DtlzHelper.GDtlz2: sum (x[i] - 0.5)^2 for i >= M-1. 114 def dtlz2_g(+x: List<&2, F32>, m: Nat) -> F32: 115 sum_sq_half(List.drop(&2, F32, x, pred(m))) 116 117 def half_pi() -> F32: 118 F32.div(F32.pi(), 2.0) 119 120 def prod_cos.go(+x: List<&2, F32>, fuel: Nat, +i: Nat, acc: F32) -> F32: 121 match fuel: 122 case 0n: 123 acc 124 case 1n+f: 125 prod_cos.go( 126 x, 127 f, 128 1n+i, 129 F32.mul(acc, F32.cos(F32.mul(Util.get_f32(x, i), half_pi()))) 130 ) 131 132 def prod_cos(+x: List<&2, F32>, n: Nat) -> F32: 133 prod_cos.go(x, n, 0n, 1.0) 134 135 def dtlz2_sin(+x: List<&2, F32>, +m: Nat, +i: Nat) -> F32: 136 F32.sin(F32.mul(Util.get_f32(x, pred(Nat.sub(m, i))), half_pi())) 137 138 def dtlz2_fi.tail(+x: List<&2, F32>, +m: Nat, +g: F32, +i: Nat, +body: F32, first: Bool) -> F32: 139 match first: 140 case True{}: 141 body 142 case False{}: 143 F32.mul(body, dtlz2_sin(x, m, i)) 144 145 # C# Dtlz2Problem: (1+g) * prod_{j0]. 146 def dtlz2_fi(+x: List<&2, F32>, +m: Nat, +g: F32, +i: Nat) -> F32: 147 dtlz2_fi.tail( 148 x, 149 m, 150 g, 151 i, 152 F32.mul(F32.add(1.0, g), prod_cos(x, pred(Nat.sub(m, i)))), 153 Nat.is_eq(i, 0n) 154 ) 155 156 def dtlz2_fs.go(+x: List<&2, F32>, +m: Nat, +g: F32, fuel: Nat, +i: Nat) -> List<&2, F32>: 157 match fuel: 158 case 0n: 159 Nil{} 160 case 1n+f: 161 dtlz2_fi(x, m, g, i) <> dtlz2_fs.go(x, m, g, f, 1n+i) 162 163 def dtlz2_f(+x: List<&2, F32>, +m: Nat) -> List<&2, F32>: 164 dtlz2_fs.go(x, m, dtlz2_g(x, m), m, 0n) 165 166 def eval_kind(kind: Family, +x: List<&2, F32>, +m: Nat) -> List<&2, F32>: 167 match kind: 168 case Zdt1{}: 169 zdt1_f(x) 170 case Zdt2{}: 171 zdt2_f(x) 172 case Dtlz2{}: 173 dtlz2_f(x, m) 174 175 def evaluate(+p: Problem, +ind: Ind.Individual) -> Ind.Individual: 176 Ind.set_objectives(ind, eval_kind(kind_of(p), Ind.variables(ind), n_obj(p))) 177 178 # Independent per individual. Mid-split + parallel recurse; same order as cons. 179 def evaluate_all(+p: Problem, +xs: List<&2, Ind.Individual>) -> List<&2, Ind.Individual>: 180 Util.map_halves(~Ind.Individual, ~Ind.Individual, ~Problem, ~evaluate, p, xs)