From 5eb2a5f40652ec58064996a6ee284810b7e8588e Mon Sep 17 00:00:00 2001 From: Giovanni Squillero Date: Wed, 18 Sep 2024 16:01:45 +0200 Subject: [PATCH] patch: array_parameter --- examples/classics/onemax.py | 28 ++++++++++++++++------------ src/byron/framework/parameter.py | 29 +++++++++++++++-------------- src/byron/operators/mutations.py | 20 ++++---------------- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/examples/classics/onemax.py b/examples/classics/onemax.py index 2bc2d8e..96dbc82 100755 --- a/examples/classics/onemax.py +++ b/examples/classics/onemax.py @@ -14,7 +14,7 @@ import byron -NUM_BITS = 50 +NUM_BITS = 16 @byron.fitness_function @@ -26,23 +26,27 @@ def fitness(genotype): def main(): byron.welcome() - macro = byron.f.macro('{v}', v=byron.f.array_parameter('01', NUM_BITS + 1)) + macro = byron.f.macro('{v}', v=byron.f.array_parameter('01', NUM_BITS)) top_frame = byron.f.sequence([macro]) - - evaluator = byron.evaluator.PythonEvaluator(fitness, strip_phenotypes=True) - # evaluators.append(byron.evaluator.PythonEvaluator(fitness, strip_phenotypes=True, backend='thread_pool')) - # evaluators.append(byron.evaluator.PythonEvaluator(fitness, strip_phenotypes=True, backend='joblib')) - # evaluators.append(byron.evaluator.ScriptEvaluator('./onemax-shell.sh', args=['-f'])) - # evaluators.append(byron.evaluator.MakefileEvaluator('genome.dat', required_files=['onemax-shell.sh'])) - + evaluator = byron.evaluator.PythonEvaluator(fitness, strip_phenotypes=True, backend=None) byron.logger.info("main: Using %s", evaluator) - population = byron.ea.vanilla_ea( + population = byron.ea.adaptive_ea( top_frame, evaluator, max_generation=5_000, lambda_=20, mu=30, max_fitness=NUM_BITS ) - print() + macro = byron.f.macro('{v}', v=byron.f.array_parameter(range(2), NUM_BITS)) + top_frame = byron.f.sequence([macro]) + evaluator = byron.evaluator.PythonEvaluator(fitness, strip_phenotypes=True, backend=None) + byron.logger.info("main: Using %s", evaluator) + population = byron.ea.adaptive_ea( + top_frame, evaluator, max_generation=5_000, lambda_=20, mu=30, max_fitness=NUM_BITS + ) - byron.sys.log_operators() + # evaluator = byron.evaluator.PythonEvaluator(fitness, strip_phenotypes=True) + # evaluators.append(byron.evaluator.PythonEvaluator(fitness, strip_phenotypes=True, backend='thread_pool')) + # evaluators.append(byron.evaluator.PythonEvaluator(fitness, strip_phenotypes=True, backend='joblib')) + # evaluators.append(byron.evaluator.ScriptEvaluator('./onemax-shell.sh', args=['-f'])) + # evaluators.append(byron.evaluator.MakefileEvaluator('genome.dat', required_files=['onemax-shell.sh'])) if __name__ == "__main__": diff --git a/src/byron/framework/parameter.py b/src/byron/framework/parameter.py index ad20c45..8174b6f 100644 --- a/src/byron/framework/parameter.py +++ b/src/byron/framework/parameter.py @@ -188,6 +188,7 @@ class T(ParameterArrayABC): def __init__(self): super().__init__() + self._raw_value = None def is_correct(self, obj: Any) -> bool: if len(obj) != length: @@ -201,10 +202,12 @@ def run_paranoia_checks(self) -> bool: def mutate(self, strength: float = 1.0) -> None: if strength == 1: - new_value = [rrandom.choice(symbols) for _ in range(length)] + self._raw_value = [rrandom.choice(symbols) for _ in range(length)] else: - new_value = [rrandom.choice(symbols) if rrandom.boolean(strength) else old for old in self._value] - self.value = sep.join(new_value) + self._raw_value = [ + rrandom.choice(symbols) if rrandom.boolean(strength) else old for old in self._raw_value + ] + self.value = sep.join(self._raw_value) T._patch_info(name="Array[" + "".join(str(a) for a in symbols) + f"x{length}]") return T @@ -215,14 +218,13 @@ def _array_parameter_range(min_: int, max_: int, length: int, sep: str) -> type[ class T(ParameterArrayABC): __slots__ = [] # Preventing the automatic creation of __dict__ - MIN = min_ - MAX = max_ + RANGE = (min_, max_) LENGTH = length SEP = sep def __init__(self): super().__init__() - self.raw_value = None + self._raw_value = None def is_correct(self, obj: Any) -> bool: return all(min_ <= int(e) < max_ for e in obj.split(sep)) @@ -234,14 +236,13 @@ def run_paranoia_checks(self) -> bool: def mutate(self, strength: float = 1.0) -> None: if strength == 1: - self.raw_value = [rrandom.random_int(min_, max_) for _ in range(length)] + self._raw_value = [rrandom.random_int(min_, max_) for _ in range(length)] else: - i = rrandom.random_int(0, length) - self.raw_value[i] = rrandom.random_int(min_, max_) - while rrandom.boolean(strength): - i = rrandom.random_int(0, length) - self.raw_value[i] = rrandom.random_int(min_, max_) - self.value = sep.join(str(_) for _ in self.raw_value) + self._raw_value = [ + rrandom.random_int(min_, max_) if rrandom.boolean(strength) else old for old in self._raw_value + ] + self.value = sep.join(str(_) for _ in self._raw_value) + self.value = sep.join(str(_) for _ in self._raw_value) T._patch_info(name=f"Array[range({min_}, {max_})]x{length}]") return T @@ -256,9 +257,9 @@ def array_parameter( assert check_value_range(int(length), 1) if isinstance(symbols, range): assert check_value_range(symbols.stop - symbols.start, min_=1) - return _array_parameter_range(symbols.start, symbols.stop, int(length), sep=sep) if sep is None: sep = ' ' + return _array_parameter_range(symbols.start, symbols.stop, int(length), sep=sep) else: assert check_valid_type(symbols, Collection) assert check_valid_length(symbols, 1) diff --git a/src/byron/operators/mutations.py b/src/byron/operators/mutations.py index bee15ae..6bc3717 100644 --- a/src/byron/operators/mutations.py +++ b/src/byron/operators/mutations.py @@ -26,7 +26,7 @@ from collections import Counter from copy import deepcopy -from math import ceil, floor +from math import floor from networkx import dfs_preorder_nodes @@ -39,7 +39,7 @@ @genetic_operator(num_parents=1) -def single_parameter_mutation(parent: Individual, strength=1.0) -> list['Individual']: +def generic_parameter_mutation(parent: Individual, strength=1.0) -> list['Individual']: """Mutates a parameter The function tries at least 100 times to change the parameter by calling `mutate` with the given strength. @@ -63,26 +63,14 @@ def single_parameter_mutation(parent: Individual, strength=1.0) -> list['Individ @genetic_operator(num_parents=1) -def single_element_array_parameter_mutation(parent: Individual, strength=1.0) -> list['Individual']: - scale = 0.05 - ext_mutation = 1 / (scale * strength) +def array_parameter_mutation(parent: Individual, strength=1.0) -> list['Individual']: offspring = parent.clone candidates = [p for p in offspring.parameters if isinstance(p, ParameterArrayABC)] if not candidates: raise ByronOperatorFailure parameter = rrandom.choice(candidates) - old_value = list(parameter.value) - new_value = list(parameter.value) - for _ in range(ceil(len(parameter.value) // ext_mutation)): - i = rrandom.random_int(0, len(parameter.value)) - new_value[i] = rrandom.choice(parameter.DIGITS) - - if strength > 0 and parameter.value == old_value: - raise ByronOperatorFailure - - parameter.value = ''.join(new_value) - + parameter.mutate(strength=strength) return [offspring]