1 # Individual — C# Unsga3.Algorithm.Individual. 2 # Objectives + decision variables + evaluated + tournament bookkeeping 3 # (rank / niche / associated ref / perp distance). 4 # Constraints are g ≤ 0. `cv` is the sum of the positive parts 5 # (RefreshConstraintViolation). Feasible iff cv ≤ 0. Empty g is cv 0, 6 # so unconstrained ZDT / DTLZ stay on the Pareto path. 7 # `lnk` is false until `stamp` (C# AssociatedReference starts at -1). 8 # CV-fill clears it again: those members are not in a niche. 9 10 import Base 11 12 type Individual is Data: 13 MkIndividual{ 14 x: List<&2, F32> 15 f: List<&2, F32> 16 g: List<&2, F32> 17 cv: F32 18 rnk: Nat 19 nic: Nat 20 aref: Nat 21 dist: F32 22 ev: Bool 23 lnk: Bool 24 } 25 26 def pos_part(+x: F32) -> F32: 27 Bool.pick(F32, F32.is_gt(x, 0.0), x, 0.0) 28 29 # Sum of g_i where g_i > 0. Non-positive parts add nothing. 30 def sum_pos(xs: List<&2, F32>) -> F32: 31 match xs: 32 case Nil{}: 33 0.0 34 case +h <> t: 35 F32.add(pos_part(h), sum_pos(t)) 36 37 # v0 / A/B path: objectives already known; no decision vector. 38 # No constraints → cv 0 (feasible). 39 def from_objectives(+f: List<&2, F32>) -> Individual: 40 MkIndividual{Nil{}, f, Nil{}, 0.0, 0n, 0n, 0n, 0.0, True{}, False{}} 41 42 # Unevaluated candidate: variables set, objectives zeroed (C# ctor). 43 # Constraints empty until a problem writes them. 44 def from_variables(+x: List<&2, F32>, n_obj: Nat) -> Individual: 45 MkIndividual{x, List.replicate(F32, n_obj, 0.0), Nil{}, 0.0, 0n, 0n, 0n, 0.0, False{}, False{}} 46 47 def variables(ind: Individual) -> List<&2, F32>: 48 match ind: 49 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 50 x 51 52 def objectives(ind: Individual) -> List<&2, F32>: 53 match ind: 54 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 55 f 56 57 def constraints(ind: Individual) -> List<&2, F32>: 58 match ind: 59 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 60 g 61 62 def constraint_violation(ind: Individual) -> F32: 63 match ind: 64 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 65 cv 66 67 # C# IsFeasible: ConstraintViolation <= 0. 68 def feasible(ind: Individual) -> Bool: 69 F32.is_le(constraint_violation(ind), 0.0) 70 71 def rank(ind: Individual) -> Nat: 72 match ind: 73 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 74 rnk 75 76 def evaluated(ind: Individual) -> Bool: 77 match ind: 78 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 79 ev 80 81 def n_obj(ind: Individual) -> Nat: 82 List.length(&2, F32, objectives(ind)) 83 84 def n_var(ind: Individual) -> Nat: 85 List.length(&2, F32, variables(ind)) 86 87 def var_at(ind: Individual, i: Nat) -> F32: 88 Maybe.default(&2, F32, List.get(&2, F32, variables(ind), i), 0.0) 89 90 def set_var_at(ind: Individual, i: Nat, v: F32) -> Individual: 91 match ind: 92 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 93 MkIndividual{List.set(&2, F32, x, i, v), f, g, cv, rnk, nic, ar, pd, ev, lnk} 94 95 def set_evaluated(ind: Individual, ev2: Bool) -> Individual: 96 match ind: 97 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 98 MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev2, lnk} 99 100 def niche(ind: Individual) -> Nat: 101 match ind: 102 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 103 nic 104 105 def associated_ref(ind: Individual) -> Nat: 106 match ind: 107 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 108 ar 109 110 def perp_dist(ind: Individual) -> F32: 111 match ind: 112 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 113 pd 114 115 def set_objectives(ind: Individual, +f2: List<&2, F32>) -> Individual: 116 match ind: 117 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 118 MkIndividual{x, f2, g, cv, rnk, nic, ar, pd, True{}, lnk} 119 120 # Replace g and recompute cv (sum of positive parts). 121 def set_constraints(ind: Individual, +g2: List<&2, F32>) -> Individual: 122 match ind: 123 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 124 MkIndividual{x, f, g2, sum_pos(g2), rnk, nic, ar, pd, ev, lnk} 125 126 def set_rank(ind: Individual, rnk2: Nat) -> Individual: 127 match ind: 128 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 129 MkIndividual{x, f, g, cv, rnk2, nic, ar, pd, ev, lnk} 130 131 def stamp(ind: Individual, +rnk2: Nat, +nic2: Nat, +aref2: Nat, +dist2: F32) -> Individual: 132 match ind: 133 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 134 MkIndividual{x, f, g, cv, rnk2, nic2, aref2, dist2, ev, True{}} 135 136 # C# AssociatedReference starts at -1. `stamp` is the assignment. 137 def niche_linked(ind: Individual) -> Bool: 138 match ind: 139 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 140 lnk 141 142 # CV fill does not assign a niche (pymoo merges these after survival). 143 def clear_niche(ind: Individual) -> Individual: 144 match ind: 145 case MkIndividual{x, f, g, cv, rnk, nic, ar, pd, ev, lnk}: 146 MkIndividual{x, f, g, cv, rnk, 0n, 0n, 0.0, ev, False{}}