# encoding=utf8
import logging
import numpy as np
from niapy.algorithms.algorithm import Algorithm
logging.basicConfig()
logger = logging.getLogger("niapy.algorithms.basic")
logger.setLevel("INFO")
__all__ = ["HarmonySearch", "HarmonySearchV1"]
[docs]class HarmonySearch(Algorithm):
r"""Implementation of Harmony Search algorithm.
Algorithm:
Harmony Search Algorithm
Date:
2018
Authors:
Klemen Berkovič
License:
MIT
Reference URL:
https://journals.sagepub.com/doi/10.1177/003754970107600201
Reference paper:
Geem, Z. W., Kim, J. H., & Loganathan, G. V. (2001). A new heuristic optimization algorithm: harmony search. Simulation, 76(2), 60-68.
Attributes:
Name (List[str]): List of strings representing algorithm names
r_accept (float): Probability of accepting new bandwidth into harmony.
r_pa (float): Probability of accepting random bandwidth into harmony.
b_range (float): Range of bandwidth.
See Also:
* :class:`niapy.algorithms.algorithm.Algorithm`
"""
Name = ["HarmonySearch", "HS"]
[docs] @staticmethod
def info():
r"""Get basic information about the algorithm.
Returns:
str: Basic information.
"""
return r"""Geem, Z. W., Kim, J. H., & Loganathan, G. V. (2001). A new heuristic optimization algorithm: harmony search. Simulation, 76(2), 60-68."""
[docs] def __init__(self, population_size=30, r_accept=0.7, r_pa=0.35, b_range=1.42, *args, **kwargs):
"""Initialize HarmonySearch.
Args:
population_size (Optional[int]): Number of harmony in the memory.
r_accept (Optional[float]): Probability of accepting new bandwidth to harmony.
r_pa (Optional[float]): Probability of accepting random bandwidth into harmony.
b_range (Optional[float]): Bandwidth range.
"""
super().__init__(population_size, *args, **kwargs)
self.r_accept = r_accept
self.r_pa = r_pa
self.b_range = b_range
[docs] def set_parameters(self, population_size=30, r_accept=0.7, r_pa=0.35, b_range=1.42, **kwargs):
r"""Set the arguments of the algorithm.
Args:
population_size (Optional[int]): Number of harmony in the memory.
r_accept (Optional[float]): Probability of accepting new bandwidth to harmony.
r_pa (Optional[float]): Probability of accepting random bandwidth into harmony.
b_range (Optional[float]): Bandwidth range.
See Also:
* :func:`niapy.algorithms.algorithm.Algorithm.set_parameters`
"""
super().set_parameters(population_size=population_size, **kwargs)
self.r_accept = r_accept
self.r_pa = r_pa
self.b_range = b_range
[docs] def get_parameters(self):
"""Get algorithm parameters."""
d = super().get_parameters()
d.update({
'r_accept': self.r_accept,
'r_pa': self.r_pa,
'b_range': self.b_range
})
return d
[docs] def bw(self, task):
r"""Get bandwidth.
Args:
task (Task): Optimization task.
Returns:
float: Bandwidth.
"""
return self.uniform(-1, 1) * self.b_range
[docs] def adjustment(self, x, task):
r"""Adjust value based on bandwidth.
Args:
x (Union[int, float]): Current position.
task (Task): Optimization task.
Returns:
float: New position.
"""
return x + self.bw(task)
[docs] def improvise(self, harmonies, task):
r"""Create new individual.
Args:
harmonies (numpy.ndarray): Current population.
task (Task): Optimization task.
Returns:
numpy.ndarray: New individual.
"""
harmony = np.zeros(task.dimension)
for i in range(task.dimension):
r, j = self.random(), self.integers(self.population_size)
harmony[i] = harmonies[j, i] if r > self.r_accept else self.adjustment(harmonies[j, i], task) if r > self.r_pa else self.uniform(
task.lower[i], task.upper[i])
return harmony
[docs] def run_iteration(self, task, population, population_fitness, best_x, best_fitness, **params):
r"""Core function of HarmonySearch algorithm.
Args:
task (Task): Optimization task.
population (numpy.ndarray): Current population.
population_fitness (numpy.ndarray): Current populations function/fitness values.
best_x (numpy.ndarray): Global best individual.
best_fitness (float): Global best fitness/function value.
**params (Dict[str, Any]): Additional arguments.
Returns:
Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, float, Dict[str, Any]]:
1. New harmony/population.
2. New populations function/fitness values.
3. New global best solution
4. New global best solution fitness/objective value
5. Additional arguments.
"""
harmony = self.improvise(population, task)
harmony_fitness = task.eval(task.repair(harmony, self.rng))
iw = np.argmax(population_fitness)
if harmony_fitness <= population_fitness[iw]:
population[iw], population_fitness[iw] = harmony, harmony_fitness
best_x, best_fitness = self.get_best(harmony, harmony_fitness, best_x, best_fitness)
return population, population_fitness, best_x, best_fitness, {}
[docs]class HarmonySearchV1(HarmonySearch):
r"""Implementation of harmony search algorithm.
Algorithm:
Harmony Search Algorithm
Date:
2018
Authors:
Klemen Berkovič
License:
MIT
Reference URL:
https://link.springer.com/chapter/10.1007/978-3-642-00185-7_1
Reference paper:
Yang, Xin-She. "Harmony search as a metaheuristic algorithm." Music-inspired harmony search algorithm. Springer, Berlin, Heidelberg, 2009. 1-14.
Attributes:
Name (List[str]): List of strings representing algorithm name.
bw_min (float): Minimal bandwidth.
bw_max (float): Maximal bandwidth.
See Also:
* :class:`niapy.algorithms.basic.hs.HarmonySearch`
"""
Name = ["HarmonySearchV1", "HSv1"]
[docs] @staticmethod
def info():
r"""Get basic information about algorithm.
Returns:
str: Basic information.
"""
return r"""Yang, Xin-She. "Harmony search as a metaheuristic algorithm." Music-inspired harmony search algorithm. Springer, Berlin, Heidelberg, 2009. 1-14."""
[docs] def __init__(self, bw_min=1, bw_max=2, *args, **kwargs):
"""Initialize HarmonySearchV1.
Args:
bw_min (Optional[float]): Minimal bandwidth.
bw_max (Optional[float]): Maximal bandwidth.
"""
super().__init__(*args, **kwargs)
self.bw_min = bw_min
self.bw_max = bw_max
[docs] def set_parameters(self, bw_min=1, bw_max=2, **kwargs):
r"""Set the parameters of the algorithm.
Args:
bw_min (Optional[float]): Minimal bandwidth
bw_max (Optional[float]): Maximal bandwidth
See Also:
* :func:`niapy.algorithms.basic.hs.HarmonySearch.set_parameters`
"""
super().set_parameters(**kwargs)
self.bw_min, self.bw_max = bw_min, bw_max
[docs] def get_parameters(self):
"""Get algorithm parameters."""
d = super().get_parameters()
d.update({
'bw_min': self.bw_min,
'bw_max': self.bw_max
})
return d
[docs] def bw(self, task):
r"""Get new bandwidth.
Args:
task (Task): Optimization task.
Returns:
float: New bandwidth.
"""
return self.bw_min * np.exp(np.log(self.bw_min / self.bw_max) * (task.iters + 1) / task.max_iters)