Source code for metaheuristic_designer.constraint_handler
"""
Base class for the Constraint Handler module.
This module implements ways to enforce constraints on the objective function.
"""
from __future__ import annotations
from copy import copy
from typing import Any, Callable, Iterable, Optional
from abc import ABC, abstractmethod
from .parametrizable_mixin import ParametrizableMixin
from .utils import ScalarLike, VectorLike, MatrixLike
[docs]
class ConstraintHandler(ParametrizableMixin, ABC):
"""Abstract base for all constraint handlers.
A constraint handler can **repair** solutions (make them
feasible) and/or compute a **penalty** that is subtracted from
the objective value. Subclasses must implement at least one of
these operations.
Parameters
----------
encoding : Encoding, optional
An :class:`Encoding` that will be used to extract the
genotype before repair or penalty (default ``None``).
**kwargs
Additional keyword arguments stored as schedulable
parameters.
"""
def __init__(self, encoding=None, **kwargs):
super().__init__()
self.encoding = encoding
self.store_kwargs(**kwargs)
[docs]
@abstractmethod
def repair_solution(self, population_matrix: MatrixLike) -> MatrixLike:
"""
Modifies the incoming solution so that it follows the problem's constraints.
Parameters
----------
solution: Any
The input solution.
Returns
-------
fixed_solution: Any
Modified version of the input solution that fits the problem's constraints
"""
[docs]
@abstractmethod
def penalty(self, population_matrix: MatrixLike) -> VectorLike:
"""
Offset to the objective value for the solution corresponding to violations of the problem's constraints.
Parameters
----------
solution: Any
The input solution.
Returns
-------
penalty: float
The amount of penalty to apply to the current solution.
"""
[docs]
def get_state(self):
data = {
"class_name": self.__class__.__name__,
}
return data
[docs]
class ConstraintHandlerFromLambda(ConstraintHandler):
"""Constraint handler built from plain callables.
At least one of *repair_solution_fn* or *penalty_fn* must be
given.
Parameters
----------
repair_solution_fn : callable, optional
A function ``(solution) -> repaired_solution``.
penalty_fn : callable, optional
A function ``(solution) -> penalty_value``.
**kwargs
Keyword arguments forwarded to
:class:`ConstraintHandler`.
"""
def __init__(self, repair_solution_fn: Optional[Callable] = None, penalty_fn: Optional[Callable] = None, **kwargs):
super().__init__(**kwargs)
if repair_solution_fn is None and penalty_fn is None:
raise ValueError("You must give the implementation of the repairing procedure or the penalty calculation.")
self.repair_solution_fn = repair_solution_fn
self.penalty_fn = penalty_fn
[docs]
def repair_solution(self, solution: Iterable) -> Iterable:
if self.repair_solution_fn is None:
return copy(solution)
return self.repair_solution_fn(solution)
[docs]
def penalty(self, solution: Any) -> ScalarLike:
if self.penalty_fn is None:
return 0
return self.penalty_fn(solution)
[docs]
class NullConstraint(ConstraintHandler):
"""Constraint handler that enforces no restrictions.
The penalty is always zero, and repairing returns the solution
unchanged.
Parameters
----------
encoding : Encoding, optional
See :class:`ConstraintHandler`.
**kwargs
See :class:`ConstraintHandler`.
"""
[docs]
def repair_solution(self, solution: MatrixLike) -> MatrixLike:
return copy(solution)
[docs]
def penalty(self, _solution: Any) -> ScalarLike:
return 0
[docs]
class PenalizeConstraint(ConstraintHandler, ABC):
"""Abstract handler that only computes penalties.
Repairing does nothing (returns a copy).
Subclasses must override :meth:`penalty`.
Parameters
----------
encoding : Encoding, optional
See :class:`ConstraintHandler`.
**kwargs
See :class:`ConstraintHandler`.
"""
[docs]
def repair_solution(self, solution: Iterable) -> Iterable:
return copy(solution)
[docs]
class RepairConstraint(ConstraintHandler, ABC):
"""Abstract handler that only repairs solutions.
The penalty is always zero.
Subclasses must override :meth:`repair_solution`.
Parameters
----------
encoding : Encoding, optional
See :class:`ConstraintHandler`.
**kwargs
See :class:`ConstraintHandler`.
"""
[docs]
def penalty(self, _solution: Iterable) -> VectorLike:
return 0