Skip to content

Adding termination callback for arbitrary termination. #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions pyade/de.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ def get_default_params(dim: int) -> dict:
:rtype dict
"""
return {'callback': None, 'max_evals': 10000 * dim, 'seed': None, 'cross': 'bin',
'f': 0.5, 'cr': 0.9, 'individual_size': dim, 'population_size': 10 * dim, 'opts': None}
'f': 0.5, 'cr': 0.9, 'individual_size': dim, 'population_size': 10 * dim, 'opts': None,
'terminate_callback': None}


def apply(population_size: int, individual_size: int, f: Union[float, int],
cr: Union[float, int], bounds: np.ndarray,
func: Callable[[np.ndarray], float], opts: Any,
callback: Callable[[Dict], Any],
cross: str,
max_evals: int, seed: Union[int, None]) -> [np.ndarray, int]:
max_evals: int, seed: Union[int, None],
terminate_callback: Callable[[], bool]) -> [np.ndarray, int]:
"""
Applies the standard differential evolution algorithm.
:param population_size: Size of the population.
Expand Down Expand Up @@ -50,6 +52,8 @@ def apply(population_size: int, individual_size: int, f: Union[float, int],
:param seed: Random number generation seed. Fix a number to reproduce the
same results in later experiments.
:type seed: Union[int, None]
:param terminate_callback: Callback that checks whether it is time to terminate or not. The callback should return True if it's time to stop, otherwise False.
:type terminate_callback: Callable[[], bool]
:return: A pair with the best solution found and its fitness.
:rtype [np.ndarray, int]
"""
Expand Down Expand Up @@ -89,6 +93,9 @@ def apply(population_size: int, individual_size: int, f: Union[float, int],

max_iters = max_evals // population_size
for current_generation in range(max_iters):
if terminate_callback is not None and terminate_callback():
break

mutated = pyade.commons.binary_mutation(population, f, bounds)
if cross == 'bin':
crossed = pyade.commons.crossover(population, mutated, cr)
Expand Down
11 changes: 8 additions & 3 deletions pyade/ilshade.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ def get_default_params(dim: int):
:rtype dict
"""
return {'population_size': 12 * dim, 'individual_size': dim, 'memory_size': 6,
'max_evals': 10000 * dim, 'callback': None, 'seed': None, 'opts': None}
'max_evals': 10000 * dim, 'callback': None, 'seed': None, 'opts': None,
'terminate_callback': None}


def apply(population_size: int, individual_size: int, bounds: np.ndarray,
func: Callable[[np.ndarray], float], opts: Any,
memory_size: int, callback: Callable[[Dict], Any],
max_evals: int, seed: Union[int, None]) -> [np.ndarray, int]:
max_evals: int, seed: Union[int, None],
terminate_callback: Callable[[], bool]) -> [np.ndarray, int]:
"""
Applies the iL-SHADE differential evolution algorithm.
:param population_size: Size of the population.
Expand All @@ -46,6 +48,8 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
:param seed: Random number generation seed. Fix a number to reproduce the
same results in later experiments.
:type seed: Union[int, None]
:param terminate_callback: Callback that checks whether it is time to terminate or not. The callback should return True if it's time to stop, otherwise False.
:type terminate_callback: Callable[[], bool]
:return: A pair with the best solution found and its fitness.
:rtype [np.ndarray, int]
"""
Expand Down Expand Up @@ -96,7 +100,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
n = round((4 - population_size) / max_evals * i + population_size)
i += n

while num_evals < max_evals:
while num_evals < max_evals and (terminate_callback is not None and not terminate_callback()):
# 2.1 Adaptation
r = np.random.choice(memory_indexes, current_size)
m_cr[- 1] = 0.9
Expand Down Expand Up @@ -138,6 +142,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
archive = random.sample(archive, population_size)

weights = np.abs(fitness[indexes] - c_fitness[indexes])
weights = weights.astype(float)
weights /= np.sum(weights)

if max(cr) != 0:
Expand Down
10 changes: 8 additions & 2 deletions pyade/jade.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ def get_default_params(dim: int) -> dict:
"""
pop_size = 10 * dim
return {'max_evals': 10000 * dim, 'individual_size': dim, 'callback': None,
'population_size': pop_size, 'c': 0.1, 'p': max(.05, 3/pop_size), 'seed': None}
'population_size': pop_size, 'c': 0.1, 'p': max(.05, 3/pop_size), 'seed': None,
'opts': None, 'terminate_callback': None}


def apply(population_size: int, individual_size: int, bounds: np.ndarray,
func: Callable[[np.ndarray], float], opts: Any,
p: Union[int, float], c: Union[int, float], callback: Callable[[Dict], Any],
max_evals: int, seed: Union[int, None]) -> [np.ndarray, int]:
max_evals: int, seed: Union[int, None],
terminate_callback: Callable[[], bool]) -> [np.ndarray, int]:
"""
Applies the JADE Differential Evolution algorithm.
:param population_size: Size of the population.
Expand All @@ -47,6 +49,8 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
:param seed: Random number generation seed. Fix a number to reproduce the
same results in later experiments.
:type seed: Union[int, None]
:param terminate_callback: Callback that checks whether it is time to terminate or not. The callback should return True if it's time to stop, otherwise False.
:type terminate_callback: Callable[[], bool]
:return: A pair with the best solution found and its fitness.
:rtype [np.ndarray, int]
"""
Expand Down Expand Up @@ -84,6 +88,8 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
fitness = pyade.commons.apply_fitness(population, func, opts)
max_iters = max_evals // population_size
for current_generation in range(max_iters):
if terminate_callback is not None and terminate_callback():
break
# 2.1 Generate parameter values for current generation
cr = np.random.normal(u_cr, 0.1, population_size)
f = np.random.rand(population_size // 3) * 1.2
Expand Down
11 changes: 8 additions & 3 deletions pyade/jso.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ def get_default_params(dim: int):
"""
return {'population_size': int(round(25 * np.log(dim) * np.sqrt(dim))),
'individual_size': dim, 'memory_size': 5,
'max_evals': 10000 * dim, 'seed': None, 'callback': None, 'opts': None}
'max_evals': 10000 * dim, 'seed': None, 'callback': None, 'opts': None,
'terminate_callback': None}


def apply(population_size: int, individual_size: int, bounds: np.ndarray,
func: Callable[[np.ndarray], float], opts: Any,
memory_size: int, callback: Callable[[Dict], Any],
max_evals: int, seed: Union[int, None]) -> [np.ndarray, int]:
max_evals: int, seed: Union[int, None],
terminate_callback: Callable[[], bool]) -> [np.ndarray, int]:
"""
Applies the jSO differential evolution algorithm.
:param population_size: Size of the population.
Expand All @@ -46,6 +48,8 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
:param seed: Random number generation seed. Fix a number to reproduce the
same results in later experiments.
:type seed: Union[int, None]
:param terminate_callback: Callback that checks whether it is time to terminate or not. The callback should return True if it's time to stop, otherwise False.
:type terminate_callback: Callable[[], bool]
:return: A pair with the best solution found and its fitness.
:rtype [np.ndarray, int]
"""
Expand Down Expand Up @@ -96,7 +100,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
n = round((4 - population_size) / max_evals * i + population_size)
i += n

while num_evals < max_evals:
while num_evals < max_evals and (terminate_callback is not None and not terminate_callback()):
# 2.1 Adaptation
r = np.random.choice(memory_indexes, current_size)
m_cr[- 1] = 0.9
Expand Down Expand Up @@ -152,6 +156,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
archive = random.sample(archive, population_size)

weights = np.abs(fitness[indexes] - c_fitness[indexes])
weights = weights.astype(float)
weights /= np.sum(weights)

if max(cr) != 0:
Expand Down
13 changes: 9 additions & 4 deletions pyade/lshade.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ def get_default_params(dim: int):
:rtype dict
"""
return {'max_evals': 10000 * dim, 'population_size': 18 * dim, 'individual_size': dim,
'memory_size': 6, 'callback': None, 'seed': None, 'opts': None}
'memory_size': 6, 'callback': None, 'seed': None, 'opts': None,
'terminate_callback': None}


def apply(population_size: int, individual_size: int, bounds: np.ndarray,
func: Callable[[np.ndarray], np.float], opts: Any,
func: Callable[[np.ndarray], float], opts: Any,
memory_size: int, callback: Callable[[Dict], Any],
max_evals: int, seed: Union[int, None]) -> [np.ndarray, int]:
max_evals: int, seed: Union[int, None],
terminate_callback: Callable[[], bool]) -> [np.ndarray, int]:
"""
Applies the L-SHADE Differential Evolution Algorithm.
:param population_size: Size of the population.
Expand All @@ -46,6 +48,8 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
:param seed: Random number generation seed. Fix a number to reproduce the
same results in later experiments.
:type seed: Union[int, None]
:param terminate_callback: Callback that checks whether it is time to terminate or not. The callback should return True if it's time to stop, otherwise False.
:type terminate_callback: Callable[[], bool]
:return: A pair with the best solution found and its fitness.
:rtype [np.ndarray, int]
"""
Expand Down Expand Up @@ -96,7 +100,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
n = round((4 - init_size) / max_evals * i + init_size)
i += n

while num_evals < max_evals:
while num_evals < max_evals and (terminate_callback is not None and not terminate_callback()):
# 2.1 Adaptation
r = np.random.choice(all_indexes, population_size)
cr = np.random.normal(m_cr[r], 0.1, population_size)
Expand Down Expand Up @@ -127,6 +131,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
archive = random.sample(archive, population_size)

weights = np.abs(fitness[indexes] - c_fitness[indexes])
weights = weights.astype(float)
weights /= np.sum(weights)
m_cr[k] = np.sum(weights * cr[indexes] ** 2) / np.sum(weights * cr[indexes])
if np.isnan(m_cr[k]):
Expand Down
26 changes: 17 additions & 9 deletions pyade/lshadecnepsin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ def get_default_params(dim: int):
return {'population_size': 18 * dim,
'min_population_size': 4,
'individual_size': dim, 'memory_size': 5,
'max_evals': 10000 * dim, 'seed': None, 'callback': None, 'opts': None}
'max_evals': 10000 * dim, 'seed': None, 'callback': None, 'opts': None,
'terminate_callback': None}


def apply(population_size: int, individual_size: int, bounds: np.ndarray,
func: Callable[[np.ndarray], float], opts: Any,
memory_size: int, callback: Callable[[Dict], Any],
min_population_size: int,
max_evals: int, seed: Union[int, None]) -> [np.ndarray, int]:
max_evals: int, seed: Union[int, None],
terminate_callback: Callable[[], bool]) -> [np.ndarray, int]:
"""
Applies the L-SHADE-cnEpSin differential evolution algorithm.
:param population_size: Size of the population (NP-max)
Expand All @@ -51,6 +53,8 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
:param seed: Random number generation seed. Fix a number to reproduce the
same results in later experiments.
:type seed: Union[int, None]
:param terminate_callback: Callback that checks whether it is time to terminate or not. The callback should return True if it's time to stop, otherwise False.
:type terminate_callback: Callable[[], bool]
:return: A pair with the best solution found and its fitness.
:rtype [np.ndarray, int]
"""
Expand Down Expand Up @@ -105,13 +109,16 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
n = population_size
i = 0
max_iters = 0
while i < max_evals:
max_iters += 1
n = round((min_population_size - population_size) / max_evals * i + population_size)
i += n
if max_evals > 1e8:
max_iters = 200 # Probably wrong, but what else should I do here? the following loop goes crazy if max_evals is too high
else:
while i < max_evals: # TODO causes problems with big values...
max_iters += 1
n = round((min_population_size - population_size) / max_evals * i + population_size)
i += n

current_generation = 0
while num_evals < max_evals:
while num_evals < max_evals and (terminate_callback is not None and not terminate_callback()):
# Mutation
if current_generation <= (max_iters / 2):
if current_generation <= lp:
Expand Down Expand Up @@ -197,7 +204,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
sel = round(ps * current_size)
xmean = np.mean(xsel, axis=1)

aux = np.ones((sel, 1), dtype=np.bool)
aux = np.ones((sel, 1), dtype=bool)
xsel = xsel.T
c = 1 / (sel - 1) * np.dot(xsel - xmean, (xsel - xmean).T)
c = np.triu(c) + np.triu(c, 1).T
Expand Down Expand Up @@ -245,6 +252,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
# Update memory
if len(indexes) > 0:
weights = np.abs(fitness[indexes] - crossed_fitness[indexes])
weights = weights.astype(float)
weights /= np.sum(weights)
if max(cr[indexes]) != 0:
u_cr[k] = np.sum(weights * cr[indexes] ** 2) / np.sum(weights * cr[indexes])
Expand All @@ -253,7 +261,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
u_f[k] = np.sum(weights * f[indexes] ** 2) / np.sum(weights * f[indexes])

if lp < current_generation < (max_iters / 2):
chosen = np.logical_and(np.array(option == 'p2', dtype=np.bool), winners)
chosen = np.logical_and(np.array(option == 'p2', dtype=bool), winners)
if len(freq_i[chosen]) != 0:
u_freq[k] = np.mean(freq_i[chosen])
if np.isnan(u_freq[k]):
Expand Down
6 changes: 3 additions & 3 deletions pyade/mmts.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ def local_search_3(individual: np.ndarray, reset_sr: np.ndarray, search_range: U
return grade, best_solution, best_fitness, new_individual, apply_func_with_opts(new_individual, func, opts), num_evals

def mmts(population: np.ndarray, bounds: np.ndarray, fitness: np.ndarray, max_evals: int, func, opts: Any):
enable = np.ones(population.shape[0], np.bool)
improve = np.ones(population.shape[0], np.bool)
enable = np.ones(population.shape[0], bool)
improve = np.ones(population.shape[0], bool)
minimum, maximum = bounds[0]
search_range = (maximum - minimum) / 2
reset_sr = np.ones(population.shape[0]) * .4 * search_range
Expand Down Expand Up @@ -263,7 +263,7 @@ def mmts(population: np.ndarray, bounds: np.ndarray, fitness: np.ndarray, max_ev
best_fitness = search[2]
num_evals += search[5]

enable = np.zeros(population.shape[0], np.bool)
enable = np.zeros(population.shape[0], bool)
best_grades = np.argsort(grades)[::-1][:num_foreground]
enable[best_grades] = True

Expand Down
15 changes: 9 additions & 6 deletions pyade/mpede.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ def get_default_params(dim: int) -> dict:
pop_size = 250
return {'max_evals': 10000 * dim, 'individual_size': dim, 'callback': None,
'population_size': pop_size, 'seed': None, 'lambdas': [0.2, 0.2, 0.2, 0.4],
'ng': 20, 'c': 0.1, 'p': 0.04, 'opts': None
}
'ng': 20, 'c': 0.1, 'p': 0.04, 'opts': None,
'terminate_callback': None}


def apply(population_size: int, individual_size: int, bounds: np.ndarray,
func: Callable[[np.ndarray], float], opts: Any,
callback: Callable[[Dict], Any],
lambdas: Union[list, np.array],
ng: int, c: Union[int, float], p: Union[int, float],
max_evals: int, seed: Union[int, None]) -> [np.ndarray, int]:
max_evals: int, seed: Union[int, None],
terminate_callback: Callable[[], bool]) -> [np.ndarray, int]:

"""
Applies the MPEDE differential evolution algorithm.
Expand Down Expand Up @@ -57,6 +58,8 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
:param c: Variable to control parameter adoption. Must be in [0, 1].
:type c: Union[int, float]
:type seed: Union[int, None]
:param terminate_callback: Callback that checks whether it is time to terminate or not. The callback should return True if it's time to stop, otherwise False.
:type terminate_callback: Callable[[], bool]
:return: A pair with the best solution found and its fitness.
:rtype [np.ndarray, int]

Expand Down Expand Up @@ -123,7 +126,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
num_evals += len(pops[j])

# 2. Start the algorithm
while num_evals <= max_evals:
while num_evals < max_evals and (terminate_callback is not None and not terminate_callback()):
current_generation += 1

# 2.1 Generate CR and F values
Expand Down Expand Up @@ -192,7 +195,7 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
k = [f_var[i] / len(pops[i] / ng) for i in range(3)]
chosen = np.argmax(k)

indexes = np.arange(0, len(population), 1, np.int)
indexes = np.arange(0, len(population), 1, int)
np.random.shuffle(indexes)
indexes = np.array_split(indexes, 4)
chosen = np.random.randint(0, 3)
Expand All @@ -218,4 +221,4 @@ def apply(population_size: int, individual_size: int, bounds: np.ndarray,
callback(**(locals()))

best = np.argmin(fitness)
return population[best], fitness[best]
return population[best], fitness[best]
10 changes: 8 additions & 2 deletions pyade/sade.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ def get_default_params(dim: int) -> dict:
:rtype dict
"""
return {'max_evals': 10000 * dim, 'population_size': 10 * dim, 'callback': None,
'individual_size': dim, 'seed': None, 'opts': None}
'individual_size': dim, 'seed': None, 'opts': None,
'terminate_callback': None}


def apply(population_size: int, individual_size: int,
bounds: np.ndarray,
func: Callable[[np.ndarray], float], opts: Any,
callback: Callable[[Dict], Any],
max_evals: int, seed: Union[int, None]):
max_evals: int, seed: Union[int, None],
terminate_callback: Callable[[], bool]) -> [np.ndarray, int]:
"""
Applies the Self-adaptive differential evolution algorithm (SaDE).
:param population_size: Size of the population.
Expand All @@ -42,6 +44,8 @@ def apply(population_size: int, individual_size: int,
:param seed: Random number generation seed. Fix a number to reproduce the
same results in later experiments.
:type seed: Union[int, None]
:param terminate_callback: Callback that checks whether it is time to terminate or not. The callback should return True if it's time to stop, otherwise False.
:type terminate_callback: Callable[[], bool]
:return: A pair with the best solution found and its fitness.
:rtype [np.ndarray, int]
"""
Expand Down Expand Up @@ -87,6 +91,8 @@ def apply(population_size: int, individual_size: int,

max_iters = max_evals // population_size
for current_generation in range(max_iters):
if terminate_callback is not None and terminate_callback():
break
# 2.1 Mutation
# 2.1.1 Randomly choose which individuals do each mutation
choice = np.random.rand(population_size)
Expand Down
Loading