Source code for zellij.strategies.tools.cooling

# @Author: Thomas Firmin <ThomasFirmin>
# @Date:   2022-05-03T15:41:48+02:00
# @Email:  thomas.firmin@univ-lille.fr
# @Project: Zellij
# @Last modified by:   tfirmin
# @Last modified time: 2022-10-03T22:37:19+02:00
# @License: CeCILL-C (http://www.cecill.info/index.fr.html)


import numpy as np
from abc import abstractmethod

import logging

logger = logging.getLogger("zellij.cooling")


[docs]class Cooling(object): """Cooling Cooling is a base object which defines what a cooling Schedule is. Attributes ---------- Tcurrent : float Current temperature cross : int Count the number of times Tend is crossed. T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ <T0>. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ <peaks> times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, T0, Tend, peaks=1): ############## # PARAMETERS # ############## assert ( T0 > Tend ), f"T0 must be stricly greater than Tend, got {T0}>{Tend}" self.T0 = T0 self.Tend = Tend self.peaks = peaks ############# # VARIABLES # ############# self.Tcurrent = self.T0 self.k = 0 self.cross = 0
[docs] @abstractmethod def cool(self): pass
[docs] @abstractmethod def iterations(self): pass
[docs] def reset(self): self.Tcurrent = self.T0 self.k = 0 self.cross = 0
[docs]class MulExponential(Cooling): """MulExponential Exponential multiplicative monotonic cooling. :math:`T_k = T_0.\\alpha^k` Attributes ---------- alpha : float Decrease factor. :math:`0.8 \\leq \\alpha \\leq 0.9` T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ :code:`T0`. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ :code:`peaks` times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, alpha, T0, Tend, peaks=1): super().__init__(T0, Tend, peaks) self.alpha = alpha
[docs] def cool(self): self.Tcurrent = self.T0 * self.alpha**self.k if self.Tcurrent <= self.Tend: self.cross += 1 self.k = 0 else: self.k += 1 return self.Tcurrent if self.cross < self.peaks else False
[docs] def iterations(self): return ( int(np.ceil(np.log(self.Tend / self.T0) / np.log(self.alpha))) * self.peaks )
[docs]class MulLogarithmic(Cooling): """MulLogarithmic Logarithmic multiplicative monotonic cooling. :math:`T_k = \\frac{T_0}{1+\\alpha.log(1+k)}` Parameters ---------- alpha : float Decrease factor. :math:`\\alpha>1` T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ :code:`T0`. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ :code:`peaks` times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, alpha, T0, Tend, peaks=1): super().__init__(T0, Tend, peaks) self.alpha = alpha
[docs] def cool(self): self.Tcurrent = self.T0 / (1 + self.alpha * np.log(1 + self.k)) if self.Tcurrent <= self.Tend: self.cross += 1 self.k = 0 else: self.k += 1 return self.Tcurrent if self.cross < self.peaks else False
[docs] def iterations(self): return ( int(np.ceil(np.exp((self.T0 / self.Tend - 1 / self.alpha)) + 1)) * self.peaks )
[docs]class MulLinear(Cooling): """MulLinear Linear multiplicative monotonic cooling. :math:`T_k = \\frac{T_0}{1+\\alpha.k}` Parameters ---------- alpha : float Decrease factor. :math:`\\alpha>0` T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ :code:`T0`. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ :code:`peaks` times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, alpha, T0, Tend, peaks=1): super().__init__(T0, Tend, peaks) self.alpha = alpha
[docs] def cool(self): self.Tcurrent = self.T0 / (1 + self.alpha * self.k) if self.Tcurrent <= self.Tend: self.cross += 1 self.k = 0 else: self.k += 1 return self.Tcurrent if self.cross < self.peaks else False
[docs] def iterations(self): return int(np.ceil(self.T0 / (self.Tend * self.alpha))) * self.peaks
[docs]class MulQuadratic(Cooling): """MulQuadratic Quadratic multiplicative monotonic cooling. :math:`T_k = \\frac{T_0}{1+\\alpha.k^2}` Parameters ---------- alpha : float Decrease factor. :math:`\\alpha>0` T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ :code:`T0`. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ :code:`peaks` times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, alpha, T0, Tend, peaks=1): super().__init__(T0, Tend, peaks) self.alpha = alpha
[docs] def cool(self): self.Tcurrent = self.T0 / (1 + self.alpha * self.k**2) if self.Tcurrent <= self.Tend: self.cross += 1 self.k = 0 else: self.k += 1 return self.Tcurrent if self.cross < self.peaks else False
[docs] def iterations(self): return ( int(np.ceil(np.sqrt(self.T0 / (self.Tend * self.alpha)))) * self.peaks )
[docs]class AddLinear(Cooling): """AddLinear Linear additive monotonic cooling. :math:`T_k = T_{end} + (T_0-T_{end})\\left(\\frac{cycles-k}{cycles}\\right)` Parameters ---------- cycles : int Number of cooling cycles. T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ :code:`T0`. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ :code:`peaks` times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, cycles, T0, Tend, peaks=1): super().__init__(T0, Tend, peaks) self.cycles = cycles
[docs] def cool(self): self.Tcurrent = self.Tend + (self.T0 - self.Tend) * ( (self.cycles - self.k) / self.cycles ) if self.k == self.cycles: self.cross += 1 self.k = 0 else: self.k += 1 return self.Tcurrent if self.cross < self.peaks else False
[docs] def iterations(self): return self.cycles * self.peaks
[docs]class AddQuadratic(Cooling): """AddQuadratic Quadratic additive monotonic cooling. :math:`T_k = T_{end} + (T_0-T_{end})\\left(\\frac{cycles-k}{cycles}\\right)^2` Attributes ---------- cycles : int Number of cooling cycles. T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ :code:`T0`. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ :code:`peaks` times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, cycles, T0, Tend, peaks=1): super().__init__(T0, Tend, peaks) self.cycles = cycles
[docs] def cool(self): self.Tcurrent = ( self.Tend + (self.T0 - self.Tend) * ((self.cycles - self.k) / self.cycles) ** 2 ) if self.k == self.cycles: self.cross += 1 self.k = 0 else: self.k += 1 return self.Tcurrent if self.cross < self.peaks else False
[docs] def iterations(self): return self.cycles * self.peaks
[docs]class AddExponential(Cooling): """AddExponential Exponential additive monotonic cooling. :math:`T_k = T_{end} + \\frac{T_0-T_{end}}{1+e^{\\frac{2ln(T_0-T_{end})}{cycles}}(k-0,5cycles)}` Attributes ---------- cycles : int Number of cooling cycles. T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ :code:`T0`. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ :code:`peaks` times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, cycles, T0, Tend, peaks=1): super().__init__(T0, Tend, peaks) self.cycles = cycles
[docs] def cool(self): self.Tcurrent = self.Tend + (self.T0 - self.Tend) * ( 1 / ( 1 + np.exp( (2 * np.log(self.T0 - self.Tend) / self.cycles) * (self.k - 0.5 * self.cycles) ) ) ) if self.k == self.cycles: self.cross += 1 self.k = 0 else: self.k += 1 return self.Tcurrent if self.cross < self.peaks else False
[docs] def iterations(self): return self.cycles * self.peaks
[docs]class AddTrigonometric(Cooling): """AddTrigonometric Trigonometric additive monotonic cooling. :math:`T_k = T_{end} + 0,5(T_0-T_{end})(1+cos(\\frac{k.\\pi}{cycles}))` Attributes ---------- cycles : int Number of cooling cycles. T0 : float Initial temperature of the cooling schedule.\ Higher temperature leads to higher acceptance of a worse solution. (more exploration) Tend : float Temperature threshold. When reached the temperature is violently increased proportionally to\ :code:`T0`. It allows to periodically easily escape from local optima. peaks : int, default=1 Maximum number of crossed threshold according to :code:`Tend`. The temperature will be increased\ :code:`peaks` times. Methods ------- cool() Decrease temperature and return the current temperature. reset() Reset cooling schedule iterations() Get the theoretical number of iterations to end the schedule. """ def __init__(self, cycles, T0, Tend, peaks=1): super().__init__(T0, Tend, peaks) self.cycles = cycles
[docs] def cool(self): self.Tcurrent = self.Tend + 0.5 * (self.T0 - self.Tend) * ( 1 + np.cos(self.k * np.pi / self.cycles) ) if self.k == self.cycles: self.cross += 1 self.k = 0 else: self.k += 1 return self.Tcurrent if self.cross < self.peaks else False
[docs] def iterations(self): return self.cycles * self.peaks