-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
No longer required now that py2 support has been dropped
- Loading branch information
1 parent
ea81059
commit e226348
Showing
1 changed file
with
34 additions
and
227 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
================================================== | ||
:mod:`utilities` -- Optimization utility functions | ||
================================================== | ||
This module provides utility classes and decorators for evolutionary computations. | ||
.. Copyright 2012 Aaron Garrett | ||
|
@@ -23,214 +23,21 @@ | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
THE SOFTWARE. | ||
.. module:: utilities | ||
.. moduleauthor:: Aaron Garrett <[email protected]> | ||
""" | ||
import collections | ||
import functools | ||
import multiprocessing | ||
from collections import OrderedDict | ||
|
||
try: | ||
import cPickle as pickle | ||
except ImportError: | ||
import pickle | ||
|
||
|
||
try: | ||
from collections import OrderedDict as OrderedDict | ||
except ImportError: | ||
# http://code.activestate.com/recipes/576693/ (r9) | ||
# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. | ||
# Passes Python2.7's test suite and incorporates all the latest updates. | ||
|
||
try: | ||
from thread import get_ident as _get_ident | ||
except ImportError: | ||
from dummy_thread import get_ident as _get_ident | ||
try: | ||
from _abcoll import KeysView, ValuesView, ItemsView | ||
except ImportError: | ||
pass | ||
|
||
class OrderedDict(dict): | ||
def __init__(self, *args, **kwds): | ||
if len(args) > 1: | ||
raise TypeError('expected at most 1 arguments, got %d' % len(args)) | ||
try: | ||
self.__root | ||
except AttributeError: | ||
self.__root = root = [] # sentinel node | ||
root[:] = [root, root, None] | ||
self.__map = {} | ||
self.__update(*args, **kwds) | ||
|
||
def __setitem__(self, key, value, dict_setitem=dict.__setitem__): | ||
if key not in self: | ||
root = self.__root | ||
last = root[0] | ||
last[1] = root[0] = self.__map[key] = [last, root, key] | ||
dict_setitem(self, key, value) | ||
|
||
def __delitem__(self, key, dict_delitem=dict.__delitem__): | ||
dict_delitem(self, key) | ||
link_prev, link_next, key = self.__map.pop(key) | ||
link_prev[1] = link_next | ||
link_next[0] = link_prev | ||
|
||
def __iter__(self): | ||
root = self.__root | ||
curr = root[1] | ||
while curr is not root: | ||
yield curr[2] | ||
curr = curr[1] | ||
|
||
def __reversed__(self): | ||
root = self.__root | ||
curr = root[0] | ||
while curr is not root: | ||
yield curr[2] | ||
curr = curr[0] | ||
|
||
def clear(self): | ||
try: | ||
for node in self.__map.itervalues(): | ||
del node[:] | ||
root = self.__root | ||
root[:] = [root, root, None] | ||
self.__map.clear() | ||
except AttributeError: | ||
pass | ||
dict.clear(self) | ||
|
||
def popitem(self, last=True): | ||
if not self: | ||
raise KeyError('dictionary is empty') | ||
root = self.__root | ||
if last: | ||
link = root[0] | ||
link_prev = link[0] | ||
link_prev[1] = root | ||
root[0] = link_prev | ||
else: | ||
link = root[1] | ||
link_next = link[1] | ||
root[1] = link_next | ||
link_next[0] = root | ||
key = link[2] | ||
del self.__map[key] | ||
value = dict.pop(self, key) | ||
return key, value | ||
|
||
def keys(self): | ||
return list(self) | ||
|
||
def values(self): | ||
return [self[key] for key in self] | ||
|
||
def items(self): | ||
return [(key, self[key]) for key in self] | ||
|
||
def iterkeys(self): | ||
return iter(self) | ||
|
||
def itervalues(self): | ||
for k in self: | ||
yield self[k] | ||
|
||
def iteritems(self): | ||
for k in self: | ||
yield (k, self[k]) | ||
|
||
def update(*args, **kwds): | ||
if len(args) > 2: | ||
raise TypeError('update() takes at most 2 positional ' | ||
'arguments (%d given)' % (len(args),)) | ||
elif not args: | ||
raise TypeError('update() takes at least 1 argument (0 given)') | ||
self = args[0] | ||
other = () | ||
if len(args) == 2: | ||
other = args[1] | ||
if isinstance(other, dict): | ||
for key in other: | ||
self[key] = other[key] | ||
elif hasattr(other, 'keys'): | ||
for key in other.keys(): | ||
self[key] = other[key] | ||
else: | ||
for key, value in other: | ||
self[key] = value | ||
for key, value in kwds.items(): | ||
self[key] = value | ||
|
||
__update = update | ||
__marker = object() | ||
|
||
def pop(self, key, default=__marker): | ||
if key in self: | ||
result = self[key] | ||
del self[key] | ||
return result | ||
if default is self.__marker: | ||
raise KeyError(key) | ||
return default | ||
|
||
def setdefault(self, key, default=None): | ||
if key in self: | ||
return self[key] | ||
self[key] = default | ||
return default | ||
|
||
def __repr__(self, _repr_running={}): | ||
call_key = id(self), _get_ident() | ||
if call_key in _repr_running: | ||
return '...' | ||
_repr_running[call_key] = 1 | ||
try: | ||
if not self: | ||
return '%s()' % (self.__class__.__name__,) | ||
return '%s(%r)' % (self.__class__.__name__, self.items()) | ||
finally: | ||
del _repr_running[call_key] | ||
|
||
def __reduce__(self): | ||
items = [[k, self[k]] for k in self] | ||
inst_dict = vars(self).copy() | ||
for k in vars(OrderedDict()): | ||
inst_dict.pop(k, None) | ||
if inst_dict: | ||
return (self.__class__, (items,), inst_dict) | ||
return self.__class__, (items,) | ||
|
||
def copy(self): | ||
return self.__class__(self) | ||
|
||
@classmethod | ||
def fromkeys(cls, iterable, value=None): | ||
d = cls() | ||
for key in iterable: | ||
d[key] = value | ||
return d | ||
|
||
def __eq__(self, other): | ||
if isinstance(other, OrderedDict): | ||
return len(self)==len(other) and self.items() == other.items() | ||
return dict.__eq__(self, other) | ||
|
||
def __ne__(self, other): | ||
return not self == other | ||
|
||
def viewkeys(self): | ||
return KeysView(self) | ||
|
||
def viewvalues(self): | ||
return ValuesView(self) | ||
|
||
def viewitems(self): | ||
return ItemsView(self) | ||
|
||
|
||
class BoundedOrderedDict(OrderedDict): | ||
def __init__(self, *args, **kwds): | ||
self._lock = multiprocessing.Lock() | ||
|
@@ -251,37 +58,37 @@ def _checklen(self): | |
|
||
def memoize(func=None, maxlen=None): | ||
"""Cache a function's return value each time it is called. | ||
This function serves as a function decorator to provide a caching of | ||
evaluated fitness values. If called later with the same arguments, | ||
evaluated fitness values. If called later with the same arguments, | ||
the cached value is returned instead of being re-evaluated. | ||
This decorator assumes that candidates are individually pickleable, | ||
and their pickled values are used for hashing into a dictionary. It | ||
should be used when evaluating an *expensive* fitness | ||
function to avoid costly re-evaluation of those fitnesses. The | ||
This decorator assumes that candidates are individually pickleable, | ||
and their pickled values are used for hashing into a dictionary. It | ||
should be used when evaluating an *expensive* fitness | ||
function to avoid costly re-evaluation of those fitnesses. The | ||
typical usage is as follows:: | ||
@memoize | ||
def expensive_fitness_function(candidates, args): | ||
# Implementation of expensive fitness calculation | ||
pass | ||
It is also possible to provide the named argument *maxlen*, which | ||
specifies the size of the memoization cache to use. (If *maxlen* is | ||
``None``, then an unbounded cache is used.) Once the size of the cache | ||
``None``, then an unbounded cache is used.) Once the size of the cache | ||
has reached *maxlen*, the oldest element is replaced by the newest | ||
element in order to keep the size constant. This usage is as follows:: | ||
@memoize(maxlen=100) | ||
def expensive_fitness_function(candidates, args): | ||
# Implementation of expensive fitness calculation | ||
pass | ||
.. warning:: The ``maxlen`` parameter must be passed as a named keyword | ||
argument, or an ``AttributeError`` will be raised (e.g., saying | ||
argument, or an ``AttributeError`` will be raised (e.g., saying | ||
``@memoize(100)`` will cause an error). | ||
""" | ||
if func is not None: | ||
cache = BoundedOrderedDict(maxlen=maxlen) | ||
|
@@ -303,53 +110,53 @@ def memoize_factory(func): | |
|
||
class Objectify(object): | ||
"""Create an "objectified" version of a function. | ||
This function allows an ordinary function passed to it to | ||
become essentially a callable instance of a class. For inspyred, | ||
This function allows an ordinary function passed to it to | ||
become essentially a callable instance of a class. For inspyred, | ||
this means that evolutionary operators (selectors, variators, | ||
replacers, etc.) can be created as normal functions and then | ||
be given the ability to have attributes *that are specific to | ||
the object*. Python functions can always have attributes without | ||
employing any special mechanism, but those attributes exist for the | ||
employing any special mechanism, but those attributes exist for the | ||
function, and there is no way to create a new "object" except | ||
by implementing a new function with the same functionality. | ||
This class provides a way to "objectify" the same function | ||
multiple times in order to provide each "object" with its own | ||
set of independent attributes. | ||
The attributes that are created on an objectified function are | ||
passed into that function via the ubiquitous ``args`` variable | ||
in inspyred. Any user-specified attributes are added to the | ||
in inspyred. Any user-specified attributes are added to the | ||
``args`` dictionary and replace any existing entry if necessary. | ||
If the function modifies those entries in the dictionary (e.g., | ||
when dynamically modifying parameters), the corresponding | ||
when dynamically modifying parameters), the corresponding | ||
attributes are modified as well. | ||
Essentially, a local copy of the ``args`` dictionary is created | ||
into which the attributes are inserted. This modified local copy | ||
into which the attributes are inserted. This modified local copy | ||
is then passed to the function. After the function returns, the | ||
values of the attributes from the dictionary are retrieved and | ||
values of the attributes from the dictionary are retrieved and | ||
are used to update the class attributes. | ||
The typical usage is as follows:: | ||
def typical_function(*args, **kwargs): | ||
# Implementation of typical function | ||
pass | ||
fun_one = Objectify(typical_function) | ||
fun_two = Objectify(typical_function) | ||
fun_one.attribute = value_one | ||
fun_two.attribute = value_two | ||
""" | ||
def __init__(self, func): | ||
self.func = func | ||
try: | ||
functools.update_wrapper(self, func) | ||
except: | ||
pass | ||
|
||
def __call__(self, *args, **kwargs): | ||
params = vars(self) | ||
try: | ||
|