# Use Functions, Methods, and Classes to Build Fitness and Callbacks In PyGAD 2.19.0, it is possible to pass user-defined functions or methods to the following parameters: 1. `fitness_func` 2. `on_start` 3. `on_fitness` 4. `on_parents` 5. `on_crossover` 6. `on_mutation` 7. `on_generation` 8. `on_stop` You can also pass a class instance for any of these parameters. The same 3 options (function, method, or class) work for the operator parameters `crossover_type`, `mutation_type`, and `parent_selection_type`. See the [User-Defined Crossover, Mutation, and Parent Selection Operators](https://pygad.readthedocs.io/en/latest/user_defined_operators.html#user-defined-crossover-mutation-and-parent-selection-operators) section for more about the operators. This section gives 3 examples of how to build these handlers using: 1. Functions. 2. Methods. 3. Classes. ## Assign Functions This is a dummy example where the fitness function returns a random value. Note that the instance of the `pygad.GA` class is passed as the last parameter of all functions. ```python import pygad import numpy def fitness_func(ga_instance, solution, solution_idx): return numpy.random.rand() def on_start(ga_instance): print("on_start") def on_fitness(ga_instance, last_gen_fitness): print("on_fitness") def on_parents(ga_instance, last_gen_parents): print("on_parents") def on_crossover(ga_instance, last_gen_offspring): print("on_crossover") def on_mutation(ga_instance, last_gen_offspring): print("on_mutation") def on_generation(ga_instance): print("on_generation\n") def on_stop(ga_instance, last_gen_fitness): print("on_stop") ga_instance = pygad.GA(num_generations=5, num_parents_mating=4, sol_per_pop=10, num_genes=2, on_start=on_start, on_fitness=on_fitness, on_parents=on_parents, on_crossover=on_crossover, on_mutation=on_mutation, on_generation=on_generation, on_stop=on_stop, fitness_func=fitness_func) ga_instance.run() ``` ## Assign Methods The next example has all the methods defined inside the class `Test`. All of the methods accept an additional parameter representing the method's object of the class `Test`. All methods accept `self` as the first parameter and the instance of the `pygad.GA` class as the last parameter. ```python import pygad import numpy class Test: def fitness_func(self, ga_instance, solution, solution_idx): return numpy.random.rand() def on_start(self, ga_instance): print("on_start") def on_fitness(self, ga_instance, last_gen_fitness): print("on_fitness") def on_parents(self, ga_instance, last_gen_parents): print("on_parents") def on_crossover(self, ga_instance, last_gen_offspring): print("on_crossover") def on_mutation(self, ga_instance, last_gen_offspring): print("on_mutation") def on_generation(self, ga_instance): print("on_generation\n") def on_stop(self, ga_instance, last_gen_fitness): print("on_stop") ga_instance = pygad.GA(num_generations=5, num_parents_mating=4, sol_per_pop=10, num_genes=2, on_start=Test().on_start, on_fitness=Test().on_fitness, on_parents=Test().on_parents, on_crossover=Test().on_crossover, on_mutation=Test().on_mutation, on_generation=Test().on_generation, on_stop=Test().on_stop, fitness_func=Test().fitness_func) ga_instance.run() ``` ## Assign a Class Besides functions and methods, you can pass an instance of a class. The class must implement the `__call__()` method, which makes its instances callable like a function. PyGAD calls the instance the same way it calls a function. The `__call__()` method must accept the same parameters as the matching function. The `self` parameter does not count. For example, the `__call__()` method of a fitness class accepts `self` plus the same 3 parameters as a fitness function: the instance of the `pygad.GA` class, a solution, and its index. A class is useful when the handler needs to keep state across generations. Because the same instance is reused for every call, any data you store in its attributes (for example, in the `__init__()` method) stays available across all the generations. The next example builds the fitness function, the crossover and mutation operators, and all the callbacks as classes. An instance of each class is passed to the matching parameter. ```python import pygad import numpy class Fitness: def __call__(self, ga_instance, solution, solution_idx): fitness = numpy.sum(solution) return fitness class Crossover: def __call__(self, parents, offspring_size, ga_instance): return numpy.random.rand(offspring_size[0], offspring_size[1]) class Mutation: def __call__(self, offspring, ga_instance): return offspring class OnStart: def __call__(self, ga_instance): print("on_start") class OnFitness: def __call__(self, ga_instance, fitness): print("on_fitness") class OnParents: def __call__(self, ga_instance, parents): print("on_parents") class OnCrossover: def __call__(self, ga_instance, offspring): print("on_crossover") class OnMutation: def __call__(self, ga_instance, offspring): print("on_mutation") class OnGeneration: def __call__(self, ga_instance): print("on_generation") class OnStop: def __call__(self, ga_instance, fitness): print("on_stop") ga_instance = pygad.GA(num_generations=10, num_parents_mating=5, sol_per_pop=10, num_genes=5, fitness_func=Fitness(), crossover_type=Crossover(), mutation_type=Mutation(), on_start=OnStart(), on_fitness=OnFitness(), on_parents=OnParents(), on_crossover=OnCrossover(), on_mutation=OnMutation(), on_generation=OnGeneration(), on_stop=OnStop(), suppress_warnings=True) ga_instance.run() ```