Source code for deap.tools.constraint


from functools import wraps
from itertools import repeat

try:
    from collections.abc import Sequence
except ImportError:
    from collections import Sequence


[docs]class DeltaPenalty(object): r"""This decorator returns penalized fitness for invalid individuals and the original fitness value for valid individuals. The penalized fitness is made of a constant factor *delta* added with an (optional) *distance* penalty. The distance function, if provided, shall return a value growing as the individual moves away the valid zone. :param feasibility: A function returning the validity status of any individual. :param delta: Constant or array of constants returned for an invalid individual. :param distance: A function returning the distance between the individual and a given valid point. The distance function can also return a sequence of length equal to the number of objectives to affect multi-objective fitnesses differently (optional, defaults to 0). :returns: A decorator for evaluation function. This function relies on the fitness weights to add correctly the distance. The fitness value of the ith objective is defined as .. math:: f^\mathrm{penalty}_i(\mathbf{x}) = \Delta_i - w_i d_i(\mathbf{x}) where :math:`\mathbf{x}` is the individual, :math:`\Delta_i` is a user defined constant and :math:`w_i` is the weight of the ith objective. :math:`\Delta` should be worst than the fitness of any possible individual, this means higher than any fitness for minimization and lower than any fitness for maximization. See the :doc:`/tutorials/advanced/constraints` for an example. """ def __init__(self, feasibility, delta, distance=None): self.fbty_fct = feasibility if not isinstance(delta, Sequence): self.delta = repeat(delta) else: self.delta = delta self.dist_fct = distance def __call__(self, func): @wraps(func) def wrapper(individual, *args, **kwargs): if self.fbty_fct(individual): return func(individual, *args, **kwargs) weights = tuple(1 if w >= 0 else -1 for w in individual.fitness.weights) dists = tuple(0 for w in individual.fitness.weights) if self.dist_fct is not None: dists = self.dist_fct(individual) if not isinstance(dists, Sequence): dists = repeat(dists) return tuple(d - w * dist for d, w, dist in zip(self.delta, weights, dists)) return wrapper
DeltaPenality = DeltaPenalty
[docs]class ClosestValidPenalty(object): r"""This decorator returns penalized fitness for invalid individuals and the original fitness value for valid individuals. The penalized fitness is made of the fitness of the closest valid individual added with a weighted (optional) *distance* penalty. The distance function, if provided, shall return a value growing as the individual moves away the valid zone. :param feasibility: A function returning the validity status of any individual. :param feasible: A function returning the closest feasible individual from the current invalid individual. :param alpha: Multiplication factor on the distance between the valid and invalid individual. :param distance: A function returning the distance between the individual and a given valid point. The distance function can also return a sequence of length equal to the number of objectives to affect multi-objective fitnesses differently (optional, defaults to 0). :returns: A decorator for evaluation function. This function relies on the fitness weights to add correctly the distance. The fitness value of the ith objective is defined as .. math:: f^\mathrm{penalty}_i(\mathbf{x}) = f_i(\operatorname{valid}(\mathbf{x})) - \\alpha w_i d_i(\operatorname{valid}(\mathbf{x}), \mathbf{x}) where :math:`\mathbf{x}` is the individual, :math:`\operatorname{valid}(\mathbf{x})` is a function returning the closest valid individual to :math:`\mathbf{x}`, :math:`\\alpha` is the distance multiplicative factor and :math:`w_i` is the weight of the ith objective. """ def __init__(self, feasibility, feasible, alpha, distance=None): self.fbty_fct = feasibility self.fbl_fct = feasible self.alpha = alpha self.dist_fct = distance def __call__(self, func): @wraps(func) def wrapper(individual, *args, **kwargs): if self.fbty_fct(individual): return func(individual, *args, **kwargs) f_ind = self.fbl_fct(individual) # print("individual", f_ind) f_fbl = func(f_ind, *args, **kwargs) # print("feasible", f_fbl) weights = tuple(1.0 if w >= 0 else -1.0 for w in individual.fitness.weights) if len(weights) != len(f_fbl): raise IndexError("Fitness weights and computed fitness are of different size.") dists = tuple(0 for w in individual.fitness.weights) if self.dist_fct is not None: dists = self.dist_fct(f_ind, individual) if not isinstance(dists, Sequence): dists = repeat(dists) # print("penalty ", tuple( - w * self.alpha * d for f, w, d in zip(f_fbl, weights, dists))) # print("returned", tuple(f - w * self.alpha * d for f, w, d in zip(f_fbl, weights, dists))) return tuple(f - w * self.alpha * d for f, w, d in zip(f_fbl, weights, dists)) return wrapper
ClosestValidPenality = ClosestValidPenalty # List of exported function names. __all__ = ['DeltaPenalty', 'ClosestValidPenalty', 'DeltaPenality', 'ClosestValidPenality'] if __name__ == "__main__": from deap import base from deap import benchmarks from deap import creator import numpy MIN_BOUND = numpy.array([0] * 30) MAX_BOUND = numpy.array([1] * 30) creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0)) creator.create("Individual", list, fitness=creator.FitnessMin) def distance(feasible_ind, original_ind): """A distance function to the feasibility region.""" return sum((f - o)**2 for f, o in zip(feasible_ind, original_ind)) def closest_feasible(individual): """A function returning a valid individual from an invalid one.""" feasible_ind = numpy.array(individual) feasible_ind = numpy.maximum(MIN_BOUND, feasible_ind) feasible_ind = numpy.minimum(MAX_BOUND, feasible_ind) return feasible_ind def valid(individual): """Determines if the individual is valid or not.""" if any(individual < MIN_BOUND) or any(individual > MAX_BOUND): return False return True toolbox = base.Toolbox() toolbox.register("evaluate", benchmarks.zdt2) toolbox.decorate("evaluate", ClosestValidPenalty(valid, closest_feasible, 1.0e-6, distance)) ind1 = creator.Individual((-5.6468535666e-01, 2.2483050478e+00, -1.1087909644e+00, -1.2710112861e-01, 1.1682438733e+00, -1.3642007438e+00, -2.1916417835e-01, -5.9137308999e-01, -1.0870160336e+00, 6.0515070232e-01, 2.1532075914e+00, -2.6164718271e-01, 1.5244071578e+00, -1.0324305612e+00, 1.2858152343e+00, -1.2584683962e+00, 1.2054392372e+00, -1.7429571973e+00, -1.3517256013e-01, -2.6493429355e+00, -1.3051320798e-01, 2.2641961090e+00, -2.5027232340e+00, -1.2844874148e+00, 1.9955852925e+00, -1.2942218834e+00, 3.1340109155e+00, 1.6440111097e+00, -1.7750105857e+00, 7.7610242710e-01)) print(toolbox.evaluate(ind1)) print("Individuals is valid: %s" % ("True" if valid(ind1) else "False"))