Skip to content

Instantly share code, notes, and snippets.

@fmder
Last active May 15, 2020 20:19
Show Gist options
  • Save fmder/a8eb232e8b51a56a300b8494d5255622 to your computer and use it in GitHub Desktop.
Save fmder/a8eb232e8b51a56a300b8494d5255622 to your computer and use it in GitHub Desktop.
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