def __call__(self, **params): p = ParamOverrides(self, params) measured_sheets = [ s for s in topo.sim.objects(CFSheet).values() if hasattr(s, 'measure_maps') and s.measure_maps ] results = AttrTree() # Could easily be extended to measure CoG of all projections # and e.g. register them using different names (e.g. "Afferent # XCoG"), but then it's not clear how the PlotGroup would be # able to find them automatically (as it currently supports # only a fixed-named plot). requested_proj = p.proj_name for sheet in measured_sheets: for proj in sheet.in_connections: if (proj.name == requested_proj) or \ (requested_proj == '' and (proj.src != sheet)): cog_data = self._update_proj_cog(p, proj) for key, data in cog_data.items(): name = proj.name[0].upper() + proj.name[1:] results.set_path((key, name), data) if p.measurement_storage_hook: p.measurement_storage_hook(results) return results
def __call__(self, **params): p = ParamOverrides(self, params) measured_sheets = [s for s in topo.sim.objects(CFSheet).values() if hasattr(s,'measure_maps') and s.measure_maps] results = AttrTree() # Could easily be extended to measure CoG of all projections # and e.g. register them using different names (e.g. "Afferent # XCoG"), but then it's not clear how the PlotGroup would be # able to find them automatically (as it currently supports # only a fixed-named plot). requested_proj=p.proj_name for sheet in measured_sheets: for proj in sheet.in_connections: if (proj.name == requested_proj) or \ (requested_proj == '' and (proj.src != sheet)): cog_data = self._update_proj_cog(p, proj) for key, data in cog_data.items(): name = proj.name[0].upper() + proj.name[1:] results.set_path((key, name), data) if p.measurement_storage_hook: p.measurement_storage_hook(results) return results
def _filter_attrtree(self, attrtree, path_filters): """ Filters the loaded AttrTree using the supplied path_filters. """ if not path_filters: return attrtree # Convert string path filters path_filters = [tuple(pf.split('.')) if not isinstance(pf, tuple) else pf for pf in path_filters] # Search for substring matches between paths and path filters new_attrtree = AttrTree() for path, item in attrtree.path_items.items(): if any([all([subpath in path for subpath in pf]) for pf in path_filters]): new_attrtree.set_path(path, item) return new_attrtree
def _filter_attrtree(self, attrtree, path_filters): """ Filters the loaded AttrTree using the supplied path_filters. """ if not path_filters: return attrtree # Convert string path filters path_filters = [ tuple(pf.split('.')) if not isinstance(pf, tuple) else pf for pf in path_filters ] # Search for substring matches between paths and path filters new_attrtree = AttrTree() for path, item in attrtree.path_items.items(): if any([ all([subpath in path for subpath in pf]) for pf in path_filters ]): new_attrtree.set_path(path, item) return new_attrtree
class Model(param.Parameterized): """ The available setup options are: :'training_patterns': fills the training_patterns AttrTree with pattern generator instances. The path is the name of the input sheet. Usually calls PatternCoordinator to do this. :'setup_sheets': determines the number of sheets, their types and names sets sheet parameters according to the registered methods in level sets sheet matchconditions according to the registered methods in matchconditions :'projections': determines which connections should be present between the sheets according to the matchconditions of SheetSpec objects, using connect to specify the connection type and sets their parameters according to the registered methods in connect The available instantiate options are: :'sheets': instantiates all sheets and registers them in topo.sim :'projections': instantiates all projections and registers them in topo.sim """ __abstract = True matchconditions = MatchConditions() sheet_decorators = set() projection_decorators = set() @classmethod def register_decorator(cls, object_type): name = object_type.name decorator = ClassDecorator(name, object_type) setattr(cls, name, decorator) if issubclass(object_type, topo.sheet.Sheet): cls.sheet_decorators.add(decorator) if issubclass(object_type, topo.projection.Projection): cls.projection_decorators.add(decorator) @classmethod def _collect(cls, decorators, name): """ Given a list of ClassDecorators (e.g self.sheet_decorators or self.projection_decorators), collate the named attribute (i.e. 'types' or 'labels') across the decorators according to priority. """ flattened = [el for d in decorators for el in getattr(d, name).items()] return dict( (k, v) for (k, (_, v)) in sorted(flattened, key=lambda x: x[1][0])) @property def sheet_labels(self): "The mapping of level method to corresponding label" return self._collect(self.sheet_decorators, 'labels') @property def sheet_types(self): "The mapping of level label to sheet type" return self._collect(self.sheet_decorators, 'types') @property def projection_labels(self): "The mapping of projection method to corresponding label" return self._collect(self.projection_decorators, 'labels') @property def projection_types(self): "The mapping of projection label to projection type" return self._collect(self.projection_decorators, 'types') @property def modified_parameters(self): "Dictionary of modified model parameters" return {k: v for k, v in self.get_param_values(onlychanged=True)} def __init__(self, setup_options=True, register=True, time_dependent=True, **params): numbergen.TimeAware.time_dependent = time_dependent if register: self._register_global_params(params) super(Model, self).__init__(**params) self._sheet_types = {} self._projection_types = {} self.attrs = AttrTree() self.training_patterns = AttrTree() self.sheets = AttrTree() self.projections = AttrTree() self.setup(setup_options) def _register_global_params(self, params): """ Register the parameters of this object as global parameters available for users to set from the command line. Values supplied as global parameters will override those of the given dictionary of params. """ for name, obj in self.params().items(): global_params.add(**{name: obj}) for name, val in params.items(): global_params.params(name).default = val params.update(global_params.get_param_values()) params["name"] = self.name #==============================================# # Public methods to be implemented by modelers # #==============================================# def setup_attributes(self, attrs): """ Method to precompute any useful attributes from the class parameters. For instance, if there is a ``num_lags`` parameter, this method could compute the actual projection delays and store it as attrs.lags. The return value is the updated attrs AttrTree. In addition, this method can be used to configure class attributes of the model components. """ return attrs def setup_training_patterns(self, **overrides): """ Returns a dictionary of PatternGenerators to be added to self.training_patterns, with the target sheet name keys and pattern generator values. The overrides keywords can be used by a subclass to parameterize the training patterns e.g. override the default parameters of a PatternCoordinator object. """ raise NotImplementedError def setup_sheets(self): """ Returns a dictionary of properties or equivalent Lancet.Args object. Each outer key must be the level name and the values are lists of property dictionaries for the sheets at that level (or equivalent Lancet Args object). For instance, two LGN sheets at the 'LGN' level could be defined by either: {'LGN':[{'polarity':'ON'}, {'polarity':'OFF'}]} OR {'LGN':lancet.List('polarity', ['ON', 'OFF'])} The specified properties are used to initialize the sheets AttrTree with SheetSpec objects. """ raise NotImplementedError def setup_analysis(self): """ Set up appropriate defaults for analysis functions in topo.analysis.featureresponses. """ pass #====================================================# # Remaining methods should not need to be overridden # #====================================================# def setup(self, setup_options): """ This method can be used to setup certain parts of the submodel. If setup_options=True, all setup methods are called. setup_options can also be a list, whereas all list items of available_setup_options are accepted. Available setup options are: 'training_patterns','sheets','projections' and 'analysis'. Please consult the docstring of the Model class for more information about each setup option. """ available_setup_options = [ 'attributes', 'training_patterns', 'sheets', 'projections', 'analysis' ] if setup_options == True: setup_options = available_setup_options if 'attributes' in setup_options: self.attrs = self.setup_attributes(self.attrs) if 'training_patterns' in setup_options: training_patterns = self.setup_training_patterns() for name, training_pattern in training_patterns.items(): self.training_patterns.set_path(name, training_pattern) if 'sheets' in setup_options: sheet_properties = self.setup_sheets() enumeration = enumerate(sheet_properties.items()) for (ordering, (level, property_list)) in enumeration: sheet_type = self.sheet_types[level] if isinstance(property_list, lancet.Identity): property_list = [{}] elif isinstance(property_list, lancet.Args): property_list = property_list.specs # If an empty list or Args() elif not property_list: continue for properties in property_list: spec_properties = dict(level=level, **properties) sheet_spec = SheetSpec(sheet_type, spec_properties) sheet_spec.sort_precedence = ordering self.sheets.set_path(str(sheet_spec), sheet_spec) self._update_sheet_spec_parameters() if 'projections' in setup_options: self._compute_projection_specs() if 'analysis' in setup_options: self._setup_analysis() def _update_sheet_spec_parameters(self): for sheet_spec in self.sheets.path_items.values(): param_method = self.sheet_labels.get(sheet_spec.level, None) if not param_method: raise Exception("Parameters for sheet level %r not specified" % sheet_spec.level) updated_params = param_method(self, sheet_spec.properties) sheet_spec.update(**updated_params) def _matchcondition_holds(self, matchconditions, src_sheet): """ Given a dictionary of properties to match and a target sheet spec, return True if the matchcondition holds else False. """ matches = True if matchconditions is None: return False for incoming_key, incoming_value in matchconditions.items(): if incoming_key in src_sheet.properties and \ str(src_sheet.properties[incoming_key]) not in str(incoming_value): matches = False break return matches def _compute_projection_specs(self): """ Loop through all possible combinations of SheetSpec objects in self.sheets If the src_sheet fulfills all criteria specified in dest_sheet.matchconditions, create a new ProjectionSpec object and add this item to self.projections. """ sheetspec_product = itertools.product(self.sheets.path_items.values(), self.sheets.path_items.values()) for src_sheet, dest_sheet in sheetspec_product: has_matchcondition = (dest_sheet.level in self.matchconditions) conditions = (self.matchconditions.compute_conditions( dest_sheet.level, self, dest_sheet.properties) if has_matchcondition else {}) for matchname, matchconditions in conditions.items(): if self._matchcondition_holds(matchconditions, src_sheet): proj = ProjectionSpec(self.projection_types[matchname], src_sheet, dest_sheet) paramsets = self.projection_labels[matchname]( self, src_sheet.properties, dest_sheet.properties) paramsets = [paramsets] if isinstance(paramsets, dict) else paramsets for paramset in paramsets: proj = ProjectionSpec(self.projection_types[matchname], src_sheet, dest_sheet) proj.update(**paramset) # Only used when time_dependent=False # (which is to be deprecated) proj.matchname = matchname path = (str(dest_sheet), paramset['name']) self.projections.set_path(path, proj) def __call__(self, instantiate_options=True, verbose=False): """ Instantiates all sheets or projections in self.sheets or self.projections and registers them in the topo.sim instance. If instantiate_options=True, all items are initialised instantiate_options can also be a list, whereas all list items of available_instantiate_options are accepted. Available instantiation options are: 'sheets' and 'projections'. Please consult the docstring of the Model class for more information about each instantiation option. """ msglevel = self.message if verbose else self.debug available_instantiate_options = ['sheets', 'projections'] if instantiate_options == True: instantiate_options = available_instantiate_options if 'sheets' in instantiate_options: for sheet_spec in self.sheets.path_items.itervalues(): msglevel('Level ' + sheet_spec.level + ': Sheet ' + str(sheet_spec)) sheet_spec() if 'projections' in instantiate_options: for proj in sorted(self.projections): msglevel('Match: ' + proj.matchname + ': Connection ' + str(proj.src) + \ '->' + str(proj.dest) + ' ' + proj.parameters['name']) proj() def summary(self, printed=True): heading_line = '=' * len(self.name) summary = [heading_line, self.name, heading_line, ''] for sheet_spec in sorted(self.sheets): summary.append(sheet_spec.summary(printed=False)) projections = [ proj for proj in self.projections if str(proj).startswith(str(sheet_spec)) ] for projection_spec in sorted(projections, key=lambda p: str(p)): summary.append(" " + projection_spec.summary(printed=False)) summary.append('') if printed: print "\n".join(summary) else: return "\n".join(summary) def __str__(self): return self.name def _repr_pretty_(self, p, cycle): p.text(self.summary(printed=False)) def modifications(self, components=['model', 'sheets', 'projections']): """ Display the names of all modified parameters for the specified set of components. By default all modified parameters are listed - first with the model parameters, then the sheet parameters and lastly the projection parameters. """ mapping = { 'model': [self], 'sheets': self.sheets, 'projections': self.projections } lines = [] for component in components: heading = "=" * len(component) lines.extend([heading, component.capitalize(), heading, '']) specs = mapping[component] padding = max(len(str(spec)) for spec in specs) for spec in sorted(specs): modified = [str(el) for el in sorted(spec.modified_parameters)] lines.append("%s : [%s]" % (str(spec).ljust(padding), ", ".join(modified))) lines.append('') print "\n".join(lines)
class Model(param.Parameterized): """ The available setup options are: :'training_patterns': fills the training_patterns AttrTree with pattern generator instances. The path is the name of the input sheet. Usually calls PatternCoordinator to do this. :'setup_sheets': determines the number of sheets, their types and names sets sheet parameters according to the registered methods in level sets sheet matchconditions according to the registered methods in matchconditions :'projections': determines which connections should be present between the sheets according to the matchconditions of SheetSpec objects, using connect to specify the connection type and sets their parameters according to the registered methods in connect The available instantiate options are: :'sheets': instantiates all sheets and registers them in topo.sim :'projections': instantiates all projections and registers them in topo.sim """ __abstract = True matchconditions = MatchConditions() sheet_decorators = set() projection_decorators = set() @classmethod def register_decorator(cls, object_type): name = object_type.name decorator = ClassDecorator(name, object_type) setattr(cls, name, decorator) if issubclass(object_type, topo.sheet.Sheet): cls.sheet_decorators.add(decorator) if issubclass(object_type, topo.projection.Projection): cls.projection_decorators.add(decorator) @classmethod def _collect(cls, decorators, name): """ Given a list of ClassDecorators (e.g self.sheet_decorators or self.projection_decorators), collate the named attribute (i.e. 'types' or 'labels') across the decorators according to priority. """ flattened = [el for d in decorators for el in getattr(d, name).items()] return dict((k,v) for (k, (_, v)) in sorted(flattened, key=lambda x: x[1][0])) @property def sheet_labels(self): "The mapping of level method to corresponding label" return self._collect(self.sheet_decorators, 'labels') @property def sheet_types(self): "The mapping of level label to sheet type" return self._collect(self.sheet_decorators, 'types') @property def projection_labels(self): "The mapping of projection method to corresponding label" return self._collect(self.projection_decorators, 'labels') @property def projection_types(self): "The mapping of projection label to projection type" return self._collect(self.projection_decorators, 'types') @property def modified_parameters(self): "Dictionary of modified model parameters" return {k:v for k,v in self.get_param_values(onlychanged=True)} def __init__(self, setup_options=True, register=True, time_dependent=True, **params): numbergen.TimeAware.time_dependent = time_dependent if register: self._register_global_params(params) super(Model,self).__init__(**params) self._sheet_types = {} self._projection_types = {} self.attrs = AttrTree() self.training_patterns = AttrTree() self.sheets = AttrTree() self.projections = AttrTree() self.setup(setup_options) def _register_global_params(self, params): """ Register the parameters of this object as global parameters available for users to set from the command line. Values supplied as global parameters will override those of the given dictionary of params. """ for name,obj in self.params().items(): global_params.add(**{name:obj}) for name,val in params.items(): global_params.params(name).default=val params.update(global_params.get_param_values()) params["name"]=self.name #==============================================# # Public methods to be implemented by modelers # #==============================================# def setup_attributes(self, attrs): """ Method to precompute any useful attributes from the class parameters. For instance, if there is a ``num_lags`` parameter, this method could compute the actual projection delays and store it as attrs.lags. The return value is the updated attrs AttrTree. In addition, this method can be used to configure class attributes of the model components. """ return attrs def setup_training_patterns(self, **overrides): """ Returns a dictionary of PatternGenerators to be added to self.training_patterns, with the target sheet name keys and pattern generator values. The overrides keywords can be used by a subclass to parameterize the training patterns e.g. override the default parameters of a PatternCoordinator object. """ raise NotImplementedError def setup_sheets(self): """ Returns a dictionary of properties or equivalent Lancet.Args object. Each outer key must be the level name and the values are lists of property dictionaries for the sheets at that level (or equivalent Lancet Args object). For instance, two LGN sheets at the 'LGN' level could be defined by either: {'LGN':[{'polarity':'ON'}, {'polarity':'OFF'}]} OR {'LGN':lancet.List('polarity', ['ON', 'OFF'])} The specified properties are used to initialize the sheets AttrTree with SheetSpec objects. """ raise NotImplementedError def setup_analysis(self): """ Set up appropriate defaults for analysis functions in topo.analysis.featureresponses. """ pass #====================================================# # Remaining methods should not need to be overridden # #====================================================# def setup(self,setup_options): """ This method can be used to setup certain parts of the submodel. If setup_options=True, all setup methods are called. setup_options can also be a list, whereas all list items of available_setup_options are accepted. Available setup options are: 'training_patterns','sheets','projections' and 'analysis'. Please consult the docstring of the Model class for more information about each setup option. """ available_setup_options = ['attributes', 'training_patterns', 'sheets', 'projections', 'analysis'] if setup_options==True: setup_options = available_setup_options if 'attributes' in setup_options: self.attrs = self.setup_attributes(self.attrs) if 'training_patterns' in setup_options: training_patterns = self.setup_training_patterns() for name, training_pattern in training_patterns.items(): self.training_patterns.set_path(name, training_pattern) if 'sheets' in setup_options: sheet_properties = self.setup_sheets() enumeration = enumerate(sheet_properties.items()) for (ordering, (level, property_list)) in enumeration: sheet_type = self.sheet_types[level] if isinstance(property_list, lancet.Identity): property_list = [{}] elif isinstance(property_list, lancet.Args): property_list = property_list.specs # If an empty list or Args() elif not property_list: continue for properties in property_list: spec_properties = dict(level=level, **properties) sheet_spec = SheetSpec(sheet_type, spec_properties) sheet_spec.sort_precedence = ordering self.sheets.set_path(str(sheet_spec), sheet_spec) self._update_sheet_spec_parameters() if 'projections' in setup_options: self._compute_projection_specs() if 'analysis' in setup_options: self._setup_analysis() def _update_sheet_spec_parameters(self): for sheet_spec in self.sheets.path_items.values(): param_method = self.sheet_labels.get(sheet_spec.level, None) if not param_method: raise Exception("Parameters for sheet level %r not specified" % sheet_spec.level) updated_params = param_method(self,sheet_spec.properties) sheet_spec.update(**updated_params) def _matchcondition_holds(self, matchconditions, src_sheet): """ Given a dictionary of properties to match and a target sheet spec, return True if the matchcondition holds else False. """ matches=True if matchconditions is None: return False for incoming_key, incoming_value in matchconditions.items(): if incoming_key in src_sheet.properties and \ str(src_sheet.properties[incoming_key]) not in str(incoming_value): matches=False break return matches def _compute_projection_specs(self): """ Loop through all possible combinations of SheetSpec objects in self.sheets If the src_sheet fulfills all criteria specified in dest_sheet.matchconditions, create a new ProjectionSpec object and add this item to self.projections. """ sheetspec_product = itertools.product(self.sheets.path_items.values(), self.sheets.path_items.values()) for src_sheet, dest_sheet in sheetspec_product: has_matchcondition = (dest_sheet.level in self.matchconditions) conditions = (self.matchconditions.compute_conditions( dest_sheet.level, self,dest_sheet.properties) if has_matchcondition else {}) for matchname, matchconditions in conditions.items(): if self._matchcondition_holds(matchconditions, src_sheet): proj = ProjectionSpec(self.projection_types[matchname], src_sheet, dest_sheet) paramsets = self.projection_labels[matchname](self, src_sheet.properties, dest_sheet.properties) paramsets = [paramsets] if isinstance(paramsets, dict) else paramsets for paramset in paramsets: proj = ProjectionSpec(self.projection_types[matchname], src_sheet, dest_sheet) proj.update(**paramset) # Only used when time_dependent=False # (which is to be deprecated) proj.matchname = matchname path = (str(dest_sheet), paramset['name']) self.projections.set_path(path, proj) def __call__(self,instantiate_options=True, verbose=False): """ Instantiates all sheets or projections in self.sheets or self.projections and registers them in the topo.sim instance. If instantiate_options=True, all items are initialised instantiate_options can also be a list, whereas all list items of available_instantiate_options are accepted. Available instantiation options are: 'sheets' and 'projections'. Please consult the docstring of the Model class for more information about each instantiation option. """ msglevel = self.message if verbose else self.debug available_instantiate_options = ['sheets','projections'] if instantiate_options==True: instantiate_options=available_instantiate_options if 'sheets' in instantiate_options: for sheet_spec in self.sheets.path_items.itervalues(): msglevel('Level ' + sheet_spec.level + ': Sheet ' + str(sheet_spec)) sheet_spec() if 'projections' in instantiate_options: for proj in sorted(self.projections): msglevel('Match: ' + proj.matchname + ': Connection ' + str(proj.src) + \ '->' + str(proj.dest) + ' ' + proj.parameters['name']) proj() def summary(self, printed=True): heading_line = '=' * len(self.name) summary = [heading_line, self.name, heading_line, ''] for sheet_spec in sorted(self.sheets): summary.append(sheet_spec.summary(printed=False)) projections = [proj for proj in self.projections if str(proj).startswith(str(sheet_spec))] for projection_spec in sorted(projections, key=lambda p: str(p)): summary.append(" " + projection_spec.summary(printed=False)) summary.append('') if printed: print "\n".join(summary) else: return "\n".join(summary) def __str__(self): return self.name def _repr_pretty_(self, p, cycle): p.text(self.summary(printed=False)) def modifications(self, components=['model', 'sheets', 'projections']): """ Display the names of all modified parameters for the specified set of components. By default all modified parameters are listed - first with the model parameters, then the sheet parameters and lastly the projection parameters. """ mapping = {'model': [self], 'sheets':self.sheets, 'projections':self.projections} lines = [] for component in components: heading = "=" * len(component) lines.extend([heading, component.capitalize(), heading, '']) specs = mapping[component] padding = max(len(str(spec)) for spec in specs) for spec in sorted(specs): modified = [str(el) for el in sorted(spec.modified_parameters)] lines.append("%s : [%s]" % (str(spec).ljust(padding), ", ".join(modified))) lines.append('') print "\n".join(lines)