~ubuntu-branches/ubuntu/saucy/deap/saucy

« back to all changes in this revision

Viewing changes to deap/creator.py

  • Committer: Package Import Robot
  • Author(s): Miriam Ruiz, Jakub Wilk, Miriam Ruiz
  • Date: 2011-11-17 11:53:15 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20111117115315-k9lkwpqcbsq8n0q7
Tags: 0.7.1-1
[ Jakub Wilk ]
* Add Vcs-* fields.

[ Miriam Ruiz ]
* New Upstream Release
* Upgraded Standards-Version from 3.9.1 to 3.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#    This file is part of DEAP.
 
2
#
 
3
#    DEAP is free software: you can redistribute it and/or modify
 
4
#    it under the terms of the GNU Lesser General Public License as
 
5
#    published by the Free Software Foundation, either version 3 of
 
6
#    the License, or (at your option) any later version.
 
7
#
 
8
#    DEAP is distributed in the hope that it will be useful,
 
9
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
11
#    GNU Lesser General Public License for more details.
 
12
#
 
13
#    You should have received a copy of the GNU Lesser General Public
 
14
#    License along with DEAP. If not, see <http://www.gnu.org/licenses/>.
 
15
 
 
16
"""The :mod:`~deap.creator` module is the heart and soul of DEAP, it allows to
 
17
create, at runtime, classes that will fulfill the needs of your evolutionary
 
18
algorithms. This module follows the meta-factory paradigm by allowing to
 
19
create new classes via both composition and inheritance. Attributes both datas
 
20
and functions are added to existing types in order to create new types
 
21
empowered with user specific evolutionary computation capabilities. In effect,
 
22
new classes can be built from any imaginable type, from :class:`list` to
 
23
:class:`set`, :class:`dict`, :class:`~deap.base.Tree` and more,
 
24
providing the possibility to implement genetic algorithms, genetic
 
25
programming, evolution strategies, particle swarm optimizers, and many more.
 
26
"""
 
27
 
 
28
import array
 
29
import copy
 
30
 
 
31
class_replacers = {}
 
32
"""Some classes in Python's standard library as well as third party library
 
33
may be in part incompatible with the logic used in DEAP. In order to palliate
 
34
to this problem, the method :func:`create` uses the dictionary
 
35
`class_replacers` to identify if the base type provided is problematic, and if
 
36
so  the new class inherits from the replacement class instead of the
 
37
original base class.
 
38
 
 
39
`class_replacers` keys are classes to be replaced and the values are the
 
40
replacing classes.
 
41
"""
 
42
 
 
43
try:
 
44
    import numpy
 
45
    (numpy.ndarray, numpy.array)
 
46
except ImportError:
 
47
    # Numpy is not present, skip the definition of the replacement class.
 
48
    pass
 
49
except AttributeError:
 
50
    # Numpy is present, but there is either no ndarray or array in numpy,
 
51
    # also skip the definition of the replacement class.
 
52
    pass
 
53
else:
 
54
    class _numpy_array(numpy.ndarray):
 
55
        def __getslice__(self, i, j):
 
56
            """Overrides the getslice from numpy.ndarray that returns a shallow
 
57
            copy of the slice.
 
58
            """
 
59
            return numpy.ndarray.__getslice__(self, i, j).copy()
 
60
        
 
61
        def __deepcopy__(self, memo):
 
62
            """Overrides the deepcopy from numpy.ndarray that does not copy
 
63
            the object's attributes.
 
64
            """
 
65
            copy_ = numpy.ndarray.__deepcopy__(self, memo)
 
66
            copy_.__dict__.update(copy.deepcopy(self.__dict__, memo))
 
67
            return copy_
 
68
 
 
69
        @staticmethod
 
70
        def __new__(cls, iterable):
 
71
            """Creates a new instance of a numpy.ndarray from a function call"""
 
72
            return numpy.array(list(iterable)).view(cls)
 
73
 
 
74
        def __array_finalize__(self, obj):
 
75
            # __init__ will reinitialize every member of the subclass.
 
76
            # this might not be desirable for example in the case of an ES. 
 
77
            self.__init__()
 
78
 
 
79
            # Instead, e could use the following that will simply deepcopy 
 
80
            # every member that is present in the original class
 
81
            # This is significantly slower. 
 
82
            #if self.__class__ == obj.__class__:
 
83
            #    self.__dict__.update(copy.deepcopy(obj.__dict__))
 
84
    class_replacers[numpy.ndarray] = _numpy_array
 
85
 
 
86
class _array(array.array):
 
87
    @staticmethod
 
88
    def __new__(cls, seq=()):
 
89
        return super(_array, cls).__new__(cls, cls.typecode, seq)
 
90
    
 
91
    def __deepcopy__(self, memo):
 
92
        """Overrides the deepcopy from array.array that does not copy
 
93
        the object's attributes and class type.
 
94
        """
 
95
        cls = self.__class__
 
96
        copy_ = cls.__new__(cls, self)
 
97
        memo[id(self)] = copy_
 
98
        copy_.__dict__.update(copy.deepcopy(self.__dict__, memo))
 
99
        return copy_
 
100
 
 
101
    def __reduce__(self):
 
102
        return (self.__class__, (list(self),), self.__dict__)
 
103
class_replacers[array.array] = _array
 
104
 
 
105
def create(name, base, **kargs):
 
106
    """Creates a new class named *name* inheriting from *base* in the
 
107
    :mod:`~deap.creator` module. The new class can have attributes defined by
 
108
    the subsequent keyword arguments passed to the function create. If the
 
109
    argument is a class (without the parenthesis), the __init__ function is
 
110
    called in the initialization of an instance of the new object and the
 
111
    returned instance is added as an attribute of the class' instance.
 
112
    Otherwise, if the argument is not a class, (for example an :class:`int`),
 
113
    it is added as a "static" attribute of the class.
 
114
    
 
115
    The following is used to create a class :class:`Foo` inheriting from the
 
116
    standard :class:`list` and having an attribute :attr:`bar` being an empty
 
117
    dictionary and a static attribute :attr:`spam` initialized to 1. ::
 
118
    
 
119
        create("Foo", list, bar=dict, spam=1)
 
120
        
 
121
    This above line is exactly the same as defining in the :mod:`creator`
 
122
    module something like the following. ::
 
123
    
 
124
        def Foo(list):
 
125
            spam = 1
 
126
            
 
127
            def __init__(self):
 
128
                self.bar = dict()
 
129
    """
 
130
    dict_inst = {}
 
131
    dict_cls = {}
 
132
    for obj_name, obj in kargs.iteritems():
 
133
        if hasattr(obj, "__call__"):
 
134
            dict_inst[obj_name] = obj
 
135
        else:
 
136
            dict_cls[obj_name] = obj
 
137
 
 
138
    # Check if the base class has to be replaced
 
139
    if base in class_replacers:
 
140
        base = class_replacers[base]
 
141
 
 
142
    # A DeprecationWarning is raised when the object inherits from the 
 
143
    # class "object" which leave the option of passing arguments, but
 
144
    # raise a warning stating that it will eventually stop permitting
 
145
    # this option. Usually this happens when the base class does not
 
146
    # override the __init__ method from object.
 
147
    def initType(self, *args, **kargs):
 
148
        """Replace the __init__ function of the new type, in order to
 
149
        add attributes that were defined with **kargs to the instance.
 
150
        """
 
151
        for obj_name, obj in dict_inst.iteritems():
 
152
            setattr(self, obj_name, obj())
 
153
        if base.__init__ is not object.__init__:
 
154
            base.__init__(self, *args, **kargs)
 
155
        else:
 
156
            base.__init__(self)
 
157
 
 
158
    objtype = type(name, (base,), dict_cls)
 
159
    objtype.__init__ = initType
 
160
    globals()[name] = objtype