Source code for Evolution
1"""Main evolution loop orchestrator.
2
3Wires together the protocol implementations (population generation,
4circuit factory, fitness evaluation, reproduction) and runs the
5generational evolution loop with async hardware measurements.
6
7.. warning::
8 This module is marked as NOT CURRENT VERSION. See
9 :class:`~TrivialImplementation.TrivialEvolution` for the reference
10 implementation currently used for testing.
11"""
12
13from BitstreamEvolutionProtocols import CircuitFactory, EvaluatePopulationFitness, GenDataFactory, GenerateMeasurements, Hardware, Population, Reproducer
14from PlotDataRecorder import PlotDataRecorder
15from Population.PopulationInitialization import GenerateInitialPopulations
16
17import asyncio
18
[docs]
19class Evolution:
20 """
21 NOT CURRENT VERSION, SHOULD BE REVISED USING TRIVIAL_EVOLUTION
22 This class utilizes the protocols defined to run experiments
23 There is only one implementation of Evolution needed
24 If there are any desired features that are not supported in this version of the class
25 then this class should be modified to support them *and* preserve existing behavior
26 """
27
[docs]
28 def __init__(self, gen_data_factory: GenDataFactory, circuit_factory: CircuitFactory, reproduce: Reproducer,
29 gen_init_populations: GenerateInitialPopulations, eval_population_fitness: EvaluatePopulationFitness,
30 generate_measurements: GenerateMeasurements, hardware: Hardware, plot_data_recorder: PlotDataRecorder):
31 '''
32 Initializes the Evolution object with all of the protocols.
33 Does not execute any protocols
34 '''
35 self.__gen_data_factory = gen_data_factory
36 self.__circuit_factory = circuit_factory
37 self.__reproduce = reproduce
38 self.__gen_init_populations = gen_init_populations
39 self.__eval_population_fitness = eval_population_fitness
40 self.__generate_measurements = generate_measurements
41 self.__hardware = hardware
42 self.__plot_data_recorder = plot_data_recorder
43
[docs]
44 def run(self):
45 '''
46 Runs the desired experiment, based on the protocols provided
47 '''
48 populations: list[Population] = self.__gen_init_populations()
49 gen_data = self.__gen_data_factory(None)
50 while gen_data is not None:
51 measurements = self.__generate_measurements(self.__circuit_factory, populations)
52 tasks = [self.__hardware.request_measurement(m) for m in measurements]
53 # asyncio.gather dispatches all measurement requests concurrently. In the intended
54 # server-client model, each await genuinely suspends while waiting for a network
55 # response, so multiple FPGAs are measured in parallel here.
56 # NOTE: asyncio.run() creates a new event loop each generation and destroys it
57 # after. A cleaner approach is to make run() itself async (i.e. `async def run`)
58 # and use `await asyncio.gather(*tasks)` directly, keeping one event loop for the
59 # whole experiment. See .claude/docs/hardware_concurrency.md for discussion.
60 results = asyncio.run( asyncio.gather(*tasks) )
61
62 for p in populations:
63 self.__eval_population_fitness(p, results)
64 populations = list(map(lambda p: self.__reproduce(p), populations))
65
66 fits: list[float] = []
67 for p in populations:
68 for (i, f) in p:
69 fits.append(f) # type: ignore
70 # TODO: calculate diversity
71 self.__plot_data_recorder.record_generation(fits, gen_data.generation_number, 0)
72
73 gen_data = self.__gen_data_factory(gen_data)