Package topo :: Package learningfn :: Module optimized
[hide private]
[frames] | no frames]

Source Code for Module topo.learningfn.optimized

  1  """ 
  2  Learning functions (see basic.py) and projection-level learning 
  3  functions (see projfn.py) written in C to optimize performance. 
  4   
  5  Requires the weave package; without it unoptimized versions are used. 
  6   
  7  $Id: optimized.py 10867 2010-01-31 18:55:35Z ceball $ 
  8  """ 
  9  __version__ = "$Revision: 10867 $" 
 10   
 11  from numpy import zeros, ones 
 12   
 13  import param 
 14   
 15  from topo.base.sheet import activity_type 
 16  from topo.base.cf import CFPLearningFn,CFPLF_Plugin 
 17  from topo.learningfn.projfn import CFPLF_PluginScaled 
 18  from topo.base.functionfamily import Hebbian,LearningFn 
 19  from topo.misc.inlinec import inline,provide_unoptimized_equivalent,c_header 
 20  from topo.learningfn.basic import BCMFixed 
 21   
 22  from projfn import CFPLF_Trace 
 23   
 24   
 25   
 26   
27 -class CFPLF_Hebbian_opt(CFPLearningFn):
28 """ 29 CF-aware Hebbian learning rule. 30 31 Implemented in C for speed. Should be equivalent to 32 CFPLF_Plugin(single_cf_fn=Hebbian), except faster. 33 34 As a side effect, sets the norm_total attribute on any cf whose 35 weights are updated during learning, to speed up later operations 36 that might depend on it. 37 38 May return without modifying anything if the learning rate turns 39 out to be zero. 40 """ 41 single_cf_fn = param.ClassSelector(LearningFn,default=Hebbian(),readonly=True) 42
43 - def __call__(self, iterator, input_activity, output_activity, learning_rate, **params):
44 single_connection_learning_rate = self.constant_sum_connection_rate(iterator.proj_n_units,learning_rate) 45 if single_connection_learning_rate==0: 46 return 47 48 cfs = iterator.flatcfs 49 num_cfs = len(cfs) 50 irows,icols = input_activity.shape 51 cf_type = iterator.cf_type 52 53 # CEBALERT: this function *always* skips inactive units, 54 # because it uses the output_activity directly rather than 55 # going through the iterator. That's ok since we know this 56 # function can always skip inactive units. But the unoptimized 57 # equivalent should be made to do the same, because right now 58 # it respects the iterator. (Just a case of setting the 59 # iterator's active_units_mask to be True before calling the 60 # iterator in the unoptimized version.) 61 62 sheet_mask = iterator.get_sheet_mask() 63 64 code = c_header + """ 65 DECLARE_SLOT_OFFSET(weights,cf_type); 66 DECLARE_SLOT_OFFSET(input_sheet_slice,cf_type); 67 DECLARE_SLOT_OFFSET(mask,cf_type); 68 69 npfloat *x = output_activity; 70 npfloat *m = sheet_mask; 71 72 for (int r=0; r<num_cfs; ++r) { 73 double load = *x++; 74 double msk = *m++; 75 if (load != 0 && msk != 0) { 76 load *= single_connection_learning_rate; 77 78 PyObject *cf = PyList_GetItem(cfs,r); 79 80 LOOKUP_FROM_SLOT_OFFSET(float,weights,cf); 81 LOOKUP_FROM_SLOT_OFFSET(int,input_sheet_slice,cf); 82 LOOKUP_FROM_SLOT_OFFSET(float,mask,cf); 83 84 UNPACK_FOUR_TUPLE(int,rr1,rr2,cc1,cc2,input_sheet_slice); 85 86 double total = 0.0; 87 88 // modify non-masked weights 89 npfloat *inpj = input_activity+icols*rr1+cc1; 90 for (int i=rr1; i<rr2; ++i) { 91 npfloat *inpi = inpj; 92 for (int j=cc1; j<cc2; ++j) { 93 // The mask is floating point, so we have to 94 // use a robust comparison instead of testing 95 // against exactly 0.0. 96 if (*(mask++) >= 0.000001) { 97 *weights += load * *inpi; 98 total += fabs(*weights); 99 } 100 ++weights; 101 ++inpi; 102 } 103 inpj += icols; 104 } 105 106 // store the sum of the cf's weights 107 PyObject *total_obj = PyFloat_FromDouble(total); //(new ref) 108 PyObject_SetAttrString(cf,"_norm_total",total_obj); 109 PyObject_SetAttrString(cf,"_has_norm_total",Py_True); 110 Py_DECREF(total_obj); 111 } 112 } 113 """ 114 115 inline(code, ['input_activity', 'output_activity','sheet_mask','num_cfs', 116 'icols', 'cfs', 'single_connection_learning_rate','cf_type'], 117 local_dict=locals(), 118 headers=['<structmember.h>'])
119 120
121 -class CFPLF_Hebbian(CFPLF_Plugin):
122 """Same as CFPLF_Plugin(single_cf_fn=Hebbian()); just for non-optimized fallback.""" 123 single_cf_fn = param.ClassSelector(LearningFn,default=Hebbian(),readonly=True)
124 provide_unoptimized_equivalent("CFPLF_Hebbian_opt","CFPLF_Hebbian",locals()) 125 126 127 # CBERRORALERT: classes from here on probably ignore the sheet mask 128 129 # JABALERT: Is this really a fixed-threshold BCM rule? If so, is that really useful?
130 -class CFPLF_BCMFixed_opt(CFPLearningFn):
131 """ 132 CF-aware BCM learning rule. 133 134 Implemented in C for speed. Should be equivalent to 135 BCMFixed for CF sheets, except faster. 136 137 As a side effect, sets the norm_total attribute on any cf whose 138 weights are updated during learning, to speed up later operations 139 that might depend on it. 140 141 May return without modifying anything if the learning rate turns 142 out to be zero. 143 """ 144 145 unit_threshold=param.Number(default=0.5,bounds=(0,None),doc="Threshold between LTD and LTP.") 146
147 - def __call__(self, iterator, input_activity, output_activity, learning_rate, **params):
148 rows,cols = output_activity.shape 149 cfs = iterator.flatcfs 150 num_cfs = len(cfs) 151 single_connection_learning_rate = self.constant_sum_connection_rate(iterator.proj_n_units,learning_rate) 152 if single_connection_learning_rate==0: 153 return 154 155 unit_threshold=self.unit_threshold 156 157 irows,icols = input_activity.shape 158 cf_type = iterator.cf_type 159 code = c_header + """ 160 // CEBALERT: should provide a macro for getting offset 161 162 ///// GET WEIGHTS OFFSET 163 PyMemberDescrObject *weights_descr = (PyMemberDescrObject *)PyObject_GetAttrString(cf_type,"weights"); 164 Py_ssize_t weights_offset = weights_descr->d_member->offset; 165 Py_DECREF(weights_descr); 166 167 ///// GET SLICE OFFSET 168 PyMemberDescrObject *slice_descr = (PyMemberDescrObject *)PyObject_GetAttrString(cf_type,"input_sheet_slice"); 169 Py_ssize_t slice_offset = slice_descr->d_member->offset; 170 Py_DECREF(slice_descr); 171 172 ///// GET MASK OFFSET 173 PyMemberDescrObject *mask_descr = (PyMemberDescrObject *)PyObject_GetAttrString(cf_type,"mask"); 174 Py_ssize_t mask_offset = mask_descr->d_member->offset; 175 Py_DECREF(mask_descr); 176 177 178 npfloat *x = output_activity; 179 180 for (int r=0; r<num_cfs; ++r) { 181 double load = *x++; 182 double unit_activity= load; 183 if (load != 0) { 184 load *= single_connection_learning_rate; 185 186 PyObject *cf = PyList_GetItem(cfs,r); 187 188 PyArrayObject *weights_obj = *((PyArrayObject **)((char *)cf + weights_offset)); 189 PyArrayObject *slice_obj = *((PyArrayObject **)((char *)cf + slice_offset)); 190 PyArrayObject *mask_obj = *((PyArrayObject **)((char *)cf + mask_offset)); 191 192 float *wi = (float *)(weights_obj->data); 193 int *slice = (int *)(slice_obj->data); 194 float *m = (float *)(mask_obj->data); 195 196 int rr1 = *slice++; 197 int rr2 = *slice++; 198 int cc1 = *slice++; 199 int cc2 = *slice; 200 201 double total = 0.0; 202 203 // modify non-masked weights 204 npfloat *inpj = input_activity+icols*rr1+cc1; 205 for (int i=rr1; i<rr2; ++i) { 206 npfloat *inpi = inpj; 207 for (int j=cc1; j<cc2; ++j) { 208 // The mask is floating point, so we have to 209 // use a robust comparison instead of testing 210 // against exactly 0.0. 211 if (*(m++) >= 0.000001) { 212 *wi += load * *inpi * (unit_activity - unit_threshold); 213 if (*wi<0) { *wi = 0;} 214 total += fabs(*wi); 215 } 216 ++wi; 217 ++inpi; 218 } 219 inpj += icols; 220 } 221 222 // store the sum of the cf's weights 223 PyObject *total_obj = PyFloat_FromDouble(total); //(new ref) 224 PyObject_SetAttrString(cf,"_norm_total",total_obj); 225 PyObject_SetAttrString(cf,"_has_norm_total",Py_True); 226 Py_DECREF(total_obj); 227 } 228 } 229 """ 230 231 inline(code, ['input_activity', 'output_activity','num_cfs', 232 'icols', 'cfs', 'single_connection_learning_rate', 233 'unit_threshold','cf_type'], 234 local_dict=locals(), 235 headers=['<structmember.h>'])
236 237
238 -class CFPLF_BCMFixed(CFPLF_Plugin):
239 """Same as CFPLF_Plugin(single_cf_fn=BCMFixed()); just for non-optimized fallback.""" 240 single_cf_fn = param.ClassSelector(LearningFn,default=BCMFixed(),readonly=True)
241 provide_unoptimized_equivalent("CFPLF_BCMFixed_opt","CFPLF_Hebbian",locals()) 242 243 244 # CEBALERT: 2009/04/03 - when used in GCA-LISSOM, causes Python to crash.
245 -class CFPLF_Scaled_opt(CFPLF_PluginScaled):
246 """ 247 CF-aware Scaled Hebbian learning rule. 248 249 Implemented in C for speed. Should be equivalent to 250 CFPLF_PluginScaled(single_cf_fn=Hebbian), except faster. 251 252 As a side effect, sets the norm_total attribute on any cf whose 253 weights are updated during learning, to speed up later operations 254 that might depend on it. 255 """ 256 single_cf_fn = param.ClassSelector(LearningFn,default=Hebbian(),readonly=True) 257
258 - def __call__(self, iterator, input_activity, output_activity, learning_rate, **params):
259 260 if self.learning_rate_scaling_factor is None: 261 self.learning_rate_scaling_factor = ones(output_activity.shape)*1.0 262 263 learning_rate_scaling_factor = self.learning_rate_scaling_factor 264 265 cfs = iterator.flatcfs 266 num_cfs = len(cfs) 267 single_connection_learning_rate = self.constant_sum_connection_rate(iterator.proj_n_units,learning_rate) 268 if single_connection_learning_rate==0: 269 return 270 271 irows,icols = input_activity.shape 272 code = c_header + """ 273 npfloat *x = output_activity; 274 double *sclr = learning_rate_scaling_factor; 275 276 for (int r=0; r<num_cfs; ++r) { 277 double load = *x++; 278 double a= *sclr++; 279 load = load * a; 280 if (load != 0) { 281 load *= single_connection_learning_rate; 282 PyObject *cf = PyList_GetItem(cfs,r); 283 PyObject *weights_obj = PyObject_GetAttrString(cf,"weights"); 284 PyObject *slice_obj = PyObject_GetAttrString(cf,"input_sheet_slice"); 285 PyObject *mask_obj = PyObject_GetAttrString(cf,"mask"); 286 287 float *wi = (float *)(((PyArrayObject*)weights_obj)->data); 288 int *slice = (int *)(((PyArrayObject*)slice_obj)->data); 289 float *m = (float *)(((PyArrayObject*)mask_obj)->data); 290 291 int rr1 = *slice++; 292 int rr2 = *slice++; 293 int cc1 = *slice++; 294 int cc2 = *slice; 295 296 double total = 0.0; 297 298 // modify non-masked weights 299 npfloat *inpj = input_activity+icols*rr1+cc1; 300 for (int i=rr1; i<rr2; ++i) { 301 npfloat *inpi = inpj; 302 for (int j=cc1; j<cc2; ++j) { 303 // The mask is floating point, so we have to 304 // use a robust comparison instead of testing 305 // against exactly 0.0. 306 if (*(m++) >= 0.000001) { 307 *wi += load * *inpi; 308 total += fabs(*wi); 309 } 310 ++wi; 311 ++inpi; 312 } 313 inpj += icols; 314 } 315 316 // Anything obtained with PyObject_GetAttrString must be explicitly freed 317 Py_DECREF(weights_obj); 318 Py_DECREF(slice_obj); 319 Py_DECREF(mask_obj); 320 321 // store the sum of the cf's weights 322 PyObject *total_obj = PyFloat_FromDouble(total); //(new ref) 323 PyObject_SetAttrString(cf,"_norm_total",total_obj); 324 PyObject_SetAttrString(cf,"_has_norm_total",Py_True); 325 Py_DECREF(total_obj); 326 } 327 } 328 329 """ 330 331 inline(code, ['input_activity','learning_rate_scaling_factor', 'output_activity','num_cfs', 'icols', 'cfs', 'single_connection_learning_rate'], local_dict=locals())
332 333
334 -class CFPLF_Scaled(CFPLF_PluginScaled):
335 """Same as CFPLF_PluginScaled(single_cf_fn=Hebbian()); just for non-optimized fallback.""" 336 single_cf_fn = param.ClassSelector(LearningFn,default=Hebbian(),readonly=True)
337 provide_unoptimized_equivalent("CFPLF_Scaled_opt","CFPLF_Scaled",locals()) 338 339 340
341 -class CFPLF_Trace_opt(CFPLearningFn):
342 """ 343 Optimized version of CFPLF_Trace; see projfn.py for more info 344 """ 345 346 trace_strength=param.Number(default=0.5,bounds=(0.0,1.0),doc=""" 347 How much the learning is dominated by the activity trace, relative to the current value.""") 348 349 single_cf_fn = param.ClassSelector(LearningFn,default=Hebbian(),readonly=True, 350 doc="LearningFn that will be applied to each CF individually.") 351
352 - def __call__(self, iterator, input_activity, output_activity, learning_rate, **params):
353 cfs = iterator.flatcfs 354 single_connection_learning_rate = self.constant_sum_connection_rate(iterator.proj_n_units,learning_rate) 355 irows,icols = input_activity.shape 356 357 if single_connection_learning_rate==0: 358 return 359 360 ##Initialise traces to zero if they don't already exist 361 if not hasattr(self,'traces'): 362 self.traces=zeros(output_activity.shape,activity_type) 363 364 self.traces = (self.trace_strength*output_activity)+((1-self.trace_strength)*self.traces) 365 traces = self.traces 366 367 code = c_header + """ 368 npfloat *x = traces; 369 370 for (int r=0; r<num_cfs; ++r) { 371 double load = *x++; 372 if (load != 0) { 373 load *= single_connection_learning_rate; 374 PyObject *cf = PyList_GetItem(cfs,r); 375 PyObject *weights_obj = PyObject_GetAttrString(cf,"weights"); 376 PyObject *slice_obj = PyObject_GetAttrString(cf,"input_sheet_slice"); 377 PyObject *mask_obj = PyObject_GetAttrString(cf,"mask"); 378 379 float *wi = (float *)(((PyArrayObject*)weights_obj)->data); 380 int *slice = (int *)(((PyArrayObject*)slice_obj)->data); 381 float *m = (float *)(((PyArrayObject*)mask_obj)->data); 382 383 int rr1 = *slice++; 384 int rr2 = *slice++; 385 int cc1 = *slice++; 386 int cc2 = *slice; 387 388 double total = 0.0; 389 390 // modify non-masked weights 391 npfloat *inpj = input_activity+icols*rr1+cc1; 392 for (int i=rr1; i<rr2; ++i) { 393 npfloat *inpi = inpj; 394 for (int j=cc1; j<cc2; ++j) { 395 // The mask is floating point, so we have to 396 // use a robust comparison instead of testing 397 // against exactly 0.0. 398 if (*(m++) >= 0.000001) { 399 *wi += load * *inpi; 400 total += fabs(*wi); 401 } 402 ++wi; 403 ++inpi; 404 } 405 inpj += icols; 406 } 407 408 // Anything obtained with PyObject_GetAttrString must be explicitly freed 409 Py_DECREF(weights_obj); 410 Py_DECREF(slice_obj); 411 Py_DECREF(mask_obj); 412 413 // store the sum of the cf's weights 414 PyObject *total_obj = PyFloat_FromDouble(total); //(new ref) 415 PyObject_SetAttrString(cf,"norm_total",total_obj); 416 PyObject_SetAttrString(cf,"_has_norm_total",Py_True); 417 Py_DECREF(total_obj); 418 } 419 } 420 """ 421 422 inline(code, ['input_activity', 'traces','num_cfs', 'icols', 'cfs', 'single_connection_learning_rate'], local_dict=locals())
423 424 425 provide_unoptimized_equivalent("CFPLF_Trace_opt","CFPLF_Trace",locals()) 426