def _best_beta(self, agent: Agent, worst: Agent, best: Agent) -> np.ndarray: """Calculates the best attraction (eq. 15). Args: agent: Selected agent. worst: Worst agent. best: Best agent. Returns: (np.ndarray): The best attraction. """ # Calculates the fitness fitness = (agent.fit - best.fit) / (worst.fit - best.fit + c.EPSILON) # Calculates the positioning position = (best.position - agent.position) / ( g.euclidean_distance(best.position, agent.position) + c.EPSILON) # Calculates the food attraction beta = fitness * position return beta
def _food_beta(self, agent, worst, best, food, C_food): """Calculates the food attraction (eq. 13). Args: agent (Agent): Selected agent. worst (Agent): Worst agent. best (Agent): Best agent. food (Agent): Food location. C_food (float): Food coefficient. Returns: The food attraction. """ # Calculates the fitness fitness = (agent.fit - food.fit) / (worst.fit - best.fit + c.EPSILON) # Calculates the positioning position = (food.position - agent.position) / ( g.euclidean_distance(food.position, agent.position) + c.EPSILON) # Calculates the food attraction beta = C_food * fitness * position return beta
def _target_alpha(self, agent, worst, best, C_best): """Calculates the target alpha (eq. 8). Args: agent (Agent): Selected agent. worst (Agent): Worst agent. best (Agent): Best agent. C_best (float): Effectiveness coefficient. Returns: The target alpha. """ # Calculates a list of neighbours' fitness fitness = (agent.fit - best.fit) / (worst.fit - best.fit + c.EPSILON) # Calculates a list of krills' position based on neighbours position = (best.position - agent.position) / ( g.euclidean_distance(best.position, agent.position) + c.EPSILON) # Calculates the target alpha alpha = C_best * fitness * position return alpha
def _rellocation(self, agent: Agent, best_agent: Agent, function: Function) -> None: """Performs the fox rellocation procedure. Args: agent: Current agent. best_agent: Best agent. function: A Function object that will be used as the objective function. """ # Creates a temporary agent temp = copy.deepcopy(agent) # Calculates the square root of euclidean distance between agent and best agent (eq. 1) distance = np.sqrt( g.euclidean_distance(temp.position, best_agent.position)) # Randomly selects the scaling hyperparameter alpha = r.generate_uniform_random_number(0, distance) # Calculates individual reallocation (eq. 2) temp.position += alpha * np.sign(best_agent.position - temp.position) # Checks agent's limits temp.clip_by_bound() # Calculates the fitness for the temporary position temp.fit = function(temp.position) # If new fitness is better than agent's fitness if temp.fit < agent.fit: # Copies its position and fitness to the agent agent.position = copy.deepcopy(temp.position) agent.fit = copy.deepcopy(temp.fit)
def _local_alpha(self, agent, worst, best, neighbours): """Calculates the local alpha (eq. 4). Args: agent (Agent): Selected agent. worst (Agent): Worst agent. best (Agent): Best agent. neighbours (list): List of neighbours. Returns: The local alpha. """ # Calculates a list of neighbours' fitness fitness = [ (agent.fit - neighbour.fit) / (worst.fit - best.fit + c.EPSILON) for neighbour in neighbours ] # Calculates a list of krills' position based on neighbours position = [(neighbour.position - agent.position) / (g.euclidean_distance(neighbour.position, agent.position) + c.EPSILON) for neighbour in neighbours] # Calculates the local alpha alpha = np.sum([fit * pos for (fit, pos) in zip(fitness, position)], axis=0) return alpha
def _update(self, agents, n_iterations): """Method that wraps Firefly Algorithm over all agents and variables (eq. 3-9). Args: agents (list): List of agents. n_iterations (int): Maximum number of iterations. """ # Calculating current iteration delta delta = 1 - ((10e-4) / 0.9) ** (1 / n_iterations) # Applying update to alpha parameter self.alpha *= (1 - delta) # We copy a temporary list for iterating purposes temp_agents = copy.deepcopy(agents) # Iterating through 'i' agents for agent in agents: # Iterating through 'j' agents for temp in temp_agents: # Distance is calculated by an euclidean distance between 'i' and 'j' (eq. 8) distance = g.euclidean_distance(agent.position, temp.position) # If 'i' fit is bigger than 'j' fit if agent.fit > temp.fit: # Recalculate the attractiveness (eq. 6) beta = self.beta * np.exp(-self.gamma * distance) # Generates a random uniform distribution r1 = r.generate_uniform_random_number() # Updates agent's position (eq. 9) agent.position = beta * (temp.position + agent.position) + self.alpha * (r1 - 0.5)
def _calculate_force(self, agents, mass, gravity): """Calculates agents' force (eq. 7-9). Args: agents (list): List of agents. mass (np.array): An array of agents' mass. gravity (float): Current gravity value. Returns: The attraction force between all agents. """ # Calculates the force force = [[ gravity * (mass[i] * mass[j]) / (g.euclidean_distance(agents[i].position, agents[j].position) + c.EPSILON) * (agents[j].position - agents[i].position) for j in range(len(agents)) ] for i in range(len(agents))] # Transforms the force into an array force = np.asarray(force) # Applying a stochastic trait to the force force = np.sum(r.generate_uniform_random_number() * force, axis=1) return force
def update(self, space: Space, iteration: int, n_iterations: int) -> None: """Wraps Owl Search Algorithm 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) # Gathers best and worst agents (eq. 5 and 6) best = copy.deepcopy(space.agents[0]) worst = copy.deepcopy(space.agents[-1]) # Linearly decreases the `beta` coefficient beta = self.beta - ((iteration + 1) / n_iterations) * self.beta # Iterates through all agents for agent in space.agents: # Calculates the normalized intensity (eq. 4) intensity = (agent.fit - best.fit) / (worst.fit - best.fit + c.EPSILON) # Calculates the distance between owl and prey (eq. 7) distance = g.euclidean_distance(agent.position, best.position) # Obtains the change in intensity (eq. 8) noise = r.generate_uniform_random_number() intensity_change = intensity / (distance**2 + c.EPSILON) + noise # print(agent.fit, worst.fit, best.fit, intensity, intensity_change) # Generates the probability of vole movement and random `alpha` p_vm = r.generate_uniform_random_number() alpha = r.generate_uniform_random_number(high=0.5) # If probability of vole movement is smaller than 0.5 if p_vm < 0.5: # Updates current's owl position (eq. 9 - top) agent.position += ( beta * intensity_change * np.fabs(alpha * best.position - agent.position) ) # If probability is bigger or equal to 0.5 else: # Updates current's owl position (eq. 9 - bottom) agent.position -= ( beta * intensity_change * np.fabs(alpha * best.position - agent.position) )
def update(self, space): """Wraps Magnetic Optimization Algorithm over all agents and variables. Args: space (Space): Space containing agents and update-related information. """ # Sorts agents space.agents.sort(key=lambda x: x.fit) # Gathers the best and worst agents and calculates a list of normalized fitness (eq. 2) best, worst = space.agents[0], space.agents[-1] fitness = [(agent.fit - best.fit) / (worst.fit - best.fit + c.EPSILON) for agent in space.agents] # Calculates the masses (eq. 3) mass = [self.alpha + self.rho * fit for fit in fitness] # Iterates through all agents for i, agent in enumerate(space.agents): # Gathers the agents neighbours (eq. 4) root = np.sqrt(space.n_agents) north = int((i - root) % space.n_agents) south = int((i + root) % space.n_agents) west = int((i - 1) + ((i + root - 1) % root) // (root - 1) * root) east = int((i + 1) - (i % root) // (root - 1) * root) neighbours = [north, south, west, east] # Initializes the force as a zero value force = 0 # Iterates through all neighbours for n in neighbours: # Calculates the distance between current agent and neighbour (eq. 7) distance = g.euclidean_distance(agent.position, space.agents[n].position) # Calculates the force between agents (eq. 5) force += (space.agents[n].position - agent.position) * fitness[n] / (distance + c.EPSILON) # Calculates the force's mean # This increases the performance of algorithm by eliminating addition biases force = np.mean(force) # Updates the agent's velocity(eq. 9) r1 = r.generate_uniform_random_number() velocity = force / mass[i] * r1 # Updates the agent's position (eq. 10) agent.position += velocity # Clips the agent's limits agent.clip_by_bound()
def update(self, space: Space, function: Function, iteration: int, n_iterations: int) -> None: """Wraps Grasshopper 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. iteration: Current iteration. n_iterations: Maximum number of iterations. """ # Calculates the comfort coefficient (eq. 2.8) comfort = self.c_max - iteration * ( (self.c_max - self.c_min) / n_iterations) # Copies a temporary list for iterating purposes temp_agents = copy.deepcopy(space.agents) # Iterates through 'i' agents for agent in space.agents: # Initializes the total comfort as zero total_comfort = np.zeros((agent.n_variables, agent.n_dimensions)) # Iterates through 'j' agents for temp in temp_agents: # Distance is calculated by an euclidean distance between 'i' and 'j' distance = g.euclidean_distance(agent.position, temp.position) # Calculates the unitary vector unit = (temp.position - agent.position) / (distance + c.EPSILON) # Calculates the social force between agents s = self._social_force(2 + np.fmod(distance, 2)) # Expands the upper and lower bounds ub = np.expand_dims(agent.ub, -1) lb = np.expand_dims(agent.lb, -1) # Sums the current comfort to the total one total_comfort += comfort * ((ub - lb) / 2) * s * unit # Updates the agent's position (eq. 2.7) agent.position = comfort * total_comfort + space.best_agent.position # Checks the agent's limits agent.clip_by_bound() # Evaluates the new agent's position agent.fit = function(agent.position)
def _update(self, agents, best_agent, function, iteration, n_iterations): """Method that wraps the Grasshopper Optimization Algorithm over all agents and variables. Args: agents (list): List of agents. best_agent (Agent): Global best agent. function (Function): A function object. iteration (int): Current iteration. n_iterations (int): Maximum number of iterations. """ # Calculates the comfort coefficient (Eq. 2.8) comfort = self.c_max - iteration * ( (self.c_max - self.c_min) / n_iterations) # We copy a temporary list for iterating purposes temp_agents = copy.deepcopy(agents) # Iterating through 'i' agents for agent in agents: # Initializes the total comfort as zero total_comfort = np.zeros((agent.n_variables, agent.n_dimensions)) # Iterating through 'j' agents for temp in temp_agents: # Distance is calculated by an euclidean distance between 'i' and 'j' distance = g.euclidean_distance(agent.position, temp.position) # Calculates the unitary vector unit = (temp.position - agent.position) / (distance + c.EPSILON) # Calculates the social force between agents s = self._social_force(2 + np.fmod(distance, 2)) # Expands the upper and lower bounds ub = np.expand_dims(agent.ub, -1) lb = np.expand_dims(agent.lb, -1) # Sums the current comfort to the total one total_comfort += comfort * ((ub - lb) / 2) * s * unit # Updates the agent's position (Eq. 2.7) agent.position = comfort * total_comfort + best_agent.position # Checks the agent's limits agent.clip_limits() # Evaluates the new agent's position agent.fit = function(agent.position)
def _roaming(self, prides: List[Lion], function: Function) -> None: """Performs the roaming procedure (s. 2.2.4). Args: prides: List of prides holding their corresponding lions. function: A Function object that will be used as the objective function. """ # Iterates through all prides for pride in prides: # Calculates the number of roaming lions n_roaming = int(len(pride) * self.P) # Selects `n_roaming` lions selected = r.generate_integer_random_number(high=len(pride), size=n_roaming) # Iterates through all agents in pride for agent in pride: # If agent is male if not agent.female: # Iterates through selected roaming lions for s in selected: # Calculates the direction angle theta = r.generate_uniform_random_number( -np.pi / 6, np.pi / 6) # Calculates the distance between selected lion and current one distance = g.euclidean_distance( pride[s].best_position, agent.position) # Generates the step (eq. 10) step = r.generate_uniform_random_number( 0, 2 * distance) # Updates the agent's position agent.position += step * np.tan(theta) # Clip the agent's limits agent.clip_by_bound() # Defines the previous fitness and calculates the newer one agent.p_fit = copy.deepcopy(agent.fit) agent.fit = function(agent.position) # If new fitness is better than old one if agent.fit < agent.p_fit: # Updates its best position agent.best_position = copy.deepcopy(agent.position)
def update(self, space: Space, function: Function) -> None: """Wraps Red Fox 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. """ # Defines the scaling parameter alpha = r.generate_uniform_random_number(0, 0.2) # Iterates through all agents for agent in space.agents: # Performs the fox rellocation procedure self._rellocation(agent, space.best_agent, function) # Performs the fox noticing procedure self._noticing(agent, function, alpha) # Sorts agents space.agents.sort(key=lambda x: x.fit) # Calculates the habitat's center and diameter (eq. 6 and 7) habitat_center = (space.agents[0].position + space.agents[1].position) / 2 habitat_diameter = np.sqrt( g.euclidean_distance(space.agents[0].position, space.agents[1].position)) # Samples a random number k = r.generate_uniform_random_number() # Iterates through all foxes that will be replaced for agent in space.agents[-self.n_replacement:]: # If sampled number is bigger than 0.45 (eq. 8 - top) if k >= 0.45: # Samples new position agent.fill_with_uniform() agent.position += habitat_center + habitat_diameter / 2 # If sampled number is smaller than 0.45 (eq. 8 - bottom) else: # Reproduces parents into a new position (eq. 9) agent.position = ( k * (space.agents[0].position + space.agents[1].position) / 2) # Checks agent's limits agent.clip_by_bound()
def _moving_safe_place(self, prides: List[Lion]) -> None: """Move prides to safe locations (s. 2.2.3). Args: prides: List of prides holding their corresponding lions. """ # Iterates through all prides for pride in prides: # Calculates the number of improved lions (eq. 7) n_improved = np.sum( [1 for agent in pride if agent.fit < agent.p_fit]) # Calculates the fitness of lions (eq. 8) fitnesses = [agent.fit for agent in pride] # Calculates the size of tournament (eq. 9) tournament_size = np.maximum(2, int(np.ceil(n_improved / 2))) # Iterates through all agents in pride for agent in pride: # If agent is female and belongs to group 0 if agent.group == 0 and agent.female: # Gathers the winning lion from tournament selection w = g.tournament_selection(fitnesses, 1, tournament_size)[0] # Calculates the distance between agent and winner distance = g.euclidean_distance(agent.position, pride[w].position) # Generates random numbers rand = r.generate_uniform_random_number() u = r.generate_uniform_random_number(-1, 1) theta = r.generate_uniform_random_number( -np.pi / 6, np.pi / 6) # Calculates both `R1` and `R2` vectors R1 = pride[w].position - agent.position R2 = np.random.randn(*R1.T.shape) R2 = R2.T - R2.dot(R1) * R1 / (np.linalg.norm(R1)**2 + c.EPSILON) # Updates agent's position (eq. 6) agent.position += (2 * distance * rand * R1 + u * np.tan(theta) * distance * R2)
def _sensing_distance(self, agents, idx): """Calculates the sensing distance for an individual krill (eq. 7). Args: agents (list): List of agents. idx (int): Selected agent. Returns: The sensing distance for an individual krill. """ # Calculates the euclidean distances between selected krill and other krills eucl_distance = [g.euclidean_distance(agents[idx].position, agent.position) for agent in agents] # Calculates the sensing distance distance = np.sum(eucl_distance) / (self.NN * len(agents)) return distance, eucl_distance
def update(self, space: Space, n_iterations: int) -> None: """Wraps Firefly Algorithm over all agents and variables (eq. 3-9). Args: space: Space containing agents and update-related information. n_iterations: Maximum number of iterations. """ # Calculates current iteration delta delta = 1 - ((10e-4) / 0.9)**(1 / n_iterations) # Applies update to alpha parameter self.alpha *= 1 - delta # We copy a temporary list for iterating purposes temp_agents = copy.deepcopy(space.agents) # Iterates through 'i' agents for agent in space.agents: # Iterates through 'j' agents for temp in temp_agents: # Distance is calculated by an euclidean distance between 'i' and 'j' (eq. 8) distance = g.euclidean_distance(agent.position, temp.position) # If 'i' fit is bigger than 'j' fit if agent.fit > temp.fit: # Recalculate the attractiveness (eq. 6) beta = self.beta * np.exp(-self.gamma * distance) # Generates a random uniform distribution r1 = r.generate_uniform_random_number() # Updates agent's position (eq. 9) agent.position = beta * (temp.position + agent.position ) + self.alpha * (r1 - 0.5)
def test_euclidean_distance(): x = np.array([1, 2, 3, 4]) y = np.array([1, 2, 3, 4]) assert general.euclidean_distance(x, y) == 0