Package parsimony :: Package algorithms :: Module bases
[hide private]
[frames] | no frames]

Source Code for Module parsimony.algorithms.bases

  1  # -*- coding: utf-8 -*- 
  2  """ 
  3  The :mod:`parsimony.algorithms.bases` module includes several base classes 
  4  for using and creating algorithms. 
  5   
  6  Algorithms may not store states. I.e., if they are classes, do not keep 
  7  references to objects with state in the algorithm objects. It should be 
  8  possible to copy and share algorithms between e.g. estimators, and thus they 
  9  should not depend on any state. 
 10   
 11  Created on Thu Feb 20 17:42:16 2014 
 12   
 13  Copyright (c) 2013-2014, CEA/DSV/I2BM/Neurospin. All rights reserved. 
 14   
 15  @author:  Tommy Löfstedt 
 16  @email:   lofstedt.tommy@gmail.com 
 17  @license: BSD 3-clause. 
 18  """ 
 19  import abc 
 20  import functools 
 21   
 22  import parsimony.utils.consts as consts 
 23  import parsimony.functions.properties as properties 
 24   
 25  __all__ = ["BaseAlgorithm", "check_compatibility", 
 26             "ImplicitAlgorithm", "ExplicitAlgorithm", 
 27             "IterativeAlgorithm", "InformationAlgorithm"] 
28 29 30 -class BaseAlgorithm(object):
31 32 __metaclass__ = abc.ABCMeta 33 34 @staticmethod
35 - def check_compatibility(function, required_properties):
36 """Check if the function considered implements the given properties. 37 """ 38 if not isinstance(function, (list, tuple)): 39 function = [function] 40 41 for f in function: 42 for prop in required_properties: 43 if isinstance(prop, properties.OR): 44 if not prop.evaluate(f): 45 raise ValueError("%s does not implement all " \ 46 "properties %s" % (str(f), str(prop))) 47 elif not isinstance(f, prop): 48 raise ValueError("%s does not implement interface %s" % 49 (str(f), str(prop)))
50
51 - def set_params(self, **kwargs):
52 53 for k in kwargs: 54 self.__setattr__(k, kwargs[k])
55
56 - def get_params(self):
57 raise NotImplementedError('Method "get_params" has not been ' \ 58 'implemented.')
59
60 61 # TODO: Replace the one in BaseAlgorithm. 62 -def check_compatibility(f):
63 """Automatically checks if a function implements a given set of properties. 64 """ 65 @functools.wraps(f) 66 def wrapper(self, function, *args, **kwargs): 67 68 BaseAlgorithm.check_compatibility(function, self.INTERFACES) 69 70 return f(self, function, *args, **kwargs)
71 72 return wrapper 73
74 75 -def force_reset(f):
76 """Decorate run with this method to force a reset of your algorithm. 77 78 Automatically resets an algorithm by checking the implementing 79 classes and calling the appropriate reset methods. 80 """ 81 @functools.wraps(f) 82 def wrapper(self, function, *args, **kwargs): 83 84 # Add more subclasses here if necessary. 85 if isinstance(self, IterativeAlgorithm): 86 self.iter_reset() 87 if isinstance(self, InformationAlgorithm): 88 self.info_reset() 89 90 return f(self, function, *args, **kwargs)
91 92 return wrapper 93
94 95 -class ImplicitAlgorithm(BaseAlgorithm):
96 """Implicit algorithms are algorithms that do not utilise a loss function. 97 98 Implicit algorithms instead minimise or maximise some underlying function 99 implicitly, usually from the data. 100 101 Parameters 102 ---------- 103 X : One or more data matrices. 104 """ 105 __metaclass__ = abc.ABCMeta 106 107 @abc.abstractmethod
108 - def run(X, **kwargs):
109 raise NotImplementedError('Abstract method "run" must be ' \ 110 'specialised!')
111
112 113 -class ExplicitAlgorithm(BaseAlgorithm):
114 """Explicit algorithms are algorithms that minimises a given function. 115 116 The function is explicitly minimised from properties of said function. 117 118 Implementing classes should update the INTERFACES class variable with 119 the properties that function must implement. Defauls to a list with one 120 element, the Function. 121 """ 122 __metaclass__ = abc.ABCMeta 123 124 INTERFACES = [properties.Function] 125 126 @abc.abstractmethod
127 - def run(function, x, **kwargs):
128 """This function obtains a minimiser of a give function. 129 130 Parameters 131 ---------- 132 function : The function to minimise. 133 134 x : A starting point. 135 """ 136 raise NotImplementedError('Abstract method "run" must be ' \ 137 'specialised!')
138
139 140 -class IterativeAlgorithm(object):
141 """Algorithms that require iterative steps to achieve the goal. 142 143 Fields 144 ------ 145 max_iter : Non-negative integer. The maximum number of allowed iterations. 146 147 min_iter : Non-negative integer less than or equal to max_iter. The minimum 148 number of iterations that must be performed. Default is 1. 149 150 num_iter : Non-negative integer greater than or equal to min_iter. The 151 number of iterations performed by the iterative algorithm. All 152 algorithms that inherit from IterativeAlgortihm MUST call 153 iter_reset before every run. 154 155 Parameters 156 ---------- 157 max_iter : Non-negative integer. The maximum number of allowed iterations. 158 159 min_iter : Non-negative integer. The minimum number of required iterations. 160 """
161 - def __init__(self, max_iter=consts.MAX_ITER, min_iter=1, **kwargs):
162 super(IterativeAlgorithm, self).__init__(**kwargs) 163 164 self.max_iter = max_iter 165 self.min_iter = min_iter 166 self.num_iter = 0 167 168 self.iter_reset()
169
170 - def iter_reset(self):
171 172 self.num_iter = 0
173
174 175 -class InformationAlgorithm(object):
176 """Algorithms that produce information about their run. 177 178 Implementing classes should update the INFO_PROVIDED class variable with 179 the information provided by the algorithm. Defauls to an empty list. 180 181 ALL algorithms that inherit from InformationAlgorithm MUST add force_reset 182 as a decorator to the run method. 183 184 Fields 185 ------ 186 info_ret : Dictionary. The algorithm outputs are collected in this 187 dictionary. 188 189 info : List of utils.Info. The identifiers for the requested information 190 outputs. The algorithms will store the requested outputs in 191 self.info. 192 193 INFO_PROVIDED : List of utils.Info. The allowed output identifiers. The 194 implementing class should update this list with the 195 provided/allowed outputs. 196 197 Examples 198 -------- 199 >>> import parsimony.algorithms as algorithms 200 >>> from parsimony.algorithms.utils import Info 201 >>> from parsimony.functions.losses import LinearRegression 202 >>> import numpy as np 203 >>> np.random.seed(42) 204 >>> gd = algorithms.gradient.GradientDescent(info=[Info.fvalue]) 205 >>> gd.info_copy() 206 ['fvalue'] 207 >>> lr = LinearRegression(X=np.random.rand(10,15), y=np.random.rand(10,1)) 208 >>> beta = gd.run(lr, np.random.rand(15, 1)) 209 >>> fvalue = gd.info_get(Info.fvalue) 210 >>> round(fvalue[0], 10) 211 0.068510926 212 >>> round(fvalue[-1], 15) 213 1.886e-12 214 """ 215 INFO_PROVIDED = [] 216
217 - def __init__(self, info=[], **kwargs):
218 """ 219 Parameters 220 ---------- 221 info : List or tuple of utils.Info. The identifiers for the run 222 information to return. 223 """ 224 super(InformationAlgorithm, self).__init__(**kwargs) 225 226 if not isinstance(info, (list, tuple)): 227 self.info = [info] 228 else: 229 self.info = list(info) 230 self.info_ret = dict() 231 232 self.check_info_compatibility(self.info)
233
234 - def info_get(self, nfo=None):
235 """Returns the computed information about the algorithm run. 236 237 Parameters 238 ---------- 239 nfo : utils.Info. The identifier to return information about. If nfo is 240 None, all information is returned in a dictionary. 241 """ 242 if nfo is None: 243 return self.info_ret 244 else: 245 return self.info_ret[nfo]
246
247 - def info_set(self, nfo, value):
248 """Sets the computed information about the algorithm run identified by 249 nfo. 250 251 Parameters 252 ---------- 253 nfo : utils.Info. The identifier to for the computed information about. 254 255 value : object. The value to associate with nfo. 256 """ 257 self.info_ret[nfo] = value
258
259 - def info_provided(self, nfo):
260 """Returns true if the current algorithm provides the given 261 information, and False otherwise. 262 """ 263 return nfo in self.INFO_PROVIDED
264
265 - def info_requested(self, nfo):
266 """Returns true if the the given information was requested, and False 267 otherwise. 268 """ 269 return nfo in self.info
270
271 - def info_reset(self):
272 """Resets the information saved in the previous run. The info_ret 273 field, a dictionary, is cleared. 274 """ 275 self.info_ret.clear()
276
277 - def info_copy(self):
278 """Returns a shallow copy of the requested information. 279 """ 280 return list(self.info)
281
282 - def check_info_compatibility(self, info):
283 """Check if the requested information is provided. 284 285 Parameters 286 ---------- 287 info : A list of utils.Info. The identifiers for information that 288 should be computed. 289 """ 290 for i in info: 291 if not self.info_provided(i): 292 raise ValueError("Requested information not provided.")
293 294 if __name__ == "__main__": 295 import doctest 296 doctest.testmod() 297