375 lines
12 KiB
Python
375 lines
12 KiB
Python
|
|
||
|
import numpy as np
|
||
|
from .decay import GeomDecay
|
||
|
|
||
|
|
||
|
def hill_climb(problem, max_iters=np.inf, restarts=0, init_state=None,
|
||
|
curve=False, random_state=None):
|
||
|
"""
|
||
|
best_state array containing state that optimizes the fitness function.
|
||
|
best_fitness Value of fitness function at best state.
|
||
|
fitness_curve array containing the fitness at every iteration.
|
||
|
"""
|
||
|
if (not isinstance(max_iters, int) and max_iters != np.inf
|
||
|
and not max_iters.is_integer()) or (max_iters < 0):
|
||
|
raise Exception("""max_iters must be a positive integer.""")
|
||
|
|
||
|
if (not isinstance(restarts, int) and not restarts.is_integer()) \
|
||
|
or (restarts < 0):
|
||
|
raise Exception("""restarts must be a positive integer.""")
|
||
|
|
||
|
if init_state is not None and len(init_state) != problem.get_length():
|
||
|
raise Exception("""init_state must have same length as problem.""")
|
||
|
|
||
|
# Set random seed
|
||
|
if isinstance(random_state, int) and random_state > 0:
|
||
|
np.random.seed(random_state)
|
||
|
|
||
|
best_fitness = -1*np.inf
|
||
|
best_state = None
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve = []
|
||
|
|
||
|
for _ in range(restarts + 1):
|
||
|
# Initialize optimization problem
|
||
|
if init_state is None:
|
||
|
problem.reset()
|
||
|
else:
|
||
|
problem.set_state(init_state)
|
||
|
|
||
|
iters = 0
|
||
|
|
||
|
while iters < max_iters:
|
||
|
iters += 1
|
||
|
|
||
|
# Find neighbors and determine best neighbor
|
||
|
problem.find_neighbors()
|
||
|
next_state = problem.best_neighbor()
|
||
|
next_fitness = problem.eval_fitness(next_state)
|
||
|
|
||
|
# If best neighbor is an improvement, move to that state
|
||
|
if next_fitness > problem.get_fitness():
|
||
|
problem.set_state(next_state)
|
||
|
|
||
|
else:
|
||
|
break
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve.append(problem.get_fitness())
|
||
|
|
||
|
# Update best state and best fitness
|
||
|
if problem.get_fitness() > best_fitness:
|
||
|
best_fitness = problem.get_fitness()
|
||
|
best_state = problem.get_state()
|
||
|
|
||
|
best_fitness = problem.get_maximize()*best_fitness
|
||
|
|
||
|
if curve:
|
||
|
return best_state, best_fitness, np.asarray(fitness_curve)
|
||
|
|
||
|
return best_state, best_fitness
|
||
|
|
||
|
|
||
|
def random_hill_climb(problem, max_attempts=10, max_iters=np.inf, restarts=0,
|
||
|
init_state=None, curve=False, random_state=None):
|
||
|
|
||
|
if (not isinstance(max_attempts, int) and not max_attempts.is_integer()) \
|
||
|
or (max_attempts < 0):
|
||
|
raise Exception("""max_attempts must be a positive integer.""")
|
||
|
|
||
|
if (not isinstance(max_iters, int) and max_iters != np.inf
|
||
|
and not max_iters.is_integer()) or (max_iters < 0):
|
||
|
raise Exception("""max_iters must be a positive integer.""")
|
||
|
|
||
|
if (not isinstance(restarts, int) and not restarts.is_integer()) \
|
||
|
or (restarts < 0):
|
||
|
raise Exception("""restarts must be a positive integer.""")
|
||
|
|
||
|
if init_state is not None and len(init_state) != problem.get_length():
|
||
|
raise Exception("""init_state must have same length as problem.""")
|
||
|
|
||
|
# Set random seed
|
||
|
if isinstance(random_state, int) and random_state > 0:
|
||
|
np.random.seed(random_state)
|
||
|
|
||
|
best_fitness = -1*np.inf
|
||
|
best_state = None
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve = []
|
||
|
|
||
|
for _ in range(restarts + 1):
|
||
|
# Initialize optimization problem and attempts counter
|
||
|
if init_state is None:
|
||
|
problem.reset()
|
||
|
else:
|
||
|
problem.set_state(init_state)
|
||
|
|
||
|
attempts = 0
|
||
|
iters = 0
|
||
|
|
||
|
while (attempts < max_attempts) and (iters < max_iters):
|
||
|
iters += 1
|
||
|
|
||
|
# Find random neighbor and evaluate fitness
|
||
|
next_state = problem.random_neighbor()
|
||
|
next_fitness = problem.eval_fitness(next_state)
|
||
|
|
||
|
# If best neighbor is an improvement,
|
||
|
# move to that state and reset attempts counter
|
||
|
if next_fitness > problem.get_fitness():
|
||
|
problem.set_state(next_state)
|
||
|
attempts = 0
|
||
|
|
||
|
else:
|
||
|
attempts += 1
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve.append(problem.get_fitness())
|
||
|
|
||
|
# Update best state and best fitness
|
||
|
if problem.get_fitness() > best_fitness:
|
||
|
best_fitness = problem.get_fitness()
|
||
|
best_state = problem.get_state()
|
||
|
|
||
|
best_fitness = problem.get_maximize()*best_fitness
|
||
|
|
||
|
if curve:
|
||
|
return best_state, best_fitness, np.asarray(fitness_curve)
|
||
|
|
||
|
return best_state, best_fitness
|
||
|
|
||
|
|
||
|
def simulated_annealing(problem, schedule=GeomDecay(), max_attempts=10,
|
||
|
max_iters=np.inf, init_state=None, curve=False,
|
||
|
random_state=None):
|
||
|
|
||
|
if (not isinstance(max_attempts, int) and not max_attempts.is_integer()) \
|
||
|
or (max_attempts < 0):
|
||
|
raise Exception("""max_attempts must be a positive integer.""")
|
||
|
|
||
|
if (not isinstance(max_iters, int) and max_iters != np.inf
|
||
|
and not max_iters.is_integer()) or (max_iters < 0):
|
||
|
raise Exception("""max_iters must be a positive integer.""")
|
||
|
|
||
|
if init_state is not None and len(init_state) != problem.get_length():
|
||
|
raise Exception("""init_state must have same length as problem.""")
|
||
|
|
||
|
# Set random seed
|
||
|
if isinstance(random_state, int) and random_state > 0:
|
||
|
np.random.seed(random_state)
|
||
|
|
||
|
# Initialize problem, time and attempts counter
|
||
|
if init_state is None:
|
||
|
problem.reset()
|
||
|
else:
|
||
|
problem.set_state(init_state)
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve = []
|
||
|
|
||
|
attempts = 0
|
||
|
iters = 0
|
||
|
|
||
|
while (attempts < max_attempts) and (iters < max_iters):
|
||
|
temp = schedule.evaluate(iters)
|
||
|
iters += 1
|
||
|
|
||
|
if temp == 0:
|
||
|
break
|
||
|
|
||
|
else:
|
||
|
# Find random neighbor and evaluate fitness
|
||
|
next_state = problem.random_neighbor()
|
||
|
next_fitness = problem.eval_fitness(next_state)
|
||
|
|
||
|
# Calculate delta E and change prob
|
||
|
delta_e = next_fitness - problem.get_fitness()
|
||
|
prob = np.exp(delta_e/temp)
|
||
|
|
||
|
# If best neighbor is an improvement or random value is less
|
||
|
# than prob, move to that state and reset attempts counter
|
||
|
if (delta_e > 0) or (np.random.uniform() < prob):
|
||
|
problem.set_state(next_state)
|
||
|
attempts = 0
|
||
|
|
||
|
else:
|
||
|
attempts += 1
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve.append(problem.get_fitness())
|
||
|
|
||
|
best_fitness = problem.get_maximize()*problem.get_fitness()
|
||
|
best_state = problem.get_state()
|
||
|
|
||
|
if curve:
|
||
|
return best_state, best_fitness, np.asarray(fitness_curve)
|
||
|
|
||
|
return best_state, best_fitness
|
||
|
|
||
|
|
||
|
def genetic_alg(problem, pop_size=200, mutation_prob=0.1, max_attempts=10,
|
||
|
max_iters=np.inf, curve=False, random_state=None):
|
||
|
|
||
|
if pop_size < 0:
|
||
|
raise Exception("""pop_size must be a positive integer.""")
|
||
|
elif not isinstance(pop_size, int):
|
||
|
if pop_size.is_integer():
|
||
|
pop_size = int(pop_size)
|
||
|
else:
|
||
|
raise Exception("""pop_size must be a positive integer.""")
|
||
|
|
||
|
if (mutation_prob < 0) or (mutation_prob > 1):
|
||
|
raise Exception("""mutation_prob must be between 0 and 1.""")
|
||
|
|
||
|
if (not isinstance(max_attempts, int) and not max_attempts.is_integer()) \
|
||
|
or (max_attempts < 0):
|
||
|
raise Exception("""max_attempts must be a positive integer.""")
|
||
|
|
||
|
if (not isinstance(max_iters, int) and max_iters != np.inf
|
||
|
and not max_iters.is_integer()) or (max_iters < 0):
|
||
|
raise Exception("""max_iters must be a positive integer.""")
|
||
|
|
||
|
# Set random seed
|
||
|
if isinstance(random_state, int) and random_state > 0:
|
||
|
np.random.seed(random_state)
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve = []
|
||
|
|
||
|
# Initialize problem, population and attempts counter
|
||
|
problem.reset()
|
||
|
problem.random_pop(pop_size)
|
||
|
attempts = 0
|
||
|
iters = 0
|
||
|
|
||
|
while (attempts < max_attempts) and (iters < max_iters):
|
||
|
iters += 1
|
||
|
|
||
|
# Calculate breeding probabilities
|
||
|
problem.eval_mate_probs()
|
||
|
|
||
|
# Create next generation of population
|
||
|
next_gen = []
|
||
|
|
||
|
for _ in range(pop_size):
|
||
|
# Select parents
|
||
|
selected = np.random.choice(pop_size, size=2,
|
||
|
p=problem.get_mate_probs())
|
||
|
parent_1 = problem.get_population()[selected[0]]
|
||
|
parent_2 = problem.get_population()[selected[1]]
|
||
|
|
||
|
# Create offspring
|
||
|
child = problem.reproduce(parent_1, parent_2, mutation_prob)
|
||
|
next_gen.append(child)
|
||
|
|
||
|
next_gen = np.array(next_gen)
|
||
|
problem.set_population(next_gen)
|
||
|
|
||
|
next_state = problem.best_child()
|
||
|
next_fitness = problem.eval_fitness(next_state)
|
||
|
|
||
|
# If best child is an improvement,
|
||
|
# move to that state and reset attempts counter
|
||
|
if next_fitness > problem.get_fitness():
|
||
|
problem.set_state(next_state)
|
||
|
attempts = 0
|
||
|
|
||
|
else:
|
||
|
attempts += 1
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve.append(problem.get_fitness())
|
||
|
|
||
|
best_fitness = problem.get_maximize()*problem.get_fitness()
|
||
|
best_state = problem.get_state()
|
||
|
|
||
|
if curve:
|
||
|
return best_state, best_fitness, np.asarray(fitness_curve)
|
||
|
|
||
|
return best_state, best_fitness
|
||
|
|
||
|
|
||
|
def mimic(problem, pop_size=200, keep_pct=0.2, max_attempts=10,
|
||
|
max_iters=np.inf, curve=False, random_state=None, fast_mimic=False):
|
||
|
"""Use MIMIC to find the optimum for a given optimization problem.
|
||
|
|
||
|
"""
|
||
|
if problem.get_prob_type() == 'continuous':
|
||
|
raise Exception("""problem type must be discrete or tsp.""")
|
||
|
|
||
|
if pop_size < 0:
|
||
|
raise Exception("""pop_size must be a positive integer.""")
|
||
|
elif not isinstance(pop_size, int):
|
||
|
if pop_size.is_integer():
|
||
|
pop_size = int(pop_size)
|
||
|
else:
|
||
|
raise Exception("""pop_size must be a positive integer.""")
|
||
|
|
||
|
if (keep_pct < 0) or (keep_pct > 1):
|
||
|
raise Exception("""keep_pct must be between 0 and 1.""")
|
||
|
|
||
|
if (not isinstance(max_attempts, int) and not max_attempts.is_integer()) \
|
||
|
or (max_attempts < 0):
|
||
|
raise Exception("""max_attempts must be a positive integer.""")
|
||
|
|
||
|
if (not isinstance(max_iters, int) and max_iters != np.inf
|
||
|
and not max_iters.is_integer()) or (max_iters < 0):
|
||
|
raise Exception("""max_iters must be a positive integer.""")
|
||
|
|
||
|
# Set random seed
|
||
|
if isinstance(random_state, int) and random_state > 0:
|
||
|
np.random.seed(random_state)
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve = []
|
||
|
|
||
|
if fast_mimic not in (True, False):
|
||
|
raise Exception("""fast_mimic mode must be a boolean.""")
|
||
|
else:
|
||
|
problem.mimic_speed = fast_mimic
|
||
|
|
||
|
# Initialize problem, population and attempts counter
|
||
|
problem.reset()
|
||
|
problem.random_pop(pop_size)
|
||
|
attempts = 0
|
||
|
iters = 0
|
||
|
|
||
|
while (attempts < max_attempts) and (iters < max_iters):
|
||
|
iters += 1
|
||
|
|
||
|
# Get top n percent of population
|
||
|
problem.find_top_pct(keep_pct)
|
||
|
|
||
|
# Update probability estimates
|
||
|
problem.eval_node_probs()
|
||
|
|
||
|
# Generate new sample
|
||
|
new_sample = problem.sample_pop(pop_size)
|
||
|
problem.set_population(new_sample)
|
||
|
|
||
|
next_state = problem.best_child()
|
||
|
|
||
|
next_fitness = problem.eval_fitness(next_state)
|
||
|
|
||
|
# If best child is an improvement,
|
||
|
# move to that state and reset attempts counter
|
||
|
if next_fitness > problem.get_fitness():
|
||
|
problem.set_state(next_state)
|
||
|
attempts = 0
|
||
|
|
||
|
else:
|
||
|
attempts += 1
|
||
|
|
||
|
if curve:
|
||
|
fitness_curve.append(problem.get_fitness())
|
||
|
|
||
|
best_fitness = problem.get_maximize()*problem.get_fitness()
|
||
|
best_state = problem.get_state().astype(int)
|
||
|
|
||
|
if curve:
|
||
|
return best_state, best_fitness, np.asarray(fitness_curve)
|
||
|
|
||
|
return best_state, best_fitness
|