Package topo :: Package projection :: Module basic
[hide private]
[frames] | no frames]

Source Code for Module topo.projection.basic

  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  # So all Projections are present in this package 
 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   
30 -class CFPOF_SharedWeight(CFPOutputFn):
31 """ 32 CFPOutputFn for use with SharedWeightCFProjections. 33 34 Applies the single_cf_fn to the single shared CF's weights. 35 """ 36 single_cf_fn = param.ClassSelector(TransferFn,default=IdentityTF()) 37 38 # CEBALERT: remove norm_values?
39 - def __call__(self, cfs, norm_values=None, **params):
40 """Apply the specified single_cf_fn to every CF.""" 41 if type(self.single_cf_fn) is not IdentityTF: 42 cf = cfs[0,0] 43 self.single_cf_fn(cf.weights)
44 45 from topo.base.cf import _create_mask 46
47 -class SharedWeightCF(ConnectionField):
48 49 __slots__ = [] 50
51 - def __init__(self,cf,input_sheet,x=0.0,y=0.0,template=BoundingBox(radius=0.1), 52 mask=patterngenerator.Constant(), 53 min_matrix_radius=1):
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 # CEBALERT: There's no call to super's __init__; see JAHACKALERT 66 # below. 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 # Note: if passed in, mask is shared between CFs (but not if created here) 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 # JAHACKALERT the TransferFn cannot be applied in SharedWeightCF 87 # - another inconsistency in the class tree design - there 88 # should be nothing in the parent class that is ignored in its 89 # children. Probably need to extract some functionality of 90 # ConnectionField into a shared abstract parent class. 91 # We have agreed to make this right by adding a constant property that 92 # will be set true if the learning should be active 93 # The SharedWeightCFProjection class and its anccestors will 94 # have this property set to false which means that the 95 # learning will be deactivated 96 97 98
99 -class SharedWeightCFProjection(CFProjection):
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 ### JABHACKALERT: Set to be constant as a clue that learning won't 107 ### actually work yet, but we could certainly extend it to support 108 ### learning if desired, e.g. to learn position-independent responses. 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
113 - def __init__(self,**params):
114 """ 115 Initialize the Projection with a single cf_type object 116 (typically a ConnectionField), 117 """ 118 # We don't want the whole set of cfs initialized, but we 119 # do want anything that CFProjection defines. 120 super(SharedWeightCFProjection,self).__init__(initialize_cfs=False,**params) 121 122 # We want the sharedcf to be located on the grid, so use the 123 # center of a unit 124 sheet_rows,sheet_cols=self.src.shape 125 # arbitrary (e.g. could use 0,0) 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
144 - def _create_cf(self,x,y):
145 x_cf,y_cf = self.coord_mapper(x,y) 146 # Does not pass the mask, as it would have to be sliced 147 # for each cf, and is only used for learning. 148 CF = SharedWeightCF(self.__sharedcf,self.src,x=x_cf,y=y_cf, #JUDE ADDED 149 template=self._slice_template, 150 min_matrix_radius=self.min_matrix_radius, 151 mask=self.mask_template) 152 153 return CF
154 155
156 - def learn(self):
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
165 - def apply_learn_output_fns(self,active_units_mask=True):
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
174 - def n_bytes(self):
175 return self.activity.nbytes + self.__sharedcf.weights.nbytes + \ 176 sum([cf.input_sheet_slice.nbytes 177 for cf,i in CFIter(self)()])
178 179 180 181 182 183 # JABALERT: Can this be replaced with a CFProjection with a Hysteresis output_fn? 184 # If not it should probably be replaced with a new output_fn type instead.
185 -class LeakyCFProjection(ResizableCFProjection):
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
197 - def __init__(self,**params):
198 super(LeakyCFProjection,self).__init__(**params) 199 self.leaky_input_buffer = zeros(self.src.activity.shape)
200
201 - def activate(self,input_activity):
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
210 - def n_bytes(self):
211 return super(LeakyCFProjection,self).n_bytes() + \ 212 self.activity.nbytes*1 # for leaky_input_buffer
213 214
215 -class ScaledCFProjection(CFProjection):
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
236 - def __init__(self,**params):
237 # JABALERT: Private variables should be prefixed __ (truly private) or _ (nominally private) 238 # Are all of these really something you expect people to be using outside this class? 239 super(ScaledCFProjection,self).__init__(**params) 240 self.x_avg=None 241 self.sf=None 242 self.lr_sf=None 243 self.scaled_x_avg=None
244 245
246 - def calculate_sf(self):
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
263 - def do_scaling(self):
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
274 - def activate(self,input_activity):
275 """Activate using the specified response_fn and output_fn.""" 276 self.input_buffer = input_activity 277 self.activity *=0.0 278 279 if self.x_avg is None: 280 self.x_avg=self.target*ones(self.dest.shape, activity_type) 281 if self.scaled_x_avg is None: 282 self.scaled_x_avg=self.target*ones(self.dest.shape, activity_type) 283 if self.sf is None: 284 self.sf=ones(self.dest.shape, activity_type) 285 if self.lr_sf is None: 286 self.lr_sf=ones(self.dest.shape, activity_type) 287 288 self.response_fn(MaskedCFIter(self), input_activity, self.activity, self.strength) 289 for of in self.output_fns: 290 of(self.activity) 291 self.calculate_sf() 292 self.do_scaling()
293 294
295 - def n_bytes(self):
296 return super(ScaledCFProjection,self).n_bytes() + \ 297 self.activity.nbytes*4 # for x_avg,sf,lr_sf,scaled_x_avg
298 299 300
301 -class OneToOneProjection(Projection):
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
324 - def __init__(self,**kw):
325 super(OneToOneProjection,self).__init__(**kw) 326 327 self.input_buffer = None 328 329 dx,dy = self.dest.bounds.centroid() 330 331 # JPALERT: Not sure if this is the right way to generate weights. 332 # For full specificity, each initial weight should be dependent on the 333 # coordinates of both the src unit and the dest unit. 334 self.weights = self.weights_generator(bounds=self.dest.bounds, 335 xdensity=self.dest.xdensity, 336 ydensity=self.dest.ydensity) 337 338 339 # JPALERT: CoordMapperFns should really be made to take 340 # matrices of x/y points and apply their mapping to all. This 341 # could give great speedups, esp for AffineTransform mappings, 342 # which can be applied to many points with a single matrix 343 # multiplication. 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 # dest_idxs contains the indices of the dest units whose weights project 353 # in bounds on the src sheet. 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 # The [0] is required because numpy.nonzero returns the 361 # nonzero indices wrapped in a one-tuple. 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
368 - def activate(self,input):
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
375 - def learn(self):
376 if self.input_buffer is not None: 377 self.learning_fn(self.input_buffer, 378 self.dest.activity, 379 self.weights, 380 self.learning_rate)
381
382 - def n_conns(self):
383 rows,cols=self.activity.shape 384 return rows*cols
385
386 - def n_bytes(self):
387 return super(OneToOneProjection,self).n_bytes() + \ 388 self.activity.nbytes
389 390 391 __all__ = list(set([k for k,v in locals().items() 392 if isinstance(v,type) and issubclass(v,Projection)])) 393