Last active
May 15, 2020 20:19
-
-
Save fmder/a8eb232e8b51a56a300b8494d5255622 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections.abc import Sequence | |
from copy import deepcopy | |
from functools import wraps | |
from typing import Callable | |
import numpy | |
MAXIMIZE = 1.0 | |
MINIMIZE = -1.0 | |
class Fitness: | |
def __init__(self, objectives, initval=()): | |
if not isinstance(objectives, Sequence): | |
objectives = (objectives,) | |
self.objectives = numpy.asarray(objectives, dtype=numpy.float) | |
self._value = None | |
self._wvalue = None | |
self.value = initval | |
def getvalue(self) -> numpy.ndarray: | |
return self._value | |
def setvalue(self, val): | |
if not isinstance(val, Sequence) and not isinstance(val, numpy.ndarray): | |
val = (val,) | |
if len(val) > 0 and len(val) != len(self.objectives): | |
raise ValueError(f"setting fitness with {len(val)} " | |
f"value{'s' if len(val) > 1 else ''}, " | |
f"{len(self.objectives)} expected.") | |
self._value = numpy.asarray(val, dtype=numpy.float) | |
if len(val) > 0: | |
self._wvalue = self._value * self.objectives | |
def delvalue(self): | |
self._value = numpy.asarray((), dtype=numpy.float) | |
value = property(getvalue, setvalue, delvalue) | |
def __lt__(self, other: "Fitness") -> bool: | |
return other._dominates(self) | |
def __le__(self, other: "Fitness") -> bool: | |
return self < other or self == other | |
def __eq__(self, other: "Fitness") -> bool: | |
return not self._dominates(other) and not other._dominates(self) | |
def __ge__(self, other: "Fitness") -> bool: | |
return not self < other | |
def __gt__(self, other: "Fitness") -> bool: | |
return not self <= other | |
def _dominates(self, other: "Fitness") -> bool: | |
if numpy.any(self.value < other.value): | |
return False | |
elif numpy.any(self.value > other.value): | |
return True | |
return False | |
class Attributes: | |
def __init__(self, initval=None): | |
self.value = initval | |
def getvalue(self): | |
return self.value | |
def setvalue(self, val): | |
self.value = val | |
def delvalue(self): | |
self.value = None | |
class Individual(object): | |
def __init__(self): | |
self._fitnesses = dict() | |
self._attributes = dict() | |
def _register_fitness(self, name, fitness): | |
self._fitnesses[name] = fitness | |
def _register_attributes(self, name, attributes): | |
self._attributes[name] = attributes | |
def _register_property(self, name, type_): | |
def _getter(self): | |
return self.__getattribute__(type_)[name].getvalue() | |
def _setter(self, val): | |
self.__getattribute__(type_)[name].setvalue(val) | |
def _deletter(self): | |
self.__getattribute__(type_)[name].delvalue() | |
# Property is a class attribute | |
setattr(Individual, name, property(_getter, _setter, _deletter)) | |
def getattributes(self, name=None): | |
if name is None and len(self._attributes) == 1: | |
name = next(iter(self._attributes.keys())) | |
elif name is None: | |
raise AttributeError("individuals with multiple attributes " | |
"require accessor in operators") | |
return getattr(self, name) | |
def setattributes(self, name=None, value=None): | |
if name is None and len(self._attributes) == 1: | |
name = next(iter(self._attributes.keys())) | |
elif name is None: | |
raise AttributeError("individuals with multiple attributes " | |
"require accessor in operators") | |
return setattr(self, name, value) | |
def setfitness(self, name=None, value=None): | |
if name is None and len(self._fitnesses) == 1: | |
name = next(iter(self._fitnesses.keys())) | |
elif name is None: | |
raise AttributeError("individuals with multiple fitnesses " | |
"require accessor in operators") | |
return setattr(self, name, value) | |
def getfitness(self, name=None): | |
if name is None and len(self._fitnesses) == 1: | |
name = next(iter(self._fitnesses.keys())) | |
elif name is None: | |
raise AttributeError("individuals with multiple fitnesses " | |
"require accessor in operators") | |
return getattr(self, name) | |
def invalidate_fitness(self): | |
for f in self._fitnesses.values(): | |
f.delvalue() | |
def __setattr__(self, name, value): | |
if isinstance(value, Fitness): | |
if getattr(self, "_fitnesses", None) is None: | |
# self._fitnesses = dict() | |
raise AttributeError( | |
"cannot assign fitness before Individual.__init__() call") | |
if name in self._fitnesses: | |
return | |
self._register_fitness(name, value) | |
self._register_property(name, "_fitnesses") | |
elif isinstance(value, Attributes): | |
if getattr(self, "_attributes", None) is None: | |
# self._attributes = dict() | |
raise AttributeError( | |
"cannot assign attributes before Individual.__init__() call") | |
if name in self._attributes: | |
return | |
self._register_attributes(name, value) | |
self._register_property(name, "_attributes") | |
else: | |
super().__setattr__(name, value) | |
def variation(func: Callable): | |
@wraps(func) | |
def wrapper(*args, **kwargs): | |
key = kwargs.pop("key", None) | |
individuals = list() | |
variator_args = list() | |
variator_kwargs = dict() | |
for argument in args: | |
if isinstance(argument, Individual): | |
copy_ = deepcopy(argument) | |
individuals.append(copy_) | |
argument = copy_.getattributes(key) | |
variator_args.append(argument) | |
for keyword, argument in kwargs.items(): | |
if isinstance(argument, Individual): | |
copy_ = deepcopy(argument) | |
individuals.append(copy_) | |
argument = copy_.getattributes(key) | |
variator_kwargs[keyword] = argument | |
attributes = func(*variator_args, **variator_kwargs) | |
if len(individuals) > 1: | |
for i, a in zip(individuals, attributes): | |
i.setattributes(key, a) | |
i.invalidate_fitness() | |
else: | |
individuals = individuals[0] | |
individuals.setattributes(key, attributes) | |
individuals.invalidate_fitness() | |
return individuals | |
return wrapper | |
@variation | |
def cx(i1, i2, pb): | |
return i2, i1 | |
@variation | |
def mut(i, pb): | |
return i | |
# USER CODE | |
class MyIndividual(Individual): | |
def __init__(self): | |
super().__init__() | |
self.fitness = Fitness((MINIMIZE, MAXIMIZE)) | |
self.fitness2 = Fitness(MINIMIZE) | |
self.floats = Attributes() | |
self.integers = Attributes() | |
i1 = MyIndividual() | |
i2 = MyIndividual() | |
i1.fitness = [1, 2] | |
i1.fitness2 = 15 | |
i2.fitness = [3, 4] | |
print(i1.fitness) | |
print(i2.fitness) | |
i1.floats = [1.0, 2.0, 3.0] | |
i2.floats = [3.0, 4.0, 5.0] | |
i3, i4 = cx(i1, i2, pb=3, key="floats") | |
i5 = mut(i1, pb=3, key="floats") | |
print(i1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment