Source code for EvaluateFitness.EvalPulseCountFitness
1"""Oscillation pulse-count fitness evaluator.
2
3Fitness is computed as the inverse distance from a target pulse count.
4Used with :class:`~EvaluateFitness.EvaluateFitness.EvaluateFitness` via
5the :class:`~EvaluateFitness.EvaluateFitness.FitnessEvaluator` protocol::
6
7 evaluator = EvalPulseCountFitness(target=1000, plot_data_recorder=recorder)
8 EvaluateFitness(evaluator)
9"""
10
11from PlotDataRecorder import PlotDataRecorder
12
13
[docs]
14class EvalPulseCountFitness:
15 """Evaluate fitness based on how close measured pulse counts are to a target.
16
17 For each individual the minimum fitness across all samples is used,
18 where fitness = 1 when pulses == target, 0 when pulses == 0, and
19 ``1 / abs(target - pulses)`` otherwise.
20 """
[docs]
21 def __init__(self, target: int, plot_data_recorder: PlotDataRecorder):
22 """Create an evaluator targeting *target* pulses."""
23 self.__target = target
24 self.__plot_data_recorder = plot_data_recorder
25
[docs]
26 def start_eval(self):
27 """Called before evaluating a generation (no-op for pulse count)."""
28 pass
29
[docs]
30 def end_eval(self):
31 """Called after evaluating a generation (no-op for pulse count)."""
32 pass
33
[docs]
34 def calculate_success(self, data: list[int], index: int, src_pop: str) -> float:
35 """Return the minimum fitness across all pulse-count samples in *data*."""
36 acc: list[float] = []
37 for p in data:
38 acc.append(self.__calculate_individual(p))
39 fit = min(acc)
40
41 min_idx = acc.index(fit)
42 pulses_at_min = data[min_idx]
43
44 self.__plot_data_recorder.record_all_live_data(index, pulses_at_min, src_pop)
45
46 return fit
47
[docs]
48 def calculate_error(self, err: Exception, index: int, src_pop: str) -> float:
49 """Return zero fitness when measurement fails."""
50 self.__plot_data_recorder.record_all_live_data(index, -1, src_pop)
51 return 0
52
53 def __calculate_individual(self, pulses: int) -> float:
54 if pulses == self.__target:
55 return 1
56 elif pulses == 0:
57 return 0
58 else:
59 return 1.0 / abs(self.__target - pulses)