Agent Conversation: google/gemini-3.1-pro-preview
Task: optimization-multi-objective
Multi-Objective Optimization: Custom Evolutionary Strategy Design
Research Question
Design a novel multi-objective evolutionary algorithm (MOEA) strategy that achieves better convergence, diversity, and spread on standard benchmark problems than classic approaches like NSGA-II, MOEA/D, and SPEA2.
Background
Multi-objective optimization aims to find a set of Pareto-optimal solutions that represent the best trade-offs among conflicting objectives. Evolutionary algorithms are the dominant approach, differing primarily in three components:
- Parent selection: How to choose individuals for mating (e.g., tournament with crowding distance, reference-vector-based).
- Variation: How to produce offspring via crossover and mutation operators.
- Environmental selection (survival): How to prune the combined parent+offspring pool back to population size (e.g., non-dominated sorting + crowding, decomposition into subproblems, indicator-based selection).
Classic algorithms include:
- NSGA-II (Deb et al., 2002): Non-dominated sorting + crowding distance for diversity.
- MOEA/D (Zhang & Li, 2007): Decomposes the problem into scalar subproblems using weight vectors.
- SPEA2 (Zitzler et al., 2001): Strength-based fitness with density estimation via k-nearest neighbors.
State-of-the-art methods include:
- NSGA-III (Deb & Jain, 2014): Reference-point-based selection for many-objective problems.
- RVEA (Cheng et al., 2016): Angle-penalized distance with adaptive reference vectors.
- AGE-MOEA (Panichella, 2019): Adaptive geometry estimation for survival selection.
There is active research into strategies that combine ideas across these paradigms, adapt to problem geometry, or use novel diversity maintenance mechanisms.
Task
Implement a custom multi-objective evolutionary strategy by modifying the CustomMOEA class in deap/custom_moea.py. You should implement the select, vary, survive, and optionally on_generation methods. The algorithm must work for both 2-objective and 3-objective problems.
Interface
class CustomMOEA:
def __init__(self, pop_size, n_obj, n_var, bounds, cx_eta=20.0, mut_eta=20.0, mut_prob=None):
"""Initialize the MOEA with problem parameters."""
def select(self, population: list, k: int) -> list:
"""Select k parents from the population for mating.
Returns: list of k selected individuals."""
def vary(self, parents: list) -> list:
"""Apply crossover and mutation to produce offspring.
Returns: list of offspring (fitness invalidated)."""
def survive(self, population: list, offspring: list) -> list:
"""Environmental selection: choose pop_size individuals from combined pool.
Returns: list of pop_size individuals for next generation."""
def on_generation(self, gen: int, population: list):
"""Optional per-generation callback for adaptive strategies."""
Ind
Tool Input
1{
2 "end_line": 441,
3 "start_line": 297,
4 "content": "class CustomMOEA:\n \"\"\"SDE-NSGA-II: Non-dominated Sorting with Shift-Based Density Estimation.\"\"\"\n\n def __init__(\n self,\n pop_size: int,\n n_obj: int,\n n_var: int,\n bounds: Tuple[float, float],\n cx_eta: float = 20.0,\n mut_eta: float = 20.0,\n mut_prob: Optional[float] = None,\n ):\n self.pop_size = pop_size\n self.n_obj = n_obj\n self.n_var = n_var\n self.bounds = bounds\n self.cx_eta = cx_eta\n self.mut_eta = mut_eta\n self.mut_prob = mut_prob if mut_prob is not None else 1.0 / n_var\n\n def select(self, population: list, k: int) -> list:\n \"\"\"Tournament selection using rank and SDE distances.\"\"\"\n selected = []\n for _ in range(k):\n i1, i2 = random.sample(range(len(population)), 2)\n a, b = population[i1], population[i2]\n \n rank_a = getattr(a, '_rank', 0)\n rank_b = getattr(b, '_rank', 0)\n \n if rank_a < rank_b:\n selected.append(deepcopy(a))\n elif rank_b < rank_a:\n selected.append(deepcopy(b))\n else:\n cd_a = getattr(a.fitness, 'crowding_dist', 0)\n cd_b = getattr(b.fitness, 'crowding_dist', 0)\n if cd_a > cd_b:\n selected.append(deepcopy(a))\n elif cd_b > cd_a:\n selected.append(deepcopy(b))\n else:\n selected.append(deepcopy(random.choice([a, b])))\n return selected\n\n def vary(self, parents: list) -> list:\n offspring = [deepcopy(p) for p in parents]\n lo, hi = self.bounds\n n = len(offspring)\n\n # Mix SBX and Differential Evolution (DE)\n for i in range(0, n - 1, 2):\n if random.random() < 0.5:\n # SBX Crossover\n tools.cxSimulatedBinaryBounded(offspring[i], offspring[i+1], eta=self.cx_eta, low=lo, up=hi)\n try:\n del offspring[i].fitness.values\n del offspring[i+1].fitness.values\n except AttributeError:\n pass\n else:\n # DE Crossover for both children\n for child_idx in (i, i+1):\n r1, r2, r3 = random.sample(range(n), 3)\n j_rand = random.randint(0, self.n_var - 1)\n for j in range(self.n_var):\n if random.random() < 0.9 or j == j_rand:\n val = parents[r1][j] + 0.5 * (parents[r2][j] - parents[r3][j])\n offspring[child_idx][j] = max(lo, min(hi, val))\n try:\n del offspring[child_idx].fitness.values\n except AttributeError:\n pass\n\n # Polynomial Mutation\n for ind in offspring:\n if random.random() < 1.0:\n tools.mutPolynomialBounded(ind, eta=self.mut_eta, low=lo, up=hi, indpb=self.mut_prob)\n try:\n del ind.fitness.values\n except AttributeError:\n pass\n\n return offspring\n\n def survive(self, population: list, offspring: list) -> list:\n combined = population + offspring\n fronts = tools.sortNondominated(combined, self.pop_size, first_front_only=False)\n \n next_gen = []\n for rank, front in enumerate(fronts):\n for ind in front:\n ind._rank = rank\n \n if len(next_gen) + len(front) <= self.pop_size:\n self._assign_sde_crowding(front)\n next_gen.extend(front)\n else:\n remaining = self.pop_size - len(next_gen)\n truncated_front = self._sde_truncation(front, remaining)\n self._assign_sde_crowding(truncated_front)\n next_gen.extend(truncated_front)\n break\n \n return next_gen\n\n def _sde_truncation(self, front, k):\n F = np.array([ind.fitness.values for ind in front])\n n = len(front)\n \n z_min = F.min(axis=0)\n z_max = F.max(axis=0)\n scale = np.clip(z_max - z_min, 1e-12, None)\n F_norm = (F - z_min) / scale\n \n sde_mat = np.zeros((n, n))\n for i in range(n):\n for j in range(n):\n if i != j:\n diff = np.maximum(0, F_norm[j] - F_norm[i])\n sde_mat[i, j] = np.sqrt(np.sum(diff**2))\n else:\n sde_mat[i, j] = np.inf\n \n active = list(range(n))\n while len(active) > k:\n sorted_dists = []\n for i in active:\n dists = [sde_mat[i, j] for j in active if j != i]\n dists.sort()\n sorted_dists.append(dists)\n \n worst_idx = 0\n for i in range(1, len(active)):\n for d_i, d_worst in zip(sorted_dists[i], sorted_dists[worst_idx]):\n if abs(d_i - d_worst) > 1e-12:\n if d_i < d_worst:\n worst_idx = i\n break\n active.pop(worst_idx)\n \n return [front[i] for i in active]\n\n def _assign_sde_crowding(self, front):\n if len(front) <= 2:\n for ind in front:\n ind.fitness.crowding_dist = float('inf')\n return\n \n F = np.array([ind.fitness.values for ind in front])\n n = len(front)\n z_min = F.min(axis=0)\n z_max = F.max(axis=0)\n scale = np.clip(z_max - z_min, 1e-12, None)\n F_norm = (F - z_min) / scale\n \n for i in range(n):\n min_dist = float('inf')\n for j in range(n):\n if i != j:\n diff = np.maximum(0, F_norm[j] - F_norm[i])\n d = np.sum(diff**2)\n if d < min_dist:\n min_dist = d\n front[i].fitness.crowding_dist = np.sqrt(min_dist)\n\n def on_generation(self, gen: int, population: list):\n pass\n",
5 "filename": "deap/custom_moea.py",
6 "op": "replace"
7}OK: Replaced lines 297..441 with 169 line(s) in deap/custom_moea.py. Editable range: 297–465. [Current file: deap/custom_moea.py | editable: 297–465 | total: 614 lines] 294| # ================================================================ 295| 296| 297 class CustomMOEA: 298 """SDE-NSGA-II: Non-dominated Sorting with Shift-Based Density Estimation.""" 299 300 def __init__( 301 self, 302 pop_size: int, 303 n_obj: int, 304 n_var: int, 305 bounds: Tuple[float, float], 306 cx_eta: float = 20.0, 307 mut_eta: float = 20.0, 308 mut_prob: Optional[float] = None, 309 ): 310 self.pop_size = pop_size 311 self.n_obj = n_obj 312 self.n_var = n_var 313 self.bounds = bounds 314 self.cx_eta = cx_eta 315 self.mut_eta = mut_eta 316 self.mut_prob = mut_prob if mut_prob is not None e
[Test #1] (2 tests remaining; call submit(n=1) to submit this result) ## Seed 42 ### zdt1 (scripts/zdt1.sh) Training progress (last generations): TRAIN_METRICS gen=120 hv=0.869303 igd=0.004363 spread=0.367419 front_size=100 TRAIN_METRICS gen=140 hv=0.870570 igd=0.004145 spread=0.365231 front_size=100 TRAIN_METRICS gen=160 hv=0.870548 igd=0.004157 spread=0.371205 front_size=100 TRAIN_METRICS gen=180 hv=0.870436 igd=0.004348 spread=0.380956 front_size=100 TRAIN_METRICS gen=200 hv=0.870901 igd=0.004224 spread=0.362991 front_size=100 Test results (zdt1): hv: 0.870901 igd: 0.004224 spread: 0.362991 ### zdt3 (scripts/zdt3.sh) Training progress (last generations): TRAIN_METRICS gen=120 hv=1.324785 igd=0.005862 spread=0.668579 front_size=100 TRAIN_METRICS gen=140 hv=1.324737 igd=0.006371 spread=0.732037 front_size=100 TRAIN_METRICS gen=160 hv=1.326150 igd=0.006189 spread=0.659373 front_size=100 TRAIN_METRICS gen=180 hv=1.326179 igd=0.005565 spread=0.688672 front_size=100 TRAIN_METRIC
Tool Input
1{
2 "start_line": 383,
3 "end_line": 463,
4 "content": " def survive(self, population: list, offspring: list) -> list:\n combined = population + offspring\n fronts = tools.sortNondominated(combined, self.pop_size, first_front_only=False)\n \n F_all = np.array([ind.fitness.values for ind in combined])\n z_min = F_all.min(axis=0)\n z_max = F_all.max(axis=0)\n scale = np.clip(z_max - z_min, 1e-12, None)\n \n next_gen = []\n for rank, front in enumerate(fronts):\n for ind in front:\n ind._rank = rank\n \n if len(next_gen) + len(front) <= self.pop_size:\n self._assign_sde_crowding(front, z_min, scale)\n next_gen.extend(front)\n else:\n remaining = self.pop_size - len(next_gen)\n truncated_front = self._sde_truncation(front, remaining, z_min, scale)\n self._assign_sde_crowding(truncated_front, z_min, scale)\n next_gen.extend(truncated_front)\n break\n \n return next_gen\n\n def _sde_truncation(self, front, k, z_min, scale):\n F = np.array([ind.fitness.values for ind in front])\n n = len(front)\n \n F_norm = (F - z_min) / scale\n \n sde_mat = np.zeros((n, n))\n for i in range(n):\n for j in range(n):\n if i != j:\n diff = np.maximum(0, F_norm[j] - F_norm[i])\n sde_mat[i, j] = np.sqrt(np.sum(diff**2))\n else:\n sde_mat[i, j] = np.inf\n \n active = list(range(n))\n while len(active) > k:\n sorted_dists = []\n for i in active:\n dists = [sde_mat[i, j] for j in active if j != i]\n dists.sort()\n sorted_dists.append(dists)\n \n worst_idx = 0\n for i in range(1, len(active)):\n for d_i, d_worst in zip(sorted_dists[i], sorted_dists[worst_idx]):\n if abs(d_i - d_worst) > 1e-12:\n if d_i < d_worst:\n worst_idx = i\n break\n active.pop(worst_idx)\n \n return [front[i] for i in active]\n\n def _assign_sde_crowding(self, front, z_min, scale):\n if len(front) <= 2:\n for ind in front:\n ind.fitness.crowding_dist = float('inf')\n return\n \n F = np.array([ind.fitness.values for ind in front])\n n = len(front)\n F_norm = (F - z_min) / scale\n \n for i in range(n):\n min_dist = float('inf')\n for j in range(n):\n if i != j:\n diff = np.maximum(0, F_norm[j] - F_norm[i])\n d = np.sum(diff**2)\n if d < min_dist:\n min_dist = d\n front[i].fitness.crowding_dist = np.sqrt(min_dist)",
5 "filename": "deap/custom_moea.py",
6 "op": "replace"
7}OK: Replaced lines 383..463 with 79 line(s) in deap/custom_moea.py. Editable range: 297–463. [Current file: deap/custom_moea.py | editable: 297–463 | total: 612 lines] 294| # ================================================================ 295| 296| 297 class CustomMOEA: 298 """SDE-NSGA-II: Non-dominated Sorting with Shift-Based Density Estimation.""" 299 300 def __init__( 301 self, 302 pop_size: int, 303 n_obj: int, 304 n_var: int, 305 bounds: Tuple[float, float], 306 cx_eta: float = 20.0, 307 mut_eta: float = 20.0, 308 mut_prob: Optional[float] = None, 309 ): 310 self.pop_size = pop_size 311 self.n_obj = n_obj 312 self.n_var = n_var 313 self.bounds = bounds 314 self.cx_eta = cx_eta 315 self.mut_eta = mut_eta 316 self.mut_prob = mut_prob if mut_prob is not None el
Tool Input
1{
2 "start_line": 346,
3 "end_line": 440,
4 "filename": "deap/custom_moea.py",
5 "content": " def select(self, population: list, k: int) -> list:\n fronts = tools.sortNondominated(population, len(population), first_front_only=False)\n for front in fronts:\n compute_crowding_distance(front)\n return tools.selTournamentDCD(population, k)\n\n def vary(self, parents: list) -> list:\n offspring = []\n lo, hi = self.bounds\n for i in range(len(parents)):\n child = deepcopy(parents[i])\n if random.random() < 0.5:\n # DE variation\n r1, r2, r3 = random.sample(parents, 3)\n F_weight = random.uniform(0.4, 0.8)\n CR = random.uniform(0.7, 1.0)\n j_rand = random.randint(0, self.n_var - 1)\n for j in range(self.n_var):\n if random.random() < CR or j == j_rand:\n child[j] = r1[j] + F_weight * (r2[j] - r3[j])\n if child[j] < lo: child[j] = lo\n if child[j] > hi: child[j] = hi\n else:\n # SBX\n mate_idx = random.randint(0, len(parents) - 1)\n tools.cxSimulatedBinaryBounded(child, parents[mate_idx], eta=self.cx_eta, low=lo, up=hi)\n\n if random.random() < 1.0:\n tools.mutPolynomialBounded(child, eta=self.mut_eta, low=lo, up=hi, indpb=self.mut_prob)\n del child.fitness.values\n offspring.append(child)\n return offspring\n\n def survive(self, population: list, offspring: list) -> list:\n combined = population + offspring\n fronts = tools.sortNondominated(combined, self.pop_size, first_front_only=False)\n\n next_gen = []\n for front in fronts:\n if len(next_gen) + len(front) <= self.pop_size:\n next_gen.extend(front)\n else:\n remaining = self.pop_size - len(next_gen)\n F_front = np.array([ind.fitness.values for ind in front])\n \n z_min = np.min(F_front, axis=0)\n z_max = np.max(F_front, axis=0)\n scale = z_max - z_min\n scale[scale < 1e-12] = 1.0\n F_front_norm = (F_front - z_min) / scale\n \n selected_indices = []\n min_dists = np.full(len(front), np.inf)\n \n if len(next_gen) > 0:\n F_next = np.array([ind.fitness.values for ind in next_gen])\n F_next_norm = (F_next - z_min) / scale\n for i in range(len(front)):\n diffs = F_next_norm - F_front_norm[i]\n dists = np.sum(diffs**2, axis=1)\n if len(dists) > 0:\n min_dists[i] = np.min(dists)\n else:\n for m in range(self.n_obj):\n if len(selected_indices) >= remaining:\n break\n idx = np.argmin(F_front_norm[:, m])\n if idx not in selected_indices:\n selected_indices.append(idx)\n diffs = F_front_norm - F_front_norm[idx]\n min_dists = np.minimum(min_dists, np.sum(diffs**2, axis=1))\n \n while len(selected_indices) < remaining:\n masked_dists = min_dists.copy()\n masked_dists[selected_indices] = -1.0 \n \n best_idx = int(np.argmax(masked_dists))\n selected_indices.append(best_idx)\n \n diffs = F_front_norm - F_front_norm[best_idx]\n min_dists = np.minimum(min_dists, np.sum(diffs**2, axis=1))\n \n next_gen.extend([front[i] for i in selected_indices])\n break\n \n return next_gen\n\n def on_generation(self, gen: int, population: list):\n pass",
6 "op": "replace"
7}OK: Replaced lines 346..440 with 89 line(s) in deap/custom_moea.py. Editable range: 297–435. [Current file: deap/custom_moea.py | editable: 297–435 | total: 584 lines] 294| # ================================================================ 295| 296| 297 class CustomMOEA: 298 """Custom multi-objective evolutionary algorithm. 299 300 The agent should implement a novel evolutionary strategy for multi-objective 301 optimization. The algorithm operates on a population of individuals, each 302 with a fitness consisting of multiple objective values (all minimized). 303 304 Available DEAP utilities (already imported): 305 - tools.sortNondominated(pop, k) -> list of fronts 306 - tools.selTournamentDCD(pop, k) -> selected individuals 307 - tools.cxSimulatedBinaryBounded(ind1, ind2, eta, low, up) 308 - tools.mutPolynomialBounded(ind, eta, low, up, indpb) 309 - tools.unifor
[Test #1] (2 tests remaining; call submit(n=1) to submit this result) ### zdt1 (scripts/zdt1.sh) Training progress (last generations): TRAIN_METRICS gen=120 hv=0.861814 igd=0.008300 spread=0.276295 front_size=100 TRAIN_METRICS gen=140 hv=0.863574 igd=0.007169 spread=0.261842 front_size=100 TRAIN_METRICS gen=160 hv=0.864249 igd=0.006943 spread=0.284282 front_size=100 TRAIN_METRICS gen=180 hv=0.865457 igd=0.006280 spread=0.308060 front_size=100 TRAIN_METRICS gen=200 hv=0.866405 igd=0.005741 spread=0.279686 front_size=100 Test results (zdt1): hv: 0.866405 igd: 0.005741 spread: 0.279686 ### zdt3 (scripts/zdt3.sh) Training progress (last generations): TRAIN_METRICS gen=120 hv=1.309892 igd=0.008135 spread=0.501338 front_size=100 TRAIN_METRICS gen=140 hv=1.313320 igd=0.007280 spread=0.515454 front_size=100 TRAIN_METRICS gen=160 hv=1.316302 igd=0.006455 spread=0.480840 front_size=100 TRAIN_METRICS gen=180 hv=1.318929 igd=0.005904 spread=0.482375 front_size=100 TRAIN_METRICS gen=200 hv=
Tool Input
1{
2 "op": "replace",
3 "content": "class CustomMOEA:\n \"\"\"SDE-DE: Shift-Based Density Estimation with Differential Evolution.\"\"\"\n\n def __init__(\n self,\n pop_size: int,\n n_obj: int,\n n_var: int,\n bounds: Tuple[float, float],\n cx_eta: float = 20.0,\n mut_eta: float = 20.0,\n mut_prob: Optional[float] = None,\n ):\n self.pop_size = pop_size\n self.n_obj = n_obj\n self.n_var = n_var\n self.bounds = bounds\n self.cx_eta = cx_eta\n self.mut_eta = mut_eta\n self.mut_prob = mut_prob if mut_prob is not None else 1.0 / n_var\n\n def select(self, population: list, k: int) -> list:\n # Assign rank and SDE density\n fronts = tools.sortNondominated(population, len(population), first_front_only=False)\n for rank, front in enumerate(fronts):\n if len(front) <= 2:\n for ind in front:\n ind._sde_density = float('inf')\n ind._rank = rank\n else:\n front_arr = np.array([ind.fitness.values for ind in front])\n z_min = np.min(front_arr, axis=0)\n z_max = np.max(front_arr, axis=0)\n scale = z_max - z_min\n scale[scale < 1e-12] = 1.0\n F_norm = (front_arr - z_min) / scale\n \n diff_front = F_norm[np.newaxis, :, :] - F_norm[:, np.newaxis, :]\n diff_front = np.maximum(0, diff_front)\n dists_front = np.sqrt(np.sum(diff_front ** 2, axis=2))\n np.fill_diagonal(dists_front, np.inf)\n \n sde_density = np.min(dists_front, axis=1)\n for i, ind in enumerate(front):\n ind._sde_density = sde_density[i]\n ind._rank = rank\n\n selected = []\n for _ in range(k):\n i1, i2 = random.sample(range(len(population)), 2)\n ind1, ind2 = population[i1], population[i2]\n \n if ind1._rank < ind2._rank:\n selected.append(deepcopy(ind1))\n elif ind1._rank > ind2._rank:\n selected.append(deepcopy(ind2))\n else:\n if ind1._sde_density > ind2._sde_density:\n selected.append(deepcopy(ind1))\n else:\n selected.append(deepcopy(ind2))\n return selected\n\n def vary(self, parents: list) -> list:\n offspring = []\n lo, hi = self.bounds\n\n for i in range(len(parents)):\n child = deepcopy(parents[i])\n \n if random.random() < 0.5:\n # Differential Evolution (DE/rand/1/bin)\n pool = random.sample(parents, 3)\n p1, p2, p3 = pool[0], pool[1], pool[2]\n \n F_w = random.uniform(0.4, 0.9)\n CR = random.uniform(0.1, 0.9)\n jr = random.randint(0, self.n_var - 1)\n \n for j in range(self.n_var):\n if random.random() < CR or j == jr:\n child[j] = p1[j] + F_w * (p2[j] - p3[j])\n child[j] = min(max(child[j], lo), hi)\n else:\n # Simulated Binary Crossover\n mate = random.choice(parents)\n tools.cxSimulatedBinaryBounded(\n child, mate,\n eta=self.cx_eta, low=lo, up=hi,\n )\n \n # Polynomial Mutation\n if random.random() < 1.0:\n tools.mutPolynomialBounded(\n child, eta=self.mut_eta, low=lo, up=hi, indpb=self.mut_prob,\n )\n \n del child.fitness.values\n offspring.append(child)\n\n return offspring\n\n def survive(self, population: list, offspring: list) -> list:\n combined = population + offspring\n fronts = tools.sortNondominated(combined, self.pop_size, first_front_only=False)\n \n # Determine global normalization bounds for SDE\n combined_arr = np.array([ind.fitness.values for ind in combined])\n z_min = np.min(combined_arr, axis=0)\n z_max = np.max(combined_arr, axis=0)\n scale = z_max - z_min\n scale[scale < 1e-12] = 1.0\n\n next_gen = []\n for front in fronts:\n if len(next_gen) + len(front) <= self.pop_size:\n next_gen.extend(front)\n else:\n remaining = self.pop_size - len(next_gen)\n front_arr = np.array([ind.fitness.values for ind in front])\n F_norm = (front_arr - z_min) / scale\n \n active_indices = list(range(len(front)))\n \n # SDE Step-wise density truncation\n while len(active_indices) > remaining:\n active_F_norm = F_norm[active_indices]\n diff_front = active_F_norm[np.newaxis, :, :] - active_F_norm[:, np.newaxis, :]\n diff_front = np.maximum(0, diff_front)\n dists_front = np.sqrt(np.sum(diff_front ** 2, axis=2))\n np.fill_diagonal(dists_front, np.inf)\n \n sde_density = np.min(dists_front, axis=1)\n worst_idx = np.argmin(sde_density)\n del active_indices[worst_idx]\n \n selected_front = [front[i] for i in active_indices]\n next_gen.extend(selected_front)\n break\n \n return next_gen\n\n def on_generation(self, gen: int, population: list):\n pass",
4 "start_line": 297,
5 "end_line": 441,
6 "filename": "deap/custom_moea.py"
7}OK: Replaced lines 297..441 with 144 line(s) in deap/custom_moea.py. Editable range: 297–440. [Current file: deap/custom_moea.py | editable: 297–440 | total: 589 lines] 294| # ================================================================ 295| 296| 297 class CustomMOEA: 298 """SDE-DE: Shift-Based Density Estimation with Differential Evolution.""" 299 300 def __init__( 301 self, 302 pop_size: int, 303 n_obj: int, 304 n_var: int, 305 bounds: Tuple[float, float], 306 cx_eta: float = 20.0, 307 mut_eta: float = 20.0, 308 mut_prob: Optional[float] = None, 309 ): 310 self.pop_size = pop_size 311 self.n_obj = n_obj 312 self.n_var = n_var 313 self.bounds = bounds 314 self.cx_eta = cx_eta 315 self.mut_eta = mut_eta 316 self.mut_prob = mut_prob if mut_prob is not None else
[Test #1] (2 tests remaining; call submit(n=1) to submit this result) ### zdt1 (scripts/zdt1.sh) Training progress (last generations): TRAIN_METRICS gen=120 hv=0.870131 igd=0.004339 spread=0.370017 front_size=100 TRAIN_METRICS gen=140 hv=0.870798 igd=0.004353 spread=0.359246 front_size=100 TRAIN_METRICS gen=160 hv=0.870716 igd=0.004020 spread=0.303221 front_size=100 TRAIN_METRICS gen=180 hv=0.870533 igd=0.004196 spread=0.339481 front_size=100 TRAIN_METRICS gen=200 hv=0.870868 igd=0.004286 spread=0.386009 front_size=100 Test results (zdt1): hv: 0.870868 igd: 0.004286 spread: 0.386009 ### zdt3 (scripts/zdt3.sh) Training progress (last generations): TRAIN_METRICS gen=120 hv=1.324893 igd=0.005870 spread=0.682078 front_size=100 TRAIN_METRICS gen=140 hv=1.326212 igd=0.005766 spread=0.685178 front_size=100 TRAIN_METRICS gen=160 hv=1.325837 igd=0.006494 spread=0.732752 front_size=100 TRAIN_METRICS gen=180 hv=1.326230 igd=0.005822 spread=0.719554 front_size=100 TRAIN_METRICS gen=200 hv=
Tool Input
1{
2 "filename": "deap/custom_moea.py",
3 "content": "class CustomMOEA:\n \"\"\"SDE: Shift-Based Density Estimation.\"\"\"\n\n def __init__(\n self,\n pop_size: int,\n n_obj: int,\n n_var: int,\n bounds: Tuple[float, float],\n cx_eta: float = 20.0,\n mut_eta: float = 20.0,\n mut_prob: Optional[float] = None,\n ):\n self.pop_size = pop_size\n self.n_obj = n_obj\n self.n_var = n_var\n self.bounds = bounds\n self.cx_eta = cx_eta\n self.mut_eta = mut_eta\n self.mut_prob = mut_prob if mut_prob is not None else 1.0 / n_var\n\n def select(self, population: list, k: int) -> list:\n # Assign rank and SDE density\n fronts = tools.sortNondominated(population, len(population), first_front_only=False)\n for rank, front in enumerate(fronts):\n if len(front) <= 2:\n for ind in front:\n ind._sde_density = float('inf')\n ind._rank = rank\n else:\n front_arr = np.array([ind.fitness.values for ind in front])\n z_min = np.min(front_arr, axis=0)\n z_max = np.max(front_arr, axis=0)\n scale = z_max - z_min\n scale[scale < 1e-12] = 1.0\n F_norm = (front_arr - z_min) / scale\n \n diff_front = F_norm[np.newaxis, :, :] - F_norm[:, np.newaxis, :]\n diff_front = np.maximum(0, diff_front)\n dists_front = np.sqrt(np.sum(diff_front ** 2, axis=2))\n np.fill_diagonal(dists_front, np.inf)\n \n sde_density = np.min(dists_front, axis=1)\n for i, ind in enumerate(front):\n ind._sde_density = sde_density[i]\n ind._rank = rank\n\n selected = []\n for _ in range(k):\n i1, i2 = random.sample(range(len(population)), 2)\n ind1, ind2 = population[i1], population[i2]\n \n if ind1._rank < ind2._rank:\n selected.append(deepcopy(ind1))\n elif ind1._rank > ind2._rank:\n selected.append(deepcopy(ind2))\n else:\n if ind1._sde_density > ind2._sde_density:\n selected.append(deepcopy(ind1))\n else:\n selected.append(deepcopy(ind2))\n return selected\n\n def vary(self, parents: list) -> list:\n offspring = [deepcopy(ind) for ind in parents]\n lo, hi = self.bounds\n\n for i in range(0, len(offspring) - 1, 2):\n if random.random() < 0.9:\n tools.cxSimulatedBinaryBounded(\n offspring[i], offspring[i + 1],\n eta=self.cx_eta, low=lo, up=hi,\n )\n del offspring[i].fitness.values\n del offspring[i + 1].fitness.values\n\n for ind in offspring:\n if random.random() < 1.0:\n tools.mutPolynomialBounded(\n ind, eta=self.mut_eta, low=lo, up=hi, indpb=self.mut_prob,\n )\n if not hasattr(ind, \"fitness\") or not ind.fitness.valid:\n try:\n del ind.fitness.values\n except AttributeError:\n pass\n return offspring\n\n def survive(self, population: list, offspring: list) -> list:\n combined = population + offspring\n fronts = tools.sortNondominated(combined, self.pop_size, first_front_only=False)\n \n combined_arr = np.array([ind.fitness.values for ind in combined])\n z_min = np.min(combined_arr, axis=0)\n z_max = np.max(combined_arr, axis=0)\n scale = z_max - z_min\n scale[scale < 1e-12] = 1.0\n\n next_gen = []\n for front in fronts:\n if len(next_gen) + len(front) <= self.pop_size:\n next_gen.extend(front)\n else:\n remaining = self.pop_size - len(next_gen)\n front_arr = np.array([ind.fitness.values for ind in front])\n F_norm = (front_arr - z_min) / scale\n \n active_indices = list(range(len(front)))\n \n while len(active_indices) > remaining:\n active_F_norm = F_norm[active_indices]\n diff_front = active_F_norm[np.newaxis, :, :] - active_F_norm[:, np.newaxis, :]\n diff_front = np.maximum(0, diff_front)\n dists_front = np.sqrt(np.sum(diff_front ** 2, axis=2))\n np.fill_diagonal(dists_front, np.inf)\n \n sde_density = np.min(dists_front, axis=1)\n worst_idx = np.argmin(sde_density)\n del active_indices[worst_idx]\n \n selected_front = [front[i] for i in active_indices]\n next_gen.extend(selected_front)\n break\n \n return next_gen\n\n def on_generation(self, gen: int, population: list):\n pass",
4 "start_line": 297,
5 "op": "replace",
6 "end_line": 440
7}OK: Replaced lines 297..440 with 128 line(s) in deap/custom_moea.py. Editable range: 297–424. [Current file: deap/custom_moea.py | editable: 297–424 | total: 573 lines] 294| # ================================================================ 295| 296| 297 class CustomMOEA: 298 """SDE: Shift-Based Density Estimation.""" 299 300 def __init__( 301 self, 302 pop_size: int, 303 n_obj: int, 304 n_var: int, 305 bounds: Tuple[float, float], 306 cx_eta: float = 20.0, 307 mut_eta: float = 20.0, 308 mut_prob: Optional[float] = None, 309 ): 310 self.pop_size = pop_size 311 self.n_obj = n_obj 312 self.n_var = n_var 313 self.bounds = bounds 314 self.cx_eta = cx_eta 315 self.mut_eta = mut_eta 316 self.mut_prob = mut_prob if mut_prob is not None else 1.0 / n_var 317 318
[Test #2] (1 test remaining; call submit(n=2) to submit this result) ### zdt1 (scripts/zdt1.sh) Training progress (last generations): TRAIN_METRICS gen=120 hv=0.862388 igd=0.007579 spread=0.395742 front_size=100 TRAIN_METRICS gen=140 hv=0.865814 igd=0.005624 spread=0.354986 front_size=100 TRAIN_METRICS gen=160 hv=0.866732 igd=0.005067 spread=0.352107 front_size=100 TRAIN_METRICS gen=180 hv=0.867996 igd=0.004650 spread=0.319493 front_size=100 TRAIN_METRICS gen=200 hv=0.869099 igd=0.004539 spread=0.347738 front_size=100 Test results (zdt1): hv: 0.869099 igd: 0.004539 spread: 0.347738 ### zdt3 (scripts/zdt3.sh) Training progress (last generations): TRAIN_METRICS gen=120 hv=1.309961 igd=0.008402 spread=0.668550 front_size=100 TRAIN_METRICS gen=140 hv=1.317024 igd=0.007289 spread=0.691042 front_size=100 TRAIN_METRICS gen=160 hv=1.321535 igd=0.006548 spread=0.696177 front_size=100 TRAIN_METRICS gen=180 hv=1.324288 igd=0.006877 spread=0.713986 front_size=100 TRAIN_METRICS gen=200 hv=1