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
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
54
55
56
57
58
59
60
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
124 provide_unoptimized_equivalent("CFPLF_Hebbian_opt","CFPLF_Hebbian",locals())
125
126
127
128
129
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
241 provide_unoptimized_equivalent("CFPLF_BCMFixed_opt","CFPLF_Hebbian",locals())
242
243
244
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
337 provide_unoptimized_equivalent("CFPLF_Scaled_opt","CFPLF_Scaled",locals())
338
339
340
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
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