Implementing the Random Search metaheuristic with MetaGen

Developing use cases in google colab:

In this example a simple RandomSearch algorithm has been developed using the metagen framework.

Initialization

The RandomSearch class is defined, and its constructor (__init__) is provided with the following parameters:

  • domain: Domain: The domain of possible solutions.

  • fitness: Callable[[Solution], float]: A function that calculates the fitness of a solution.

  • search_space_size: int = 30: The number of potential solutions to generate.

  • iterations: int = 20: The number of search iterations to perform.

  • The constructor stores these parameters as instance variables.

Generating Potential Solutions

In the run method, an empty list called potential_solutions is initialized to store potential solution objects.

A loop is used to create self.search_space_size potential solutions. For each iteration of the loop, a Solution object is created, passing in the domain and a connector obtained from the domain. These potential solutions are appended to the potential_solutions list.

Best Solution search

The initial best solution is determined by finding the solution with the minimum fitness value among the potential solutions. The deepcopy function is used to create a deep copy of this solution and assign it to the variable solution.

Another loop is used to perform the search for self.iterations iterations.

Inside this loop, each potential solution in the potential_solutions list is processed. For each potential solution (ps), the following steps are performed:

  • ps.mutate(): The mutate method is called on the potential solution, which modifies it to explore new possibilities within the solution space by employing the mutate function in Solution.

  • ps.evaluate(self.fitness): The fitness of the potential solution is evaluated using the provided fitness function self.fitness by employing the evaluate function in Solution.

  • If the fitness of the potential solution (ps) is better (i.e., lower fitness value) than the fitness of the current best solution (solution), the solution is updated with a deep copy of the potential solution. This is done to keep track of the best solution found so far.

  • After completing the search loop, the best solution found during the search is returned as the result of the run method.

class RandomSearch:

    def __init__(self, domain: Domain, fitness: Callable[[Solution], float], search_space_size: int = 30,
                iterations: int = 20) -> None:

        self.domain = domain
        self.fitness = fitness
        self.search_space_size = search_space_size
        self.iterations = iterations

    def run(self) -> Solution:

        potential_solutions: List[Solution] = list()

        for _ in range(0, self.search_space_size):
            potential_solutions.append(Solution(self.domain, connector=self.domain.get_connector()))

        solution: Solution = deepcopy(min(potential_solutions))

        for _ in range(0, self.iterations):
            for ps in potential_solutions:
                ps.mutate()

                ps.evaluate(self.fitness)
                if ps < solution:
                    solution = deepcopy(ps)

        return solution