Project-Platypus / Platypus

A Free and Open Source Python Library for Multiobjective Optimization
GNU General Public License v3.0
565 stars 153 forks source link

Stopping criteria of Platypus #99

Closed ghost closed 1 year ago

ghost commented 5 years ago

Hey guys,

First of all thanks for providing such a valuable opensource code. Can you please let me know if there are any stopping criteria available like these:

  1. Function tolerance: The algorithm stops if the average relative change in the best fitness function value over lets say 200000 generations is less than or equal to Function tolerance.

  2. Time limit: To stop the optimization process after lets say 10 hours.

I think it is now obvious what programming language I am used to ;)

Thank you in advance.

dhadka commented 5 years ago

When you call algorithm.run(...), the first argument is the termination condition. By default, it is the number of function evaluations: algorithm.run(10000). But you can also pass in a TerminationCondition type. For example, algorithm.run(MaxTime(60)) would run for 1 minute (approximately). For function tolerance, you would need to subclass TerminationCondition and implement that check.

morecfd commented 5 years ago

I am also keen to see an, easy to implement, example of this stopping condition. Something like: the algorithm proceeds until the best solution during the evolution process doesn't change to a better value after a predefined number of generations.

apoorvreddy commented 5 years ago

This is a sample implementation:

import random
from platypus import Problem, Real, NSGAII, SPEA2, GAOperator, SBX, PM, TerminationCondition, ProcessPoolEvaluator
import sys

num_vars = 2
num_objs = 1
num_cons = 2

class EarlyStopping(TerminationCondition):
    def __init__(self, min_iterations, max_iterations, tolerance, patience):
        """
        Parameters:
        min_iterations (int): Run optimizer for at least this many iterations
        max_iterations (int): Do not run optimizer for more than this many iterations
        tolerance (float): Maximum change able to tolerate in function objective to declare convergence
        patience (int): Tolerance should be satisfied for steps = patience before early stopping
        """
        super(EarlyStopping, self).__init__()
        self.min_iterations = min_iterations
        self.max_iterations = max_iterations
        self.tolerance = tolerance
        self.patience = patience
        self.current_patience = 0
        self.last_iter_obj_value = 1e10 #sys.float_info.max
        self.iteration = -1

    def initialize(self, algorithm):
        pass

    def shouldTerminate(self, algorithm):
        self.iteration += 1
        if self.iteration < self.min_iterations:
            return False
        elif self.iteration >= self.max_iterations:
            return True
        else:
            for p in algorithm.population:
                if p.feasible:
                    self.current_iter_obj_value = p.objectives[0]
                    break
            print("iteration", self.iteration, "obj", p.objectives[0])
            if abs(self.current_iter_obj_value - self.last_iter_obj_value) <= self.tolerance:
                self.current_patience += 1
            else:
                self.current_patience = 0
            self.last_iter_obj_value = self.current_iter_obj_value

            if self.current_patience == self.patience:
                return True
        return False

# convex problem objective = ((x-5)/4)^2 + ((y-3)/2)^2 with constraint x >= 10 and y>=4. So solution is (10, 4)
def convex_objective(x):
    x0 = x[0]
    x1 = x[1]
    return ((x0-5)/4)**2 + ((x1-3)/2) ** 2, (x[0] - 10, x[1] - 4)

problem = Problem(num_vars, num_objs, num_cons)
problem.types[:] = Real(0, 50)
problem.directions[:] = Problem.MINIMIZE
problem.constraints[:] = '>=0'
problem.function = convex_objective

def run_opt():
    iteration_history = []
    random.seed(24837102)
    earlystop = EarlyStopping(1, 2, 1e-2, 30)
    with ProcessPoolEvaluator(8) as evaluator:
        algorithm = SPEA2(problem
                           , population_size=100
                           , variator=GAOperator(SBX(probability=0.9
                                                     , distribution_index=20)
                                                 , PM(probability=0.1
                                                      , distribution_index=10))
                           , evaluator=evaluator
                           , archive=iteration_history)
        algorithm.run(earlystop)
    return algorithm, iteration_history, earlystop

alg, hist, earlystop = run_opt()
print("Converged in {0} iterations".format(earlystop.count))
print("solution", alg.population[0])
print("iteration_history", hist)
github-actions[bot] commented 1 year ago

This issue is stale and will be closed soon. If you feel this issue is still relevant, please comment to keep it active. Please also consider working on a fix and submitting a PR.