def update(self, space: Space, function: Function, iteration: int, n_iterations: int) -> None: """Wraps motion and genetic updates over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. iteration: Current iteration. n_iterations: Maximum number of iterations. """ # Sorts agents space.agents.sort(key=lambda x: x.fit) # Calculates the food location (eq. 12) food = self._food_location(space.agents, function) # Iterates through all agents for i, _ in enumerate(space.agents): # Updates current agent's position space.agents[i].position = self._update_position( space.agents, i, iteration, n_iterations, food, self.motion[i], self.foraging[i], ) # Performs the crossover and mutation space.agents[i] = self._crossover(space.agents, i) space.agents[i] = self._mutation(space.agents, i)
def update(self, space: Space, function: Function) -> None: """Wraps Evolutionary Programming over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. """ # Calculates the number of agents n_agents = len(space.agents) # Creates a list for the produced children children = [] # Iterates through all agents for i, agent in enumerate(space.agents): # Mutates a parent and generates a new child a = self._mutate_parent(agent, i, function) # Updates the strategy self._update_strategy(i, agent.lb, agent.ub) # Appends the mutated agent to the children children.append(a) # Joins both populations space.agents += children # Number of individuals to be selected n_individuals = int(n_agents * self.bout_size) # Creates an empty array of wins wins = np.zeros(len(space.agents)) # Iterates through all agents in the new population for i, agent in enumerate(space.agents): # Iterate through all tournament individuals for _ in range(n_individuals): # Gathers a random index index = r.generate_integer_random_number(0, len(space.agents)) # If current agent's fitness is smaller than selected one if agent.fit < space.agents[index].fit: # Increases its winning by one wins[i] += 1 # Sorts agents list based on its winnings space.agents = [ agents for _, agents in sorted( zip(wins, space.agents), key=lambda pair: pair[0], reverse=True ) ] # Gathers the best `n_agents` space.agents = space.agents[:n_agents]
def update(self, space: Space, function: Function) -> None: """Wraps Evolution Strategies over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. """ # Calculates the number of agents n_agents = len(space.agents) # Creates a list for the produced children children = [] # Iterate through all children for i in range(self.n_children): # Mutates a parent and generates a new child a = self._mutate_parent(space.agents[i], i, function) # Updates the strategy self._update_strategy(i) # Appends the mutated agent to the children children.append(a) # Joins both populations, sorts agents and gathers best `n_agents` space.agents += children space.agents.sort(key=lambda x: x.fit) space.agents = space.agents[:n_agents]
def update(self, space: Space, function: Function) -> None: """Wraps Artificial Flora over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. """ # Sorts the agents space.agents.sort(key=lambda x: x.fit) # Creates a list of new agents new_agents = [] # Iterates thorugh all agents for i, agent in enumerate(space.agents): # Iterates through amount of branches for _ in range(self.m): # Makes a copy of current agent a = copy.deepcopy(agent) # Generates random numbers r1 = r.generate_uniform_random_number() r2 = r.generate_uniform_random_number() r3 = r.generate_uniform_random_number() # Calculates the new distance (eq. 1) distance = (self.g_distance[i] * r1 * self.c1 + self.p_distance[i] * r2 * self.c2) # Generates a random gaussian number D = r.generate_gaussian_random_number( 0, distance, (space.n_variables, space.n_dimensions)) # Updates offspring's position (eq. 5) a.position += D # Clips its limits a.clip_by_bound() # Evaluates its fitness a.fit = function(a.position) # Calculates the probability of selection (eq. 6) p = np.fabs(np.sqrt(a.fit / space.agents[-1].fit)) * self.Q # If random number is smaller than probability of selection if r3 < p: # Appends the offsprings new_agents.append(a) # Updates both grandparent and parent distances (eq. 2 and 3) self.g_distance[i] = self.p_distance[i] self.p_distance[i] = np.std(agent.position - a.position) # Randomly selects the agents idx = d.generate_choice_distribution(len(new_agents), None, space.n_agents) space.agents = [new_agents[i] for i in idx]
def update(self, space: Space, function: Function) -> None: """Wraps Forest Optimization Algorithm over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. """ # Performs the local seeding self._local_seeding(space, function) # Limits the population candidate = self._population_limiting(space) # Performs the global seeding self._global_seeding(space, function, candidate) # Sorts agents and their corresponding ages space.agents, self.age = map( list, zip(*sorted(zip(space.agents, self.age), key=lambda x: x[0].fit))) # Sets the best tree's age to zero self.age[0] = 0
def evaluate(self, space: Space, function: Function) -> None: """Evaluates the search space according to the objective function. Args: space: A TreeSpace object. function: A Function object that will be used as the objective function. """ # Iterates through all (trees, agents) for (tree, agent) in zip(space.trees, space.agents): # Runs through the tree and returns a position array agent.position = copy.deepcopy(tree.position) # Checks the agent limits agent.clip_by_bound() # Calculates the fitness value of the agent agent.fit = function(agent.position) # If tree's fitness is better than global fitness if agent.fit < space.best_agent.fit: # Makes a deep copy of current tree, position and fitness space.best_tree = copy.deepcopy(tree) space.best_agent.position = copy.deepcopy(agent.position) space.best_agent.fit = copy.deepcopy(agent.fit) space.best_agent.ts = int(time.time())
def update(self, space: Space, function: Function) -> None: """Wraps Lion Optimization Algorithm over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. """ # Gets nomad and non-nomad (pride) lions nomads = self._get_nomad_lions(space.agents) prides = self._get_pride_lions(space.agents) # Performs the hunting procedure, moving, roaming, # mating and defending for pride lions (step 3) self._hunting(prides, function) self._moving_safe_place(prides) self._roaming(prides, function) pride_cubs = self._mating(prides, function) nomads, prides = self._defense(nomads, prides, pride_cubs) # Performs roaming, mating and attacking # for nomad lions (step 4) self._nomad_roaming(nomads, function) nomads = self._nomad_mating(nomads, function) nomads, prides = self._nomad_attack(nomads, prides) # Migrates females lions from prides (step 5) nomads, prides = self._migrating(nomads, prides) # Equilibrates the nomads and prides population (step 6) nomads, prides = self._equilibrium(nomads, prides, space.n_agents) # Checks if there is at least one male per pride self._check_prides_for_males(prides) # Defines the correct splitting point, so # the agents in space can be correctly updated correct_nomad_size = int(self.N * space.n_agents) # Updates the nomads space.agents[:correct_nomad_size] = copy.deepcopy( nomads[:correct_nomad_size]) # Updates the prides space.agents[correct_nomad_size:] = copy.deepcopy( list(itertools.chain.from_iterable(prides)))
def update(self, space: Space, function: Function, iteration: int) -> None: """Wraps Bat Algorithm over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. iteration: Current iteration. """ # Declares alpha constant alpha = 0.9 # Iterates through all agents for i, agent in enumerate(space.agents): # Updates frequency (eq. 2) # Note that we have to apply (min - max) instead of (max - min) or it will not converge beta = rnd.generate_uniform_random_number() self.frequency[i] = self.f_min + (self.f_min - self.f_max) * beta # Updates velocity (eq. 3) self.velocity[i] += (agent.position - space.best_agent.position) * self.frequency[i] # Updates agent's position (eq. 4) agent.position += self.velocity[i] # Generates random uniform and gaussian numbers p = rnd.generate_uniform_random_number() e = rnd.generate_gaussian_random_number() # Checks if probability is bigger than current pulse rate if p > self.pulse_rate[i]: # Performs a local random walk (eq. 5) # We apply 0.001 to limit the step size agent.position = space.best_agent.position + 0.001 * e * np.mean( self.loudness) # Checks agent limits agent.clip_by_bound() # Evaluates agent agent.fit = function(agent.position) # Checks if probability is smaller than loudness and if fit is better if p < self.loudness[i] and agent.fit < space.best_agent.fit: # Copies the new solution to space's best agent space.best_agent = copy.deepcopy(agent) # Increasing pulse rate (eq. 6 - left) self.pulse_rate[i] = self.r * (1 - np.exp(-alpha * iteration)) # Decreasing loudness (eq. 6 - right) self.loudness[i] = self.A * alpha
def update(self, space: Space, iteration: int, n_iterations: int) -> None: """Wraps Thermal Exchange Optimization over all agents and variables. Args: space: Space containing agents and update-related information. iteration: Current iteration. n_iterations: Maximum number of iterations. """ # Sorts agents space.agents.sort(key=lambda x: x.fit) # Updates the thermal memory and cuts it to maximum allowed size self.TM.append(copy.deepcopy(space.agents[0])) self.TM = self.TM[-self.n_TM :] # Replaces the worst agents with the thermal memory and re-sorts the agents space.agents = space.agents[: -len(self.TM)] + self.TM space.agents.sort(key=lambda x: x.fit) # Calculates the time (eq. 9) time = iteration / n_iterations # Iterates through all environmental-based agents for env in self.environment: # Updates the environment's position (eq. 10) r1 = r.generate_uniform_random_number() env.position = 1 - (self.c1 + self.c2 * (1 - time)) * r1 * env.position # Iterates through both populations' agents for agent, env in zip(space.agents, self.environment): # Calculates the agent's beta value (eq. 8) beta = agent.fit / space.agents[-1].fit # Updates the agent's position (eq. 11) agent.position = env.position + (agent.position - env.position) * np.exp( -beta * time ) # Generates a random number r1 = r.generate_uniform_random_number() # If random number is smaller than `pro` if r1 < self.pro: # Selects a random dimension idx = r.generate_integer_random_number(high=agent.n_variables) # Resets its position (eq. 12) r2 = r.generate_uniform_random_number() agent.position[idx] = agent.lb[idx] + r2 * ( agent.ub[idx] - agent.lb[idx] )
def update(self, space: Space, function: Function, n_iterations: int) -> None: """Wraps Water Cycle Algorithm over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. n_iterations: Maximum number of iterations. """ # Calculates the flow intensity self._flow_intensity(space.agents) # Updates every stream position self._update_stream(space.agents, function) # Updates every river position self._update_river(space.agents, space.best_agent, function) # Iterates through all rivers for i in range(1, self.nsr): # Iterates through all raindrops for j in range(self.nsr, len(space.agents)): # If raindrop position is better than river's if space.agents[j].fit < space.agents[i].fit: # Exchanges their positions space.agents[i], space.agents[j] = space.agents[j], space.agents[i] # Iterates through all rivers: for i in range(1, self.nsr): # If river position is better than seá's if space.agents[i].fit < space.agents[0].fit: # Exchanges their positions space.agents[i], space.agents[0] = space.agents[0], space.agents[i] # Performs the raining process (eq. 12) self._raining_process(space.agents, space.best_agent) # Updates the evaporation condition self.d_max -= self.d_max / n_iterations
def _population_limiting(self, space: Space) -> List[Agent]: """Limits the population by removing old trees. Args: space: A Space object containing meta-information. Returns: (List[Agent]): A list of candidate trees that were removed from the forest. """ # List of candidate trees candidate = [] # Iterates through all agents for i, _ in enumerate(space.agents): # Checks whether current agent has exceed its life time if self.age[i] > self.life_time: # Removes the tree and its corresponding age from forest agent = space.agents.pop(i) self.age.pop(i) # Adds the removed agent to the candidate list candidate.append(agent) # Sorts agents and their corresponding ages space.agents, self.age = map( list, zip(*sorted(zip(space.agents, self.age), key=lambda x: x[0].fit))) # If the population exceeds the forest limits if len(space.agents) > self.area_limit: # Adds extra trees to the candidate list candidate += space.agents[self.area_limit:] # Removes the extra trees and their corresponding ages from forest space.agents = space.agents[:self.area_limit] self.age = self.age[:self.area_limit] return candidate
def update(self, space: Space, function: Function, iteration: int, n_iterations: int) -> None: """Wraps Invasive Weed Optimization over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. iteration: Current iteration. n_iterations: Maximum number of iterations. """ # Calculates the current Spatial Dispersal self._spatial_dispersal(iteration, n_iterations) # Calculates the number of agents and creates list of offsprings n_agents = len(space.agents) offsprings = [] # Sorts agents space.agents.sort(key=lambda x: x.fit) # Iterates through all agents for agent in space.agents: # Calculates the seeding ratio based on its fitness ratio = (agent.fit - space.agents[-1].fit) / ( space.agents[0].fit - space.agents[-1].fit + c.EPSILON) # Calculates the number of produced seeds n_seeds = int(self.min_seeds + (self.max_seeds - self.min_seeds) * ratio) # For every seed for _ in range(n_seeds): # Reproduces and flowers the seed into a new agent a = self._produce_offspring(agent, function) # Appends the agent to the offsprings offsprings.append(a) # Joins both populations, sorts and gathers best `n_agents` space.agents += offsprings space.agents.sort(key=lambda x: x.fit) space.agents = space.agents[:n_agents]
def compile(self, space: Space) -> None: """Compiles additional information that is used by this optimizer. Args: space: A Space object containing meta-information. """ # Replaces the current agents with a derived Lion structure space.agents = [ Lion( agent.n_variables, agent.n_dimensions, agent.lb, agent.ub, agent.position, agent.fit, ) for agent in space.agents ] # Calculates the number of nomad lions and their genders n_nomad = int(self.N * space.n_agents) nomad_gender = d.generate_bernoulli_distribution(1 - self.S, n_nomad) # Iterates through all possible nomads for i, agent in enumerate(space.agents[:n_nomad]): # Toggles to `True` the nomad property agent.nomad = True # Defines the gender according to Bernoulli distribution agent.female = bool(nomad_gender[i]) # Calculates the gender of pride lions pride_gender = d.generate_bernoulli_distribution( self.S, space.n_agents - n_nomad) # Iterates through all possible prides for i, agent in enumerate(space.agents[n_nomad:]): # Defines the gender according to Bernoulli distribution agent.female = bool(pride_gender[i]) # Allocates to the corresponding pride agent.pride = i % self.P
def update(self, space: Space, function: Function) -> None: """Wraps Genetic Algorithm over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. """ # Creates a list to hold the new population new_agents = [] # Retrieves the number of agents n_agents = len(space.agents) # Calculates a list of fitness from every agent fitness = [agent.fit + c.EPSILON for agent in space.agents] # Selects the parents selected = self._roulette_selection(n_agents, fitness) # For every pair of selected parents for s in g.n_wise(selected): # Performs the crossover and mutation alpha, beta = self._crossover(space.agents[s[0]], space.agents[s[1]]) alpha, beta = self._mutation(alpha, beta) # Checking `alpha` and `beta` limits alpha.clip_by_bound() beta.clip_by_bound() # Calculates new fitness for `alpha` and `beta` alpha.fit = function(alpha.position) beta.fit = function(beta.position) # Appends the mutated agents to the children new_agents.extend([alpha, beta]) # Joins both populations, sort agents and gathers best `n_agents` space.agents += new_agents space.agents.sort(key=lambda x: x.fit) space.agents = space.agents[:n_agents]
def update( self, space: Space, function: Function, iteration: int, n_iterations: int ) -> None: """Wraps Lightning Search Algorithm over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. iteration: Current iteration. n_iterations: Maximum number of iterations. """ # Increases the channel's time self.time += 1 # If channel has reached maximum allowed time if self.time >= self.max_time: # Sorts agents space.agents.sort(key=lambda x: x.fit) # Replaces the worst channel with the best one space.agents[-1] = copy.deepcopy(space.agents[0]) # Resets the channel's time self.time = 0 # Re-sorts the agents space.agents.sort(key=lambda x: x.fit) # Updates the direction self._update_direction(space.agents[0], function) # Calculates the current energy energy = self.E - 2 * np.exp(-5 * (n_iterations - iteration) / n_iterations) # Iterates through all agents for agent in space.agents: # Updates agent's position self._update_position(agent, space.agents[0], function, energy)
def update(self, space: Space, function: Function) -> None: """Wraps Electromagnetic Field Optimization over all agents and variables (eq. 1-4). Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. """ # Sorts agents according to their fitness space.agents.sort(key=lambda x: x.fit) # Gathers the number of total agents n_agents = len(space.agents) # Making a deepcopy of current's best agent agent = copy.deepcopy(space.agents[0]) # Generates a uniform random number for the force force = r.generate_uniform_random_number() # For every decision variable for j in range(agent.n_variables): # Calculates the index of positive, negative and neutral particles pos, neg, neu = self._calculate_indexes(n_agents) # Generates a uniform random number r1 = r.generate_uniform_random_number() # If random number is smaller than the probability of selecting eletromagnets if r1 < self.ps_ratio: # Applies agent's position as positive particle's position agent.position[j] = space.agents[pos].position[j] # If random number is bigger else: # Calculates the new agent's position agent.position[j] = (space.agents[neg].position[j] + self.phi * force * (space.agents[pos].position[j] - space.agents[neu].position[j]) - force * (space.agents[neg].position[j] - space.agents[neu].position[j])) # Clips the agent's position to its limits agent.clip_by_bound() # Generates a third uniform random number r2 = r.generate_uniform_random_number() # If random number is smaller than probability of changing a random eletromagnet if r2 < self.r_ratio: # Update agent's position based on RI agent.position[self.RI] = r.generate_uniform_random_number( agent.lb[self.RI], agent.ub[self.RI]) # Increases RI by one self.RI += 1 # If RI exceeds the number of variables if self.RI >= agent.n_variables: # Resets it to one self.RI = 1 # Calculates the agent's fitness agent.fit = function(agent.position) # If newly generated agent fitness is better than worst fitness if agent.fit < space.agents[-1].fit: # Updates the corresponding agent's object space.agents[-1] = copy.deepcopy(agent)
def update(self, space: Space, function: Function) -> None: """Wraps Black Widow Optimization over all agents and variables. Args: space: Space containing agents and update-related information. function: A Function object that will be used as the objective function. """ # Retrieves the number of agents n_agents = len(space.agents) n_variables = space.n_variables # Calculates the number agents that reproduces, are cannibals and mutates n_reproduct = int(n_agents * self.pp) n_cannibals = int(n_agents * self.cr) n_mutate = int(n_agents * self.pm) # Sorts agents space.agents.sort(key=lambda x: x.fit) # Selecting the best solutions and saving in auxiliary population agents1 = copy.deepcopy(space.agents[:n_reproduct]) # Creates an empty auxiliary population agents2 = [] # For every possible reproducting agent for _ in range(0, n_reproduct): # Sampling random uniform integers as indexes idx = r.generate_uniform_random_number(0, n_agents, size=2) # Making a deepcopy of father and mother father, mother = copy.deepcopy(space.agents[int( idx[0])]), copy.deepcopy(space.agents[int(idx[1])]) # Creates an empty list of auxiliary agents new_agents = [] # For every possible pair of variables for _ in range(0, int(n_variables / 2)): # Procreates parents into two new offsprings y1, y2 = self._procreating(father, mother) # Checks `y1` and `y2` limits y1.clip_by_bound() y2.clip_by_bound() # Calculates new fitness for `y1` and `y2` y1.fit = function(y1.position) y2.fit = function(y2.position) # Appends the mother and mutated agents to the new population new_agents.extend([mother, y1, y2]) # Sorts new population new_agents.sort(key=lambda x: x.fit) # Extending auxiliary population with the number of cannibals (s. 3.3) agents2.extend(new_agents[:n_cannibals]) # For every possible mutating agent for _ in range(0, n_mutate): # Sampling a random integer as index idx = int(r.generate_uniform_random_number(0, n_reproduct)) # Performs the mutation alpha = self._mutation(agents1[idx]) # Checks `alpha` limits alpha.clip_by_bound() # Calculates new fitness for `alpha` alpha.fit = function(alpha.position) # Appends the mutated agent to the auxiliary population agents2.extend([alpha]) # Joins both populations, sorts them and retrieves `n_agents` space.agents += agents2 space.agents.sort(key=lambda x: x.fit) space.agents = space.agents[:n_agents]