Genetic Bee Colony
Genetic Bee Colony
import random
import time
class GeneticBeeColony:
"""
"""
def __init__(
self,
objective_function: Callable,
dimensions: int,
elite_count: int = 2,
):
"""
Args:
lower_bound: Lower bounds for each dimension (or a single value for
all dimensions)
upper_bound: Upper bounds for each dimension (or a single value for
all dimensions)
"""
# Set random seed if provided
np.random.seed(seed)
random.seed(seed)
# Store parameters
self.objective_function = objective_function
self.dimensions = dimensions
self.minimize = minimize
self.max_iterations = max_iterations
self.mutation_rate = mutation_rate
self.crossover_rate = crossover_rate
self.selection_pressure = selection_pressure
self.genetic_influence = genetic_influence
else:
self.lower_bound = np.array(lower_bound)
else:
self.upper_bound = np.array(upper_bound)
self.population = self._initialize_population()
self.fitness_values = self._calculate_fitness(self.obj_values)
self.trials = np.zeros(self.population_size)
self.best_index = np.argmax(self.fitness_values)
self.best_solution = np.copy(self.population[self.best_index])
self.best_obj_value = self.obj_values[self.best_index]
# For statistics
self.convergence_curve = []
self.best_solutions_history = []
self.iteration_times = []
for i in range(self.population_size):
population[i] = self.lower_bound +
np.random.random(self.dimensions) * (self.upper_bound - self.lower_bound)
return population
def _evaluate(self, solution: np.ndarray) -> float:
return self.objective_function(solution)
"""
"""
fitness = np.zeros_like(obj_values)
if self.minimize:
for i in range(len(obj_values)):
if obj_values[i] >= 0:
fitness[i] = 1 / (1 + obj_values[i])
else:
fitness[i] = 1 + abs(obj_values[i])
else:
for i in range(len(obj_values)):
if obj_values[i] >= 0:
fitness[i] = obj_values[i]
else:
fitness[i] = 1 + abs(obj_values[i])
return fitness
candidates = random.sample(range(self.population_size),
tournament_size)
best_candidate = candidates[0]
best_candidate = candidate
return best_candidate
selection_probs /= np.sum(selection_probs)
"""
neighbor = np.copy(self.population[index])
phi = random.uniform(-1, 1)
return neighbor
def _mutate(self, solution: np.ndarray) -> np.ndarray:
mutated = np.copy(solution)
for i in range(self.dimensions):
return mutated
offspring1 = np.copy(parent1)
offspring2 = np.copy(parent2)
for i in range(self.dimensions):
if self.elite_count == 0:
elite_indices = np.argsort(self.fitness_values)[-self.elite_count:]
new_fitness = self._calculate_fitness(new_obj_values)
worst_indices = np.argsort(new_fitness)[:self.elite_count]
worst_idx = worst_indices[i]
new_population[worst_idx] = np.copy(self.population[elite_idx])
new_obj_values[worst_idx] = self.obj_values[elite_idx]
"""
"""
for i in range(self.population_size):
neighbor = self._create_neighbor(i)
neighbor = self._mutate(neighbor)
# Evaluate neighbor
neighbor_obj_value = self._evaluate(neighbor)
neighbor_fitness =
self._calculate_fitness(np.array([neighbor_obj_value]))[0]
self.population[i] = neighbor
self.obj_values[i] = neighbor_obj_value
self.fitness_values[i] = neighbor_fitness
self.trials[i] = 0
self.best_solution = np.copy(neighbor)
self.best_obj_value = neighbor_obj_value
self.best_index = i
else:
self.trials[i] += 1
"""
"""
temp_population = np.copy(self.population)
temp_obj_values = np.copy(self.obj_values)
parent1_idx = self._tournament_selection()
parent2_idx = self._tournament_selection()
parent2_idx = self._tournament_selection()
# Apply crossover
# Apply mutation
offspring1 = self._mutate(offspring1)
# Evaluate offspring
offspring1_obj_value = self._evaluate(offspring1)
offspring2_obj_value = self._evaluate(offspring2)
fitness_ranks = np.argsort(self.fitness_values)
if self._calculate_fitness(np.array([offspring1_obj_value]))[0] >
self.fitness_values[worst1_idx]:
temp_population[worst1_idx] = offspring1
temp_obj_values[worst1_idx] = offspring1_obj_value
if self._calculate_fitness(np.array([offspring2_obj_value]))[0] >
self.fitness_values[worst2_idx]:
temp_population[worst2_idx] = offspring2
temp_obj_values[worst2_idx] = offspring2_obj_value
self.population = temp_population
self.obj_values = temp_obj_values
self.fitness_values = self._calculate_fitness(self.obj_values)
self.best_index = best_idx
self.best_solution = np.copy(self.population[best_idx])
self.best_obj_value = self.obj_values[best_idx]
"""
"""
# Onlooker bees
for _ in range(self.population_size):
selected_idx = np.random.choice(self.population_size,
p=selection_probs)
neighbor = self._create_neighbor(selected_idx)
# Evaluate neighbor
neighbor_obj_value = self._evaluate(neighbor)
neighbor_fitness =
self._calculate_fitness(np.array([neighbor_obj_value]))[0]
self.population[selected_idx] = neighbor
self.obj_values[selected_idx] = neighbor_obj_value
self.fitness_values[selected_idx] = neighbor_fitness
self.trials[selected_idx] = 0
self.best_solution = np.copy(neighbor)
self.best_obj_value = neighbor_obj_value
self.best_index = selected_idx
else:
self.trials[selected_idx] += 1
"""
Scout bee phase: Abandon solutions that have not been improved
for a certain number of trials and replace them with new random
solutions.
"""
for i in range(self.population_size):
new_solution = np.copy(self.population[elite_idx])
for j in range(self.dimensions):
new_solution[j] += perturbation
new_solution[j] = np.clip(new_solution[j],
self.lower_bound[j], self.upper_bound[j])
else:
new_solution = self.lower_bound +
np.random.random(self.dimensions) * (self.upper_bound - self.lower_bound)
new_obj_value = self._evaluate(new_solution)
# Update the population
self.population[i] = new_solution
self.obj_values[i] = new_obj_value
self.fitness_values[i] =
self._calculate_fitness(np.array([new_obj_value]))[0]
self.trials[i] = 0
self.best_solution = np.copy(new_solution)
self.best_obj_value = new_obj_value
self.best_index = i
def optimize(self, verbose: bool = True, log_interval: int = 100) -> Dict:
"""
Args:
Returns:
"""
start_time = time.time()
for iteration in range(self.max_iterations):
iter_start = time.time()
self._employed_bee_phase()
self._crossover_phase()
self._onlooker_bee_phase()
self._scout_bee_phase()
# Record statistics
self.convergence_curve.append(self.best_obj_value)
self.best_solutions_history.append(np.copy(self.best_solution))
self.iteration_times.append(time.time() - iter_start)
# Print progress
f"Time: {self.iteration_times[-1]:.4f}s")
total_time = time.time() - start_time
if verbose:
# Prepare results
results = {
"best_solution": self.best_solution,
"best_obj_value": self.best_obj_value,
"convergence_curve": self.convergence_curve,
"best_solutions_history": self.best_solutions_history,
"total_time": total_time,
"iteration_times": self.iteration_times,
"iterations": self.max_iterations
return results
def sphere(x):
return np.sum(x**2)
def rosenbrock(x):
def rastrigin(x):
def schwefel(x):
"""Schwefel function."""
if __name__ == "__main__":
np.random.seed(42)
random.seed(42)
gbc_sphere = GeneticBeeColony(
objective_function=sphere,
dimensions=30,
lower_bound=-100,
upper_bound=100,
minimize=True,
population_size=50,
max_iterations=1000,
mutation_rate=0.05,
crossover_rate=0.8,
selection_pressure=2.0,
genetic_influence=0.3,
elite_count=2,
seed=42
results_sphere = gbc_sphere.optimize(log_interval=200)
gbc_rosenbrock = GeneticBeeColony(
objective_function=rosenbrock,
dimensions=30,
lower_bound=-30,
upper_bound=30,
minimize=True,
population_size=50,
max_iterations=1000,
mutation_rate=0.05,
crossover_rate=0.9,
selection_pressure=2.5,
genetic_influence=0.4,
elite_count=3,
seed=42
results_rosenbrock = gbc_rosenbrock.optimize(log_interval=200)
gbc_rastrigin = GeneticBeeColony(
objective_function=rastrigin,
dimensions=30,
lower_bound=-5.12,
upper_bound=5.12,
minimize=True,
population_size=100,
max_iterations=1000,
mutation_rate=0.1,
crossover_rate=0.8,
selection_pressure=3.0,
genetic_influence=0.5,
elite_count=5,
seed=42
results_rastrigin = gbc_rastrigin.optimize(log_interval=200)
# Summary
print(f"Rosenbrock: {results_rosenbrock['best_obj_value']:.6e}")
print(f"Rastrigin: {results_rastrigin['best_obj_value']:.6e}")