Source code for pymoo.algorithms.moo.age2

from pymoo.docs import parse_doc_string

try:
    import numba
    from numba import jit
except:
    raise Exception("Please install numba to use AGEMOEA2: pip install numba")

import numpy as np


from pymoo.algorithms.base.genetic import GeneticAlgorithm
from pymoo.algorithms.moo.age import AGEMOEASurvival
from pymoo.algorithms.moo.nsga2 import binary_tournament
from pymoo.operators.crossover.sbx import SBX
from pymoo.operators.mutation.pm import PM
from pymoo.operators.sampling.rnd import FloatRandomSampling
from pymoo.operators.selection.tournament import TournamentSelection
from pymoo.util.display.multi import MultiObjectiveOutput


[docs] class AGEMOEA2(GeneticAlgorithm): def __init__(self, pop_size=100, sampling=FloatRandomSampling(), selection=TournamentSelection(func_comp=binary_tournament), crossover=SBX(prob=0.9, eta=15), mutation=PM(eta=20), eliminate_duplicates=True, n_offsprings=None, output=MultiObjectiveOutput(), **kwargs): """ Adapted from: Panichella, A. (2022). An Improved Pareto Front Modeling Algorithm for Large-scale Many-Objective Optimization. Proceedings of the 2022 Genetic and Evolutionary Computation Conference (GECCO 2022). https://doi.org/10.1145/3512290.3528732 @author: Annibale Panichella Parameters ---------- pop_size : {pop_size} sampling : {sampling} selection : {selection} crossover : {crossover} mutation : {mutation} eliminate_duplicates : {eliminate_duplicates} n_offsprings : {n_offsprings} """ super().__init__(pop_size=pop_size, sampling=sampling, selection=selection, crossover=crossover, mutation=mutation, survival=AGEMOEA2Survival(), eliminate_duplicates=eliminate_duplicates, n_offsprings=n_offsprings, output=output, advance_after_initial_infill=True, **kwargs) self.tournament_type = 'comp_by_rank_and_crowding'
@jit(nopython=True, fastmath=True) def project_on_manifold(point, p): dist = np.sum(point[point > 0] ** p) ** (1/p) return np.multiply(point, 1 / dist) import numpy as np def find_zero(point, n, precision): x = 1 epsilon = 1e-10 # Small constant for regularization past_value = x max_float = np.finfo(np.float64).max # Maximum representable float value log_max_float = np.log(max_float) # Logarithm of the maximum float for i in range(0, 100): # Original function with regularization f = 0.0 for obj_index in range(0, n): if point[obj_index] > 0: log_value = x * np.log(point[obj_index] + epsilon) if log_value < log_max_float: f += np.exp(log_value) else: return 1 # Handle overflow by returning a fallback value f = np.log(f) if f > 0 else 0 # Avoid log of non-positive numbers # Derivative with regularization numerator = 0 denominator = 0 for obj_index in range(0, n): if point[obj_index] > 0: log_value = x * np.log(point[obj_index] + epsilon) if log_value < log_max_float: power_value = np.exp(log_value) log_term = np.log(point[obj_index] + epsilon) # Use logarithmic comparison to avoid overflow if log_value + np.log(abs(log_term) + epsilon) < log_max_float: result = power_value * log_term numerator += result denominator += power_value else: # Handle extreme cases by capping the result numerator += max_float denominator += power_value else: return 1 # Handle overflow by returning a fallback value if denominator == 0 or np.isnan(denominator) or np.isinf(denominator): return 1 # Handle division by zero or NaN if np.isnan(numerator) or np.isinf(numerator): return 1 # Handle invalid numerator ff = numerator / denominator if ff == 0: # Check for zero before division return 1 # Handle by returning a fallback value # Update x using Newton's method x = x - f / ff if abs(x - past_value) <= precision: break else: past_value = x # Update current point if isinstance(x, complex) or np.isinf(x) or np.isnan(x): return 1 else: return x class AGEMOEA2Survival(AGEMOEASurvival): @staticmethod def compute_geometry(front, extreme, n): m, _ = np.shape(front) # approximate p(norm) d = np.zeros(m) for i in range(0, m): d[i] = sum(front[i] ** 2) ** 0.5 d[extreme] = np.inf index = np.argmin(d) p = find_zero(front[index], n, 0.001) if np.isnan(p) or p <= 0.1: p = 1.0 elif p > 20: p = 20.0 # avoid numpy underflow return p @staticmethod @jit(nopython=True, fastmath=True) def pairwise_distances(front, p): m, n = front.shape projected_front = front.copy() for index in range(0, m): projected_front[index] = project_on_manifold(front[index], p=p) distances = np.zeros((m, m), dtype=numba.float64) if 0.95 < p < 1.05: for row in range(0, m - 1): for column in range(row + 1, m): distances[row][column] = np.sum(np.abs(projected_front[row] - projected_front[column]) ** 2) ** 0.5 else: for row in range(0, m-1): for column in range(row+1, m): mid_point = projected_front[row] * 0.5 + projected_front[column] * 0.5 mid_point = project_on_manifold(mid_point, p) distances[row][column] = np.sum(np.abs(projected_front[row] - mid_point) ** 2) ** 0.5 + \ np.sum(np.abs(projected_front[column] - mid_point) ** 2) ** 0.5 return distances + distances.T parse_doc_string(AGEMOEA2.__init__)