-
Notifications
You must be signed in to change notification settings - Fork 107
Commit
Update for Python 3 compatibility
- Loading branch information
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
""" | ||
:mod:`FunctionSlot` -- function slots module | ||
================================================================== | ||
The *function slot* concept is large used by Pyevolve, the idea | ||
is simple, each genetic operator or any operator, can be assigned | ||
to a slot, by this way, we can add more than simple one operator, | ||
we can have for example, two or more mutator operators at same time, | ||
two or more evaluation functions, etc. In this :mod:`FunctionSlot` module, | ||
you'll find the class :class:`FunctionSlot.FunctionSlot`, which is the slot class. | ||
""" | ||
|
||
from random import uniform as rand_uniform | ||
|
||
from . import Util | ||
import collections | ||
|
||
class FunctionSlot(object): | ||
""" FunctionSlot Class - The function slot | ||
Example: | ||
>>> genome.evaluator.set(eval_func) | ||
>>> genome.evaluator[0] | ||
<function eval_func at 0x018C8930> | ||
>>> genome.evaluator | ||
Slot [Evaluation Function] (Count: 1) | ||
Name: eval_func | ||
>>> genome.evaluator.clear() | ||
>>> genome.evaluator | ||
Slot [Evaluation Function] (Count: 0) | ||
No function | ||
You can add weight to functions when using the `rand_apply` paramter: | ||
>>> genome.evaluator.set(eval_main, 0.9) | ||
>>> genome.evaluator.add(eval_sec, 0.3) | ||
>>> genome.evaluator.setRandomApply() | ||
In the above example, the function *eval_main* will be called with 90% of | ||
probability and the *eval_sec* will be called with 30% of probability. | ||
There are another way to add functions too: | ||
>>> genome.evaluator += eval_func | ||
:param name: the slot name | ||
:param rand_apply: if True, just one of the functions in the slot | ||
will be applied, this function is randomly picked based | ||
on the weight of the function added. | ||
""" | ||
|
||
def __init__(self, name="Anonymous Function", rand_apply=False): | ||
""" The creator of the FunctionSlot Class """ | ||
self.funcList = [] | ||
self.funcWeights = [] | ||
self.slotName = name | ||
self.rand_apply = rand_apply | ||
|
||
def __typeCheck(self, func): | ||
""" Used internally to check if a function passed to the | ||
function slot is callable. Otherwise raises a TypeError exception. | ||
:param func: the function object | ||
""" | ||
if not isinstance(func, collections.Callable): | ||
This comment has been minimized.
Sorry, something went wrong. |
||
Util.raiseException("The function must be a method or function", TypeError) | ||
|
||
def __iadd__(self, func): | ||
""" To add more functions using the += operator | ||
.. versionadded:: 0.6 | ||
The __iadd__ method. | ||
""" | ||
self.__typeCheck(func) | ||
self.funcList.append(func) | ||
return self | ||
|
||
def __getitem__(self, index): | ||
""" Used to retrieve some slot function index """ | ||
return self.funcList[index] | ||
|
||
def __setitem__(self, index, value): | ||
""" Used to set the index slot function """ | ||
self.__typeCheck(value) | ||
self.funcList[index] = value | ||
|
||
def __iter__(self): | ||
""" Return the function list iterator """ | ||
return iter(self.funcList) | ||
|
||
def __len__(self): | ||
""" Return the number of functions on the slot | ||
.. versionadded:: 0.6 | ||
The *__len__* method | ||
""" | ||
return len(self.funcList) | ||
|
||
def setRandomApply(self, flag=True): | ||
""" Sets the random function application, in this mode, the | ||
function will randomly choose one slot to apply | ||
:param flag: True or False | ||
""" | ||
if not isinstance(flag, bool): | ||
Util.raiseException("Random option must be True or False", TypeError) | ||
|
||
self.rand_apply = flag | ||
|
||
def clear(self): | ||
""" Used to clear the functions in the slot """ | ||
if len(self.funcList) > 0: | ||
del self.funcList[:] | ||
del self.funcWeights[:] | ||
|
||
def add(self, func, weight=0.5): | ||
""" Used to add a function to the slot | ||
:param func: the function to be added in the slot | ||
:param weight: used when you enable the *random apply*, it's the weight | ||
of the function for the random selection | ||
.. versionadded:: 0.6 | ||
The `weight` parameter. | ||
""" | ||
self.__typeCheck(func) | ||
self.funcList.append(func) | ||
self.funcWeights.append(weight) | ||
|
||
def isEmpty(self): | ||
""" Return true if the function slot is empy """ | ||
return (len(self.funcList) == 0) | ||
|
||
def set(self, func, weight=0.5): | ||
""" Used to clear all functions in the slot and add one | ||
:param func: the function to be added in the slot | ||
:param weight: used when you enable the *random apply*, it's the weight | ||
of the function for the random selection | ||
.. versionadded:: 0.6 | ||
The `weight` parameter. | ||
.. note:: the method *set* of the function slot remove all previous | ||
functions added to the slot. | ||
""" | ||
self.clear() | ||
self.__typeCheck(func) | ||
self.add(func, weight) | ||
|
||
def apply(self, index, obj, **args): | ||
""" Apply the index function | ||
:param index: the index of the function | ||
:param obj: this object is passes as parameter to the function | ||
:param args: this args dictionary is passed to the function | ||
""" | ||
if len(self.funcList) <= 0: | ||
raise Exception("No function defined: " + self.slotName) | ||
return self.funcList[index](obj, **args) | ||
|
||
def applyFunctions(self, obj=None, **args): | ||
""" Generator to apply all function slots in obj | ||
:param obj: this object is passes as parameter to the function | ||
:param args: this args dictionary is passed to the function | ||
""" | ||
if len(self.funcList) <= 0: | ||
Util.raiseException("No function defined: " + self.slotName) | ||
|
||
if not self.rand_apply: | ||
for f in self.funcList: | ||
yield f(obj, **args) | ||
else: | ||
v = rand_uniform(0, 1) | ||
fobj = None | ||
for func, weight in zip(self.funcList, self.funcWeights): | ||
fobj = func | ||
if v < weight: | ||
break | ||
v = v - weight | ||
|
||
yield fobj(obj, **args) | ||
|
||
def __repr__(self): | ||
""" String representation of FunctionSlot """ | ||
strRet = "Slot [%s] (Count: %d)\n" % (self.slotName, len(self.funcList)) | ||
|
||
if len(self.funcList) <= 0: | ||
strRet += "\t\tNo function\n" | ||
return strRet | ||
|
||
for f, w in zip(self.funcList, self.funcWeights): | ||
strRet += "\t\tName: %s - Weight: %.2f\n" % (f.__name__, w) | ||
if f.__doc__: | ||
strRet += "\t\tDoc: " + f.__doc__ + "\n" | ||
|
||
return strRet |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
""" | ||
:mod:`G1DBinaryString` -- the classical binary string chromosome | ||
===================================================================== | ||
This is the classical chromosome representation on GAs, it is the 1D | ||
Binary String. This string looks like "00011101010". | ||
Default Parameters | ||
------------------------------------------------------------- | ||
*Initializator* | ||
:func:`Initializators.G1DBinaryStringInitializator` | ||
The Binatry String Initializator for G1DBinaryString | ||
*Mutator* | ||
:func:`Mutators.G1DBinaryStringMutatorFlip` | ||
The Flip Mutator for G1DBinaryString | ||
*Crossover* | ||
:func:`Crossovers.G1DBinaryStringXSinglePoint` | ||
The Single Point Crossover for G1DBinaryString | ||
Class | ||
------------------------------------------------------------- | ||
""" | ||
|
||
from .GenomeBase import GenomeBase, G1DBase | ||
from . import Consts | ||
from . import Util | ||
|
||
class G1DBinaryString(G1DBase): | ||
""" G1DBinaryString Class - The 1D Binary String chromosome | ||
Inheritance diagram for :class:`G1DBinaryString.G1DBinaryString`: | ||
.. inheritance-diagram:: G1DBinaryString.G1DBinaryString | ||
This chromosome class extends the :class:`GenomeBase.G1DBase` class. | ||
Example: | ||
>>> genome = G1DBinaryString.G1DBinaryString(5) | ||
:param length: the 1D Binary String size | ||
""" | ||
__slots__ = ["stringLength"] | ||
|
||
def __init__(self, length=10): | ||
""" The initializator of G1DList representation """ | ||
super(G1DBinaryString, self).__init__(length) | ||
self.genomeList = [] | ||
self.stringLength = length | ||
self.initializator.set(Consts.CDefG1DBinaryStringInit) | ||
self.mutator.set(Consts.CDefG1DBinaryStringMutator) | ||
self.crossover.set(Consts.CDefG1DBinaryStringCrossover) | ||
|
||
def __setitem__(self, key, value): | ||
""" Set the specified value for an gene of List | ||
>>> g = G1DBinaryString(5) | ||
>>> for i in xrange(len(g)): | ||
... g.append(1) | ||
>>> g[4] = 0 | ||
>>> g[4] | ||
0 | ||
""" | ||
if isinstance(value, int) and value not in (0,1): | ||
Util.raiseException("The value must be zero (0) or one (1), used (%s)" % value, ValueError) | ||
elif isinstance(value, list) and not set(value) <= set([0, 1]): | ||
# if slice notation is used we check all passed values | ||
vals = set(value) - set([0, 1]) | ||
Util.raiseException("The value must be zero (0) or one (1), used (%s)" % vals, ValueError) | ||
G1DBase.__setitem__(self, key, value) | ||
|
||
def __repr__(self): | ||
""" Return a string representation of Genome """ | ||
ret = GenomeBase.__repr__(self) | ||
ret += "- G1DBinaryString\n" | ||
ret += "\tString length:\t %s\n" % (self.getListSize(),) | ||
ret += "\tString:\t\t %s\n\n" % (self.getBinary(),) | ||
return ret | ||
|
||
def getDecimal(self): | ||
""" Converts the binary string to decimal representation | ||
Example: | ||
>>> g = G1DBinaryString(5) | ||
>>> for i in xrange(len(g)): | ||
... g.append(0) | ||
>>> g[3] = 1 | ||
>>> g.getDecimal() | ||
2 | ||
:rtype: decimal value | ||
""" | ||
return int(self.getBinary(), 2) | ||
|
||
def getBinary(self): | ||
""" Returns the binary string representation | ||
Example: | ||
>>> g = G1DBinaryString(2) | ||
>>> g.append(0) | ||
>>> g.append(1) | ||
>>> g.getBinary() | ||
'01' | ||
:rtype: the binary string | ||
""" | ||
return "".join(map(str, self)) | ||
|
||
def append(self, value): | ||
""" Appends an item to the list | ||
Example: | ||
>>> g = G1DBinaryString(2) | ||
>>> g.append(0) | ||
:param value: value to be added, 0 or 1 | ||
""" | ||
if value not in [0, 1]: | ||
Util.raiseException("The value must be 0 or 1", ValueError) | ||
G1DBase.append(self, value) | ||
|
||
def copy(self, g): | ||
""" Copy genome to 'g' | ||
Example: | ||
>>> g1 = G1DBinaryString(2) | ||
>>> g1.append(0) | ||
>>> g1.append(1) | ||
>>> g2 = G1DBinaryString(2) | ||
>>> g1.copy(g2) | ||
>>> g2[1] | ||
1 | ||
:param g: the destination genome | ||
""" | ||
GenomeBase.copy(self, g) | ||
G1DBase.copy(self, g) | ||
|
||
def clone(self): | ||
""" Return a new instace copy of the genome | ||
Example: | ||
>>> g = G1DBinaryString(5) | ||
>>> for i in xrange(len(g)): | ||
... g.append(1) | ||
>>> clone = g.clone() | ||
>>> clone[0] | ||
1 | ||
:rtype: the G1DBinaryString instance clone | ||
""" | ||
newcopy = G1DBinaryString(self.getListSize()) | ||
self.copy(newcopy) | ||
return newcopy |
1 comment
on commit 07b000a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FunctionSlot.py line 66
wls (windows linux subsystem, default Ubuntu, Windows 11), python3.10 no longer accepts "collections.Callable". I had to change it to "collections.abc.Callable". Python3.9 still had the workaround, and it seems to have been the case since python3.3, based on the information at "https://docs.python.org/3.10/library/collections.abc.html". So, even if maybe this is a failure of "future", this should be kept in mind.
wls (windows linux subsystem), python3.10 no longer accepts "collections.Callable". I had to change it to "collections.abc.Callable". Python3.9 still had the workaround, and it seems to have been the case since python3.3, based on the information at "https://docs.python.org/3.10/library/collections.abc.html". So, even if maybe this is a failure of "future", this should be kept in mind.