1 """
2 Repository for the basic Projection types, without any special
3 dependencies or requirements.
4
5 $Id: basic.py 10873 2010-01-31 19:04:08Z ceball $
6 """
7
8 __version__ = "$Revision: 10873 $"
9
10 from copy import copy
11
12 from numpy import exp,ones,zeros,array,nonzero
13
14 import param
15
16
17 from topo.base.projection import Projection
18 from topo.base.boundingregion import BoundingBox
19 from topo.base.sheet import activity_type
20 from topo.base.sheetcoords import Slice
21 from topo.base.cf import CFProjection,ConnectionField,MaskedCFIter,\
22 CFPLearningFn,CFPLF_Identity,CFPOutputFn,CFIter,ResizableCFProjection
23 from topo.base.patterngenerator import PatternGenerator,Constant
24 from topo.base.functionfamily import CoordinateMapperFn,IdentityMF
25 from topo.misc.util import rowcol2idx
26 from topo.transferfn.basic import TransferFn,IdentityTF
27 from topo.learningfn.basic import LearningFn,IdentityLF
28 from topo.base import patterngenerator
29
44
45 from topo.base.cf import _create_mask
46
48
49 __slots__ = []
50
54 """
55 From an existing copy of ConnectionField (CF) that acts as a
56 template, create a new CF that shares weights with the
57 template CF. Copies all the properties of CF to stay
58 identical except the weights variable that actually contains
59 the data.
60
61 The only difference from a normal CF is that the weights of
62 the CF are implemented as a numpy view into the single master
63 copy of the weights stored in the CF template.
64 """
65
66
67 template = copy(template)
68
69 if not isinstance(template,Slice):
70 template = Slice(template,input_sheet,force_odd=True,
71 min_matrix_radius=min_matrix_radius)
72
73
74 if not hasattr(mask,'view'):
75 mask = _create_mask(patterngenerator.Constant(),
76 template.compute_bounds(input_sheet),
77 input_sheet,True,0.5)
78
79
80
81 self._has_norm_total=False
82 self.mask=mask
83 weights_slice = self._create_input_sheet_slice(input_sheet,x,y,template,min_matrix_radius=min_matrix_radius)
84 self.weights = weights_slice.submatrix(cf.weights)
85
86
87
88
89
90
91
92
93
94
95
96
97
98
100 """
101 A Projection with a single set of weights, shared by all units.
102
103 Otherwise similar to CFProjection, except that learning is
104 currently disabled.
105 """
106
107
108
109 learning_fn = param.ClassSelector(CFPLearningFn,CFPLF_Identity(),constant=True)
110 weights_output_fns = param.HookList(default=[CFPOF_SharedWeight()])
111 precedence = param.Number(default=0.5)
112
114 """
115 Initialize the Projection with a single cf_type object
116 (typically a ConnectionField),
117 """
118
119
120 super(SharedWeightCFProjection,self).__init__(initialize_cfs=False,**params)
121
122
123
124 sheet_rows,sheet_cols=self.src.shape
125
126 center_row,center_col = sheet_rows/2,sheet_cols/2
127 center_unitxcenter,center_unitycenter=self.src.matrixidx2sheet(center_row,
128 center_col)
129
130
131 self.__sharedcf=self.cf_type(self.src,
132 x=center_unitxcenter,
133 y=center_unitycenter,
134 template=self._slice_template,
135 weights_generator=self.weights_generator,
136 mask=self.mask_template,
137 output_fns=[wof.single_cf_fn for wof in self.weights_output_fns],
138 min_matrix_radius=self.min_matrix_radius)
139
140 self._create_cfs()
141
142
143
154
155
157 """
158 Because of how output functions are applied, it is not currently
159 possible to use learning functions and learning output functions for
160 SharedWeightCFProjections, so we disable them here.
161 """
162 pass
163
164
166 """
167 Because of how output functions are applied, it is not currently
168 possible to use learning functions and learning output functions for
169 SharedWeightCFProjections, so we disable them here.
170 """
171 pass
172
173
178
179
180
181
182
183
184
186 """
187 A projection that has a decay_rate parameter so that incoming
188 input is decayed over time as x(t) = input + x(t-1)*exp(-decay_rate),
189 and then the weighted sum of x(t) is calculated.
190 """
191
192 decay_rate = param.Number(default=1.0,bounds=(0,None),
193 doc="Input decay rate for each leaky synapse")
194
195 precedence = param.Number(default=0.4)
196
200
202 """
203 Retain input_activity from the previous step in leaky_input_buffer
204 and add a leaked version of it to the current input_activity. This
205 function needs to deal with a finer time-scale.
206 """
207 self.leaky_input_buffer = input_activity + self.leaky_input_buffer*exp(-self.decay_rate)
208 super(LeakyCFProjection,self).activate(self.leaky_input_buffer)
209
213
214
216 """
217 Allows scaling of activity based on a specified target average activity.
218
219 An exponentially weighted average is used to calculate the average
220 activity. This average is then used to calculate scaling factors
221 for the current activity and for the learning rate.
222
223 The plastic parameter can be used to turn off updating of the average
224 activity, e.g. during map measurement.
225 """
226
227 target = param.Number(default=0.045, doc="""Target average activity for the projection.""")
228
229 target_lr = param.Number(default=0.045, doc="""
230 Target average activity for scaling the learning rate.""")
231
232 smoothing = param.Number(default=0.999, doc="""
233 Influence of previous activity, relative to current, for computing the average.""")
234 precedence = param.Number(default=0.4)
235
244
245
247 """
248 Calculate current scaling factors based on the target and previous average activities.
249
250 Keeps track of the scaled average for debugging. Could be
251 overridden by a subclass to calculate the factors differently.
252 """
253
254 if self.plastic:
255 self.sf *=0.0
256 self.lr_sf *=0.0
257 self.sf += self.target/self.x_avg
258 self.lr_sf += self.target_lr/self.x_avg
259 self.x_avg = (1.0-self.smoothing)*self.activity + self.smoothing*self.x_avg
260 self.scaled_x_avg = (1.0-self.smoothing)*self.activity*self.sf + self.smoothing*self.scaled_x_avg
261
262
264 """
265 Scale the projection activity and learning rate for the projection.
266 """
267 self.activity *= self.sf
268 if hasattr(self.learning_fn,'learning_rate_scaling_factor'):
269 self.learning_fn.update_scaling_factor(self.lr_sf)
270 else:
271 raise ValueError("Projections to be called must have learning function which supports scaling (e.g. CFPLF_PluginScaled).")
272
273
293
294
298
299
300
302 """
303 A projection that has at most one input connection for each unit.
304
305 This projection has exactly one weight for each destination unit.
306 The input locations on the input sheet are determined by a
307 coordinate mapper. Inputs that map outside the bounds of the
308 input sheet are treated as having zero weight.
309 """
310 coord_mapper = param.ClassSelector(CoordinateMapperFn,
311 default=IdentityMF(),
312 doc='Function to map a destination unit coordinate into the src sheet.')
313
314 weights_generator = param.ClassSelector(PatternGenerator,
315 default=Constant(),constant=True,
316 doc="""Generate initial weight values for each unit of the destination sheet.""")
317
318 learning_fn = param.ClassSelector(LearningFn,default=IdentityLF(),
319 doc="""Learning function applied to weights.""")
320
321 learning_rate = param.Number(default=0)
322
323
325 super(OneToOneProjection,self).__init__(**kw)
326
327 self.input_buffer = None
328
329 dx,dy = self.dest.bounds.centroid()
330
331
332
333
334 self.weights = self.weights_generator(bounds=self.dest.bounds,
335 xdensity=self.dest.xdensity,
336 ydensity=self.dest.ydensity)
337
338
339
340
341
342
343
344 srccoords = [self.coord_mapper(x,y)
345 for y in reversed(self.dest.sheet_rows())
346 for x in self.dest.sheet_cols()]
347
348 self.src_idxs = array([rowcol2idx(r,c,self.src.activity.shape)
349 for r,c in (self.src.sheet2matrixidx(u,v)
350 for u,v in srccoords)])
351
352
353
354 src_rows,src_cols = self.src.activity.shape
355 def in_bounds(x,y):
356 r,c = self.src.sheet2matrixidx(x,y)
357 return (0 <= r < src_rows) and (0 <= c < src_cols)
358 destmask = [in_bounds(x,y) for x,y in srccoords]
359
360
361
362 self.dest_idxs = nonzero(destmask)[0]
363 self.src_idxs = self.src_idxs.take(self.dest_idxs)
364 assert len(self.dest_idxs) == len(self.src_idxs)
365
366 self.activity = zeros(self.dest.shape,dtype=float)
367
369 self.input_buffer = input
370 result = self.weights.take(self.dest_idxs) * input.take(self.src_idxs) * self.strength
371 self.activity.put(self.dest_idxs,result)
372 for of in self.output_fns:
373 of(self.activity)
374
381
385
389
390
391 __all__ = list(set([k for k,v in locals().items()
392 if isinstance(v,type) and issubclass(v,Projection)]))
393