def change_bounds(self, nominal_bounds_template): """ Change the bounding box for all of the ConnectionFields in this Projection. Calls change_bounds() on each ConnectionField. Currently only allows reducing the size, but should be extended to allow increasing as well. """ slice_template = Slice(copy(nominal_bounds_template), self.src, force_odd=True, min_matrix_radius=self.min_matrix_radius) bounds_template = slice_template.compute_bounds(self.src) if not self.bounds_template.containsbb_exclusive(bounds_template): if self.bounds_template.containsbb_inclusive(bounds_template): self.debug('Initial and final bounds are the same.') else: self.warning( 'Unable to change_bounds; currently allows reducing only.') return # it's ok so we can store the bounds and resize the weights mask_template = _create_mask(self.cf_shape, bounds_template, self.src, self.autosize_mask, self.mask_threshold) self.mask_template = mask_template self.n_units = self._calc_n_units() self.nominal_bounds_template = nominal_bounds_template self.bounds_template = bounds_template self._slice_template = slice_template cfs = self.cfs rows, cols = cfs.shape output_fns = [wof.single_cf_fn for wof in self.weights_output_fns] for r in xrange(rows): for c in xrange(cols): xcf, ycf = self.X_cf[0, c], self.Y_cf[r, 0] # CB: listhack - loop is candidate for replacement by numpy fn self._change_cf_bounds( cfs[r, c], input_sheet=self.src, x=xcf, y=ycf, template=slice_template, mask=mask_template, output_fns=output_fns, min_matrix_radius=self.min_matrix_radius)
def __init__(self, initialize_cfs=True, **params): """ Initialize the Projection with a set of cf_type objects (typically ConnectionFields), each located at the location in the source sheet corresponding to the unit in the target sheet. The cf_type objects are stored in the 'cfs' array. The nominal_bounds_template specified may be altered: the bounds must be fitted to the Sheet's matrix, and the weights matrix must have odd dimensions. These altered bounds are passed to the individual connection fields. A mask for the weights matrix is constructed. The shape is specified by cf_shape; the size defaults to the size of the nominal_bounds_template. """ super(CFProjection, self).__init__(**params) self.weights_generator.set_dynamic_time_fn(None, sublistattr='generators') # get the actual bounds_template by adjusting a copy of the # nominal_bounds_template to ensure an odd slice, and to be # cropped to sheet if necessary self._slice_template = Slice(copy(self.nominal_bounds_template), self.src, force_odd=True, min_matrix_radius=self.min_matrix_radius) self.bounds_template = self._slice_template.compute_bounds(self.src) self.mask_template = _create_mask(self.cf_shape, self.bounds_template, self.src, self.autosize_mask, self.mask_threshold) self.n_units = self._calc_n_units() if initialize_cfs: self._create_cfs() if self.apply_output_fns_init: self.apply_learn_output_fns(active_units_mask=False) ### JCALERT! We might want to change the default value of the ### input value to self.src.activity; but it fails, raising a ### type error. It probably has to be clarified why this is ### happening self.input_buffer = None self.activity = np.array(self.dest.activity)
def __init__(self, input_sheet, x=0.0, y=0.0, template=BoundingBox(radius=0.1), weights_generator=patterngenerator.Constant(), mask=patterngenerator.Constant(), output_fns=None, min_matrix_radius=1): """ Create weights at the specified (x,y) location on the specified input_sheet. The supplied template (if a BoundingRegion) is converted to a Slice, moved to the specified (x,y) location, and then the weights pattern is drawn inside by the weights_generator. Note that if the appropriate template Slice is already known, then it can be passed in instead of a BoundingRegion template. This slice will then be used directly, instead of converting the template into a Slice. The supplied template object itself will not be modified (it is copied before use). The mask allows the weights to be limited to being non-zero in a subset of the rectangular weights area. The actual mask used is a view of the given mask created by cropping to the boundaries of the input_sheet, so that the weights all correspond to actual locations in the input sheet. For instance, if a circular pattern of weights is desired, the mask should have a disk-shaped pattern of elements with value 1, surrounded by elements with the value 0. If the CF extends over the edge of the input sheet then the weights will actually be half-moon (or similar) rather than circular. """ #print "Create CF",input_sheet.name,x,y,"template=",template,"wg=",weights_generator,"m=",mask,"ofs=",output_fns,"min r=",min_matrix_radius template = copy(template) if not isinstance(template, Slice): template = Slice(template, input_sheet, force_odd=True, min_matrix_radius=min_matrix_radius) # Note: if passed in, mask is shared between CFs (but not if created here) if not hasattr(mask, 'view'): mask = _create_mask( mask, template.compute_bounds(input_sheet), # CEBALERT: it's not really worth adding more ALERTs on this # topic, but...there's no way for the CF to control autosize # and threshold. input_sheet, True, 0.5) # CB: has to be set for C code. Can't be initialized at the # class level, or it would become a read-only class attribute # (because it's a slot: # http://docs.python.org/reference/datamodel.html). Can we # somehow avoid having to think about _has_norm_total in the # python code? Could the C code initialize this value? self._has_norm_total = array([0], dtype=numpy.int32) self._norm_total = array([0.0], dtype=float) if output_fns is None: output_fns = [] # CEBALERT: now even more confusing; weights_slice is # different from input_sheet_slice. At least need to rename. weights_slice = self._create_input_sheet_slice(input_sheet, x, y, template, min_matrix_radius) # CBNOTE: this would be clearer (but not perfect, and probably slower) # m = mask_template[self.weights_slice()] self.mask = weights_slice.submatrix(mask) # view of original mask self.mask = array(self.mask, copy=1) # CEBALERT: why is this necessary? # (without it, optimized learning function creates artifacts in CFs at # left and right edges of sheet, at some densities) # CBENHANCEMENT: might want to do something about a size # that's specified (right now the size is assumed to be that # of the bounds) # shouldn't be extra computation of boundingbox because it's gone from Slice.__init__; could avoid extra lookups by getting straight from slice w = weights_generator(x=x, y=y, bounds=self.get_bounds(input_sheet), xdensity=input_sheet.xdensity, ydensity=input_sheet.ydensity, mask=self.mask) # CEBALERT: unnecessary copy! Pass type to PG & have it draw # in that. (Should be simple, except making it work for all # the PG subclasses that override array creation in various # ways (producing or using inconsistent types) turned out to # be too painful.) self.weights = w.astype(weight_type) # CEBHACKALERT: the system of masking through multiplication # by 0 works for now, while the output_fns are all # multiplicative. But in the long run we need a better way to # apply the mask. The same applies anywhere the mask is used, # including in learningfn/. We should investigate masked # arrays (from numpy). for of in output_fns: of(self.weights)