def max_threads_per_block(self, device=0): try: result = self.cy_cc.max_threads_per_block(device) except Exception as e: Global._print(e) Global._error('CUDA is not correctly installed on your system.') return result
def check_and_apply_pow_fix(eqs): """ CUDA SDKs before 7.5 had an error if std=c++11 is enabled related to pow(double, int). Only pow(double, double) was detected as device function, the pow(double, int) will be detected as host function. (This was fixed within SDK 7.5) To support also earlier versions, we simply add a double type cast. """ if eqs.strip() == "": # nothing to do return eqs try: from ANNarchy.generator.CudaCheck import CudaCheck if CudaCheck().runtime_version() > 7000: # nothing to do, is working in higher SDKs return eqs except: Global._error('CUDA is not correctly installed on your system') if Global.config['verbose']: Global._print( 'occurance of pow() and SDK below 7.5 detected, apply fix.') # detect all pow statements pow_occur = re.findall(r"pow[\( [\S\s]*?\)*?, \d+\)]*?", eqs) for term in pow_occur: eqs = eqs.replace(term, term.replace(', ', ', (double)')) return eqs
def warp_size(self, device=0): try: result = self.cy_cc.warp_size(device) except Exception as e: Global._print(e) Global._error('CUDA is not correctly installed on your system.') return result
def eventdriven(self, expression): # Standardize the equation real_tau, _, steadystate = self.standardize_ODE(expression) if real_tau is None: # the equation can not be standardized Global._print(self.expression) Global._error( 'The equation is not a linear ODE and can not be evaluated exactly.' ) # Check the steady state is not dependent on other variables for var in self.variables: if self.local_dict[var] in steadystate.atoms(): Global._print(self.expression) Global._error( 'The equation can not depend on other variables (' + var + ') to be evaluated exactly.') # Obtain C code variable_name = self.c_code(self.local_dict[self.name]) steady = self.c_code(steadystate) if steady == '0': code = variable_name + ' *= exp(dt*(_last_event%(local_index)s - (t))/(' + self.c_code( real_tau) + '));' else: code = variable_name + ' = ' + steady + ' + (' + variable_name + ' - ' + steady + ')*exp(dt*(_last_event%(local_index)s - (t))/(' + self.c_code( real_tau) + '));' return code
def load_cython_lib(libname, libpath): """ Load the shared library created by Cython using importlib. Follows the example "Multiple modules in one library" in PEP 489. TODO: As described in PEP 489 "Module Reloading" a reloading of dynamic extension modules is not supported. This leads to some problems for our reusage of the ANNarchyCore library ... Sources: PEP 489: (https://www.python.org/dev/peps/pep-0489/) """ # create a loader to mimic find module loader = importlib.machinery.ExtensionFileLoader(libname, libpath) spec = importlib.util.spec_from_loader(libname, loader) module = importlib.util.module_from_spec(spec) if Global.config['verbose']: Global._print('Loading library...', libname, libpath) loader.exec_module(module) if Global.config['verbose']: Global._print('Library loaded.') return module
def _init_attributes(self): """ Method used after compilation to initialize the attributes.""" # Initialize the population self.initialized = True # Transfer the initial values of all attributes for name, value in self.init.items(): if isinstance(value, Global.Constant): self.__setattr__(name, value.value) else: self.__setattr__(name, value) # Activate the population self.cyInstance.activate(self.enabled) # Reset to generate the right structures self.cyInstance.reset() # If the spike population has a refractory period: if self.neuron_type.type == 'spike' and self.neuron_type.description['refractory']: if isinstance(self.neuron_type.description['refractory'], str): # a global variable try: self.refractory = eval('self.'+self.neuron_type.description['refractory']) except Exception as e: Global._print(e, self.neuron_type.description['refractory']) Global._error('The initialization for the refractory period is not valid.') else: # a value self.refractory = self.neuron_type.description['refractory'] # Spiking neurons can compute a mean FR if self.neuron_type.type == 'spike': getattr(self.cyInstance, 'compute_firing_rate')(self._compute_mean_fr)
def reset(self, attributes=-1): """ Resets all parameters and variables of the population to the value they had before the call to compile(). :param attributes: list of attributes (parameter or variable) which should be reinitialized. Default: all attributes. """ if attributes == -1: try: self.set(self.init) except Exception as e: Global._print(e) Global._error( "Population.reset(): something went wrong while resetting." ) else: # only some of them for var in attributes: # check it exists if not var in self.attributes: Global._warning( "Population.reset():", var, "is not an attribute of the population, skipping.") continue try: self.__setattr__(var, self.init[var]) except Exception as e: Global._print(e) Global._warning( "Population.reset(): something went wrong while resetting", var) self.cyInstance.activate(self.enabled) self.cyInstance.reset()
def _load_from_sparse(self, pre, post, weights, delays): from scipy.sparse import csc_matrix # Create an empty CSR object csr = Connector.CSR() # Find offsets if isinstance(self.pre, PopulationView): pre_ranks = self.pre.ranks else: pre_ranks = [i for i in range(self.pre.size)] if isinstance(self.post, PopulationView): post_ranks = self.post.ranks else: post_ranks = [i for i in range(self.post.size)] # Process the sparse matrix and fill the csr weights.sort_indices() (pre, post) = weights.shape if (pre, post) != (len(pre_ranks), len(post_ranks)): Global._error("connect_from_sparse(): the sparse matrix does not have the correct dimensions.") Global._print('Expected:', (len(pre_ranks), len(post_ranks))) Global._print('Received:', (pre, post)) exit(0) for idx_post in range(post): idx_pre = weights.getcol(idx_post).indices w = weights.getcol(idx_post).data pr = [pre_ranks[i] for i in idx_pre] csr.add(post_ranks[idx_post], pr, w, [float(delays)]) return csr
def reset(self, attributes=-1): """ Resets all parameters and variables of the population to the value they had before the call to compile(). *Parameters:* * **attributes**: list of attributes (parameter or variable) which should be reinitialized. Default: all attributes. """ if attributes == -1: try: self.set(self.init) except Exception as e: Global._print(e) Global._error("Population.reset(): something went wrong while resetting", var) else: # only some of them for var in attributes: # check it exists if not var in self.attributes: Global._warning("Population.reset():", var, "is not an attribute of the population, skipping.") continue try: self.__setattr__(var, self.init[var]) except Exception as e: Global._print(e) Global._warning("Population.reset(): something went wrong while resetting", var) self.cyInstance.activate(self.enabled) self.cyInstance.reset()
def gpu_count(self): try: result = self.cy_cc.gpu_count() except Exception as e: Global._print(e) Global._error('CUDA is not correctly installed on your system.') return result
def check_and_apply_pow_fix(eqs): """ CUDA SDKs before 7.5 had an error if std=c++11 is enabled related to pow(double, int). Only pow(double, double) was detected as device function, the pow(double, int) will be detected as host function. (This was fixed within SDK 7.5) To support also earlier versions, we simply add a double type cast. """ if eqs.strip() == "": # nothing to do return eqs if Global.config['cuda_version'] > 7.0: # nothing to do, is working in higher SDKs return eqs if Global.config['verbose']: Global._print( 'occurance of pow() and SDK below 7.5 detected, apply fix.') # detect all pow statements pow_occur = re.findall(r"pow[\( [\S\s]*?\)*?, \d+\)]*?", eqs) for term in pow_occur: eqs = eqs.replace(term, term.replace(', ', ', (double)')) return eqs
def reset(self, attributes=-1, synapses=False): """ Resets all parameters and variables of the projection to the value they had before the call to compile. *Parameters:* * **attributes**: list of attributes (parameter or variable) which should be reinitialized. Default: all attributes. .. note:: Only parameters and variables are reinitialized, not the connectivity structure (including the weights and delays). The parameter ``synapses`` will be used in a future release to also reinitialize the connectivity structure. """ if attributes == -1: attributes = self.attributes for var in attributes: # Skip w if var=='w': continue # check it exists if not var in self.attributes: Global._warning("Projection.reset():", var, "is not an attribute of the population, won't reset.") continue # Set the value try: self.__setattr__(var, self.init[var]) except Exception as e: Global._print(e) Global._warning("Projection.reset(): something went wrong while resetting", var)
def parse(self, part=None): if not part: part = self.eq expression = transform_condition(part) # Check if there is a == in the condition if '==' in expression: # Is it the only term, or are there other operations? if '&' in expression or '|' in expression: expression = re.sub(r'([\w\s.]+)==([\w\s.]+)', r'Equality(\1, \2)', expression) else: terms = expression.split('==') expression = 'Equality(' + terms[0] + ', ' + terms[1] + ')' # Check if there is a != in the condition if '!=' in expression: # Is it the only term, or are there other operations? if '&' in expression or '|' in expression: expression = re.sub(r'([\w\s.]+)!=([\w\s.]+)', r'Not(Equality(\1, \2))', expression) else: terms = expression.split('!=') expression = 'Not(Equality(' + terms[0] + ', ' + terms[1] + '))' try: eq = parse_expr(expression, local_dict = self.local_dict, transformations = ((auto_number, convert_xor,)) ) except: Global._print(expression) Global._error('The function depends on unknown variables.') return ccode(eq, precision=8, user_functions=self.user_functions)
def check_and_apply_pow_fix(eqs): """ CUDA SDKs before 7.5 had an error if std=c++11 is enabled related to pow(double, int). Only pow(double, double) was detected as device function, the pow(double, int) will be detected as host function. (This was fixed within SDK 7.5) To support also earlier versions, we simply add a double type cast. """ if eqs.strip() == "": # nothing to do return eqs try: from ANNarchy.generator.CudaCheck import CudaCheck if CudaCheck().runtime_version() > 7000: # nothing to do, is working in higher SDKs return eqs except: Global._error('CUDA is not correctly installed on your system') if Global.config['verbose']: Global._print('occurance of pow() and SDK below 7.5 detected, apply fix.') # detect all pow statements pow_occur = re.findall(r"pow[\( [\S\s]*?\)*?, \d+\)]*?", eqs) for term in pow_occur: eqs = eqs.replace(term, term.replace(', ', ', (double)')) return eqs
def create_synapse(self, rank, w=0.0, delay=0): """ Creates a synapse for this dendrite with the given pre-synaptic neuron. *Parameters*: * **rank**: rank of the pre-synaptic neuron * **w**: synaptic weight (defalt: 0.0). * **delay**: synaptic delay (default = dt) """ if not Global.config['structural_plasticity']: Global._error('"structural_plasticity" has not been set to True in setup(), can not add the synapse.') return if rank in self.pre_ranks: Global._error('the synapse of rank ' + str(rank) + ' already exists.') return # Set default values for the additional variables extra_attributes = {} for var in self.proj.synapse_type.description['parameters'] + self.proj.synapse_type.description['variables']: if not var['name'] in ['w', 'delay'] and var['name'] in self.proj.synapse_type.description['local']: if not isinstance(self.proj.init[var['name']], (int, float, bool)): init = var['init'] else: init = self.proj.init[var['name']] extra_attributes[var['name']] = init try: self.proj.cyInstance.add_synapse(self.post_rank, rank, w, int(delay/Global.config['dt']), **extra_attributes) except Exception as e: Global._print(e)
def extract_axon_spike_condition(description): """ Extract the condition for emitting an axonal spike event. Further the reset after the event is returned. """ if description['raw_axon_spike'] == None: return None cond = prepare_string(description['raw_axon_spike']) if len(cond) > 1: Global._print(description['raw_axon_spike']) Global._error('The spike condition must be a single expression') translator = Equation('raw_axon_spike_cond', cond[0].strip(), description) raw_spike_code = translator.parse() # Also store the variables used in the condition, as it may be needed for CUDA generation spike_code_dependencies = translator.dependencies() reset_desc = [] if 'raw_reset' in description.keys() and description['raw_axon_reset']: reset_desc = process_equations(description['raw_axon_reset']) for var in reset_desc: translator = Equation(var['name'], var['eq'], description) var['cpp'] = translator.parse() var['dependencies'] = translator.dependencies() return { 'spike_cond': raw_spike_code, 'spike_cond_dependencies': spike_code_dependencies, 'spike_reset': reset_desc }
def _guess_proj_kernel_config(self, proj): """ Instead of a fixed amount of threads for each kernel, we try to guess a good configuration based on the pre-synaptic population size. """ from math import log max_tpb = 512 warp_size = 32 num_neur = proj.pre.size / 4 # at least 1/4 of the neurons are connected guess = warp_size # smallest block is 1 warp # Simplest case: we have more neurons than # available threads per block if num_neur > max_tpb: guess = max_tpb # check which is the closest possible thread amount pow_of_2 = [ 2**x for x in range(int(log(warp_size, 2)), int(log(max_tpb, 2)) + 1) ] for i in range(len(pow_of_2)): if pow_of_2[i] < num_neur: continue else: guess = pow_of_2[i] break if Global.config['verbose']: Global._print('projection', proj.id, ' - kernel size:', guess) return guess
def runtime_version(self): try: result = self.cy_cc.runtime_version() except Exception as e: Global._print(e) Global._error('CUDA is not correctly installed on your system.') return result
def report_latex(filename="./report.tex", standalone=True, gather_subprojections=False, net_id=0): """ Generates a .tex file describing the network according to: Nordlie E, Gewaltig M-O, Plesser HE (2009). Towards Reproducible Descriptions of Neuronal Network Models. PLoS Comput Biol 5(8): e1000456. **Parameters:** * *filename*: name of the .tex file where the report will be written (default: "./report.tex") * *standalone*: tells if the generated file should be directly compilable or only includable (default: True) * *gather_subprojections*: if a projection between two populations has been implemented as a multiple of projections between sub-populations, this flag allows to group them in the summary (default: False). * *net_id*: id of the network to be used for reporting (default: 0, everything that was declared) """ # stdout Global._print('Generating report in', filename) # Generate the summary summary = _generate_summary(net_id) # Generate the populations populations = _generate_populations(net_id) # Generate the projections projections = _generate_projections(net_id, gather_subprojections) # Generate the neuron models neuron_models = _generate_neuron_models(net_id) # Generate the synapse models synapse_models = _generate_synapse_models(net_id) # Generate the constants constants = _generate_constants(net_id) # Generate the functions functions = _generate_functions(net_id) # Generate the population parameters pop_parameters = _generate_population_parameters(net_id) # Generate the population parameters proj_parameters = _generate_projection_parameters(net_id, gather_subprojections) if not os.path.exists(os.path.dirname(filename)): os.makedirs(os.path.dirname(filename)) with open(filename, 'w') as wfile: if standalone: wfile.write(header) wfile.write(preamble) wfile.write(summary) wfile.write(populations) wfile.write(projections) wfile.write(neuron_models) wfile.write(synapse_models) wfile.write(parameters_template) wfile.write(constants) wfile.write(functions) wfile.write(pop_parameters) wfile.write(proj_parameters) if standalone: wfile.write(footer)
def version_str(self): """ Returns cuda compatibility as string, usable for -gencode as argument. """ try: cu_version = self.cy_cc.get_cuda_version() except Exception as e: Global._print(e) Global._error('CUDA is not correctly installed on your system.') return str(cu_version[0]) + str(cu_version[1])
def idx_target(val): target = val.group(1).strip() if target == '': Global._print(eq) Global._error('post.sum() requires one argument.') dependencies['post'].append('sum('+target+')') rep = '_post_sum_' + target.strip() untouched[rep] = '%(post_prefix)s_sum_' + target + '%(post_index)s' return rep
def version(self): """ Returns cuda compatibility as tuple(major,minor) """ try: result = self.cy_cc.get_cuda_version() except Exception as e: Global._print(e) Global._error('CUDA is not correctly installed on your system.') return result
def report_markdown(filename="./report.tex", standalone=True, gather_subprojections=False, title=None, author=None, date=None, net_id=0): """ Generates a .md file describing the network. *Parameters:* * **filename**: name of the .tex file where the report will be written (default: "./report.tex") * **standalone**: tells if the generated file should be directly compilable or only includable (ignored) * **gather_subprojections**: if a projection between two populations has been implemented as a multiple of projections between sub-populations, this flag allows to group them in the summary (default: False). * **net_id**: id of the network to be used for reporting (default: 0, everything that was declared) * **title**: title of the document (default: "Network description") * **author**: author of the document (default: "ANNarchy (Artificial Neural Networks architect)") * **date**: date of the document (default: empty) """ # stdout Global._print('Generating report in', filename) # Header if title == None: title = "Network description" if author == None: author = "ANNarchy (Artificial Neural Networks architect)" if date == None: date = "" header = """--- title: %(title)s author: %(author)s date: %(date)s --- """ % {'title': title, 'author': author, 'date': date} # Structure structure = _generate_summary(net_id) # Neurons neuron_models = _generate_neuron_models(net_id) # Synapses synapse_models = _generate_synapse_models(net_id) # Parameters parameters = _generate_parameters(net_id, gather_subprojections) # Possibly create the directory if it does not exist path_name = os.path.dirname(filename) if not path_name in ["", "."]: if not os.path.exists(path_name): os.makedirs(path_name) with open(filename, 'w') as wfile: wfile.write(header) wfile.write(structure) wfile.write(neuron_models) wfile.write(synapse_models) wfile.write(parameters)
def idx_target(val): target = val.group(1).strip() if target == '': Global._print(eq) Global._error('post.sum() requires one argument.') dependencies['post'].append('sum(' + target + ')') rep = '_post_sum_' + target.strip() untouched[ rep] = '%(post_prefix)s_sum_' + target + '%(post_index)s' return rep
def connect_from_file(self, filename): """ Builds a connection pattern using data saved using the Projection.save_connectivity() method (not save()!). *Parameters*: * **filename**: file where the data was saved. .. note:: Only the ranks, weights and delays are loaded, not the other variables. """ # Create an empty CSR object csr = Connector.CSR() # Load the data from ANNarchy.core.IO import _load_data try: data = _load_data(filename) except Exception as e: Global._print(e) Global._error('connect_from_file(): Unable to load the data', filename, 'into the projection.') exit(0) # Load the CSR object try: csr.post_rank = data['post_ranks'] csr.pre_rank = data['pre_ranks'] if isinstance(data['w'], (int, float)): self._single_constant_weight = True csr.w = [[data['w']]] else: csr.w = data['w'] csr.size = data['size'] csr.nb_synapses = data['nb_synapses'] if data['delay']: csr.delay = data['delay'] csr.max_delay = data['max_delay'] csr.uniform_delay = data['uniform_delay'] except Exception as e: Global._print(e) Global._error('Unable to load the data', filename, 'into the projection.') exit(0) # Store the synapses self.connector_name = "From File" self.connector_description = "From File" self._store_connectivity( self._load_from_csr, (csr, ), csr.max_delay if csr.uniform_delay > -1 else csr.delay) return self
def report_markdown(filename="./report.tex", standalone=True, gather_subprojections=False, title=None, author=None, date=None, net_id=0): """ Generates a .md file describing the network. *Parameters:* * **filename**: name of the .tex file where the report will be written (default: "./report.tex") * **standalone**: tells if the generated file should be directly compilable or only includable (ignored) * **gather_subprojections**: if a projection between two populations has been implemented as a multiple of projections between sub-populations, this flag allows to group them in the summary (default: False). * **net_id**: id of the network to be used for reporting (default: 0, everything that was declared) * **title**: title of the document (default: "Network description") * **author**: author of the document (default: "ANNarchy (Artificial Neural Networks architect)") * **date**: date of the document (default: empty) """ # stdout Global._print('Generating report in', filename) # Header if title == None: title = "Network description" if author == None: author = "ANNarchy (Artificial Neural Networks architect)" if date == None: date = "" header = """--- title: %(title)s author: %(author)s date: %(date)s --- """ % {'title': title, 'author': author, 'date': date} # Structure structure = _generate_summary(net_id) # Neurons neuron_models = _generate_neuron_models(net_id) # Synapses synapse_models = _generate_synapse_models(net_id) # Parameters parameters = _generate_parameters(net_id, gather_subprojections) if not os.path.exists(os.path.dirname(filename)): os.makedirs(os.path.dirname(filename)) with open(filename, 'w') as wfile: wfile.write(header) wfile.write(structure) wfile.write(neuron_models) wfile.write(synapse_models) wfile.write(parameters)
def get_projection(self, name): """ Returns the projection with the given *name*. :param name: name of the projection :return: The requested ``Projection`` object if existing, ``None`` otherwise. """ for proj in self.projections: if proj.name == name: return proj Global._print('get_projection(): the projection', name, 'does not exist in this network.') return None
def connect(self): # create fake LIL object to have the forward view in C++ try: from ANNarchy.core.cython_ext.Connector import LILConnectivity except Exception as e: Global._print(e) Global._error('ANNarchy was not successfully installed.') lil = LILConnectivity() lil.max_delay = self.max_delay lil.uniform_delay = self.uniform_delay self.connector_name = "Transpose" self.connector_description = "Transpose"
def _load_pop_data(pop, desc): """ Update a population with the stored data set. """ if not 'attributes' in desc.keys(): _error('Saved with a too old version of ANNarchy (< 4.2).', exit=True) for var in desc['attributes']: try: getattr(pop.cyInstance, 'set_'+var)(desc[var]) except: Global._warning('Can not load the variable ' + var + ' in the population ' + pop.name) Global._print('Skipping this variable.') continue
def _load_data(filename): " Internally loads data contained in a file" (path, fname) = os.path.split(filename) extension = os.path.splitext(fname)[1] desc = None if extension == '.mat': Global._error('Unable to load Matlab format.') return desc elif extension == '.gz': try: import gzip except: Global._error('gzip is not installed.') return desc try: with gzip.open(filename, mode = 'rb') as r_file: desc = pickle.load(r_file) except Exception as e: Global._print('Unable to read the file ' + filename) Global._print(e) return desc else: try: with open(filename, mode = 'rb') as r_file: desc = pickle.load(r_file) except Exception as e: Global._print('Unable to read the file ' + filename) Global._print(e) return desc return desc
def _check_locality(populations, projections): """ Checks that a global variable does not depend on local ones. """ for proj in projections: for var in proj.synapse_type.description['variables']: if var['locality'] == 'global': # cannot depend on local or semiglobal variables # Inside the equation for v in var['dependencies']: if _get_locality(v, proj.synapse_type.description) in ['local', 'semiglobal']: Global._print(var['eq']) Global._error('The global variable', var['name'], 'cannot depend on a synapse-specific/post-synaptic one:', v) # As pre/post dependencies deps = var['prepost_dependencies'] if len(deps['pre']) > 0 or len(deps['post']) > 0 : Global._print(proj.synapse_type.equations) Global._error('The global variable', var['name'], 'cannot depend on pre- or post-synaptic variables.') if var['locality'] == 'semiglobal': # cannot depend on pre-synaptic variables # Inside the equation for v in var['dependencies']: if _get_locality(v, proj.synapse_type.description) == 'local': Global._print(var['eq']) Global._error('The postsynaptic variable', var['name'], 'cannot depend on a synapse-specific one:', v) # As pre/post dependencies deps = var['prepost_dependencies'] if len(deps['pre']) > 0 : Global._print(proj.synapse_type.equations) Global._error('The postsynaptic variable', var['name'], 'cannot depend on pre-synaptic ones (e.g. pre.r).')
def _check_prepost(populations, projections): """ Checks that when a synapse uses pre.x r post.x, the variable x exists in the corresponding neuron """ for proj in projections: for dep in proj.synapse_type.description['dependencies']['pre']: if dep.startswith('sum('): target = re.findall(r'\(([\s\w]+)\)', dep)[0].strip() if not target in proj.pre.targets: Global._print(proj.synapse_type.equations) Global._error('The pre-synaptic population ' + proj.pre.name + ' receives no projection with the type ' + target) continue if not dep in proj.pre.attributes: Global._print(proj.synapse_type.equations) Global._error('The pre-synaptic population ' + proj.pre.name + ' has no variable called ' + dep) for dep in proj.synapse_type.description['dependencies']['post']: if dep.startswith('sum('): target = re.findall(r'\(([\s\w]+)\)', dep)[0].strip() if not target in proj.post.targets: Global._print(proj.synapse_type.equations) Global._error('The post-synaptic population ' + proj.post.name + ' receives no projection with the type ' + target) continue if not dep in proj.post.attributes: Global._print(proj.synapse_type.equations) Global._error('The post-synaptic population ' + proj.post.name + ' has no variable called ' + dep)
def _create(self): # create fake CSR object, just for compilation. try: from ANNarchy.core.cython_ext.Connector import CSR except Exception as e: Global._print(e) Global._error('ANNarchy was not successfully installed.') csr = CSR() csr.max_delay = self.delays csr.uniform_delay = self.delays self.connector_name = "Shared weights" self.connector_description = "Shared weights" self._store_connectivity(self._load_from_csr, (csr, ), self.delays)
def parse_expression(self, expression, local_dict): " Parses a string with respect to the vocabulary defined in local_dict." from sympy.parsing.sympy_parser import parse_expr, standard_transformations, convert_xor, auto_number, rationalize try: res = parse_expr(expression, local_dict=local_dict, transformations=(standard_transformations + (convert_xor, )), evaluate=False) except Exception as e: Global._print(e) Global._error('Can not analyse the expression :' + str(expression)) return res
def _create(self): # create fake LIL object, just for compilation. try: from ANNarchy.core.cython_ext.Connector import LILConnectivity except Exception as e: Global._print(e) Global._error('ANNarchy was not successfully installed.') lil = LILConnectivity() lil.max_delay = self.delays lil.uniform_delay = self.delays self.connector_name = "Copy" self.connector_description = "Copy projection" self._store_connectivity(self._load_from_lil, (lil, ), self.delays)
def connect_from_file(self, filename): """ Builds a connection pattern using data saved using the Projection.save_connectivity() method (not save()!). *Parameters*: * **filename**: file where the data was saved. .. note:: Only the ranks, weights and delays are loaded, not the other variables. """ # Create an empty CSR object csr = Connector.CSR() # Load the data from ANNarchy.core.IO import _load_data try: data = _load_data(filename) except Exception as e: Global._print(e) Global._error('connect_from_file(): Unable to load the data', filename, 'into the projection.') exit(0) # Load the CSR object try: csr.post_rank = data['post_ranks'] csr.pre_rank = data['pre_ranks'] if isinstance(data['w'], (int, float)): self._single_constant_weight = True csr.w = [[data['w']]] else: csr.w = data['w'] csr.size = data['size'] csr.nb_synapses = data['nb_synapses'] if data['delay']: csr.delay = data['delay'] csr.max_delay = data['max_delay'] csr.uniform_delay = data['uniform_delay'] except Exception as e: Global._print(e) Global._error('Unable to load the data', filename, 'into the projection.') exit(0) # Store the synapses self.connector_name = "From File" self.connector_description = "From File" self._store_connectivity(self._load_from_csr, (csr,), csr.max_delay if csr.uniform_delay > -1 else csr.delay) return self
def parse_expression(self, expression, local_dict): " Parses a string with respect to the vocabulary defined in local_dict." from sympy.parsing.sympy_parser import parse_expr, standard_transformations, convert_xor try: res = parse_expr(transform_condition(expression), local_dict = local_dict, transformations = (standard_transformations + (convert_xor,)), #evaluate=False ) except Exception as e: Global._print(e) Global._error('Can not analyse the expression :' + str(expression)) else: return res
def _load_from_matrix(self, pre, post, weights, delays, pre_post): csr = Connector.CSR() uniform_delay = not isinstance(delays, (list, np.ndarray)) if isinstance(delays, list): try: delays = np.array(delays) except: Global._error( 'connect_from_matrix(): You must provide a dense 2D matrix.') exit(0) if pre_post: # if the user prefers pre as the first index... weights = weights.T if isinstance(delays, np.ndarray): delays = delays.T shape = weights.shape if shape != (self.post.size, self.pre.size): if not pre_post: Global._error( "connect_from_matrix(): the matrix does not have the correct dimensions." ) Global._print('Expected:', (self.post.size, self.pre.size)) Global._print('Received:', shape) else: Global._error( "connect_from_matrix(): the matrix does not have the correct dimensions." ) Global._print('Expected:', (self.pre.size, self.post.size)) Global._print('Received:', shape) exit(0) for i in range(self.post.size): if isinstance(self.post, PopulationView): rk_post = self.post.ranks[i] else: rk_post = i r = [] w = [] d = [] for j in range(self.pre.size): val = weights[i, j] if val != None: if isinstance(self.pre, PopulationView): rk_pre = self.pre.ranks[j] else: rk_pre = j r.append(rk_pre) w.append(val) if not uniform_delay: d.append(delays[i, j]) if uniform_delay: d.append(delays) if len(r) > 0: csr.add(rk_post, r, w, d) return csr
def get_projection(self, name): """ Returns the projection with the given *name*. *Parameter*: * **name**: name of the projection *Returns:* * The requested ``Projection`` object if existing, ``None`` otherwise. """ for proj in self.projections: if proj.name == name: return proj Global._print('get_projection(): the projection', name, 'does not exist in this network.') return None
def parse(self): "Main method called after creating the object." try: if self.type == 'ODE': code = self.analyse_ODE(self.expression) elif self.type == 'cond': code = self.analyse_condition(self.expression) elif self.type == 'inc': code = self.analyse_increment(self.expression) elif self.type == 'return': code = self.analyse_return(self.expression) elif self.type == 'simple': code = self.analyse_assignment(self.expression) except Exception as e: Global._print(e) Global._error('Can not analyse', self.expression) return code
def implicit(self, expression): "Full implicit method, linearising for example (V - E)^2, but this is not desired." # print('Expression', expression) # Transform the gradient into a difference TODO: more robust... new_expression = expression.replace('d' + self.name, '_t_gradient_') new_expression = re.sub(r'([^\w]+)' + self.name + r'([^\w]+)', r'\1_' + self.name + r'\2', new_expression) new_expression = new_expression.replace( '_t_gradient_', '(_' + self.name + ' - ' + self.name + ')') # print('New Expression', new_expression) # Add a sympbol for the next value of the variable new_var = sp.Symbol('_' + self.name) self.local_dict['_' + self.name] = new_var # Parse the string analysed = self.parse_expression(new_expression, local_dict=self.local_dict) self.analysed = analysed # print('Analysed', analysed) # Solve the equation for delta_mp solved = sp.solve(analysed, new_var, check=False, rational=False) # print('Solved', solved) if len(solved) > 1: Global._print(self.expression) Global._error( 'Parser: the ODE is not linear, can not use the implicit method.' ) else: solved = solved[0] equation = sp.simplify(sp.collect(solved, self.local_dict['dt'])) # Obtain C code variable_name = self.c_code(self.local_dict[self.name]) explicit_code = Global.config['precision'] + ' _' + self.name + ' = '\ + self.c_code(equation) + ';' switch = variable_name + ' = _' + self.name + ' ;' # Return result return [{}, explicit_code, switch]
def parse(self): "Main method called after creating the object." try: if self.type == 'ODE': code = self.analyse_ODE(self.expression) elif self.type == 'cond': code = self.analyse_condition(self.expression) elif self.type == 'inc': code = self.analyse_increment(self.expression) elif self.type == 'return': code = self.analyse_return(self.expression) elif self.type == 'simple': code = self.analyse_assignment(self.expression) except Exception as e: Global._print(e) Global._error('Parser: cannot analyse', self.expression) return code
def parse(self): "Main method called after creating the object." # Check if the numerical method is the same for all ODEs methods = [] for var in self.variables: methods.append(var['method']) if len(list(set(methods))) > 1: # mixture of methods Global._print(methods) Global._error('Can not mix different numerical methods when solving a coupled system of equations.') else: method = methods[0] if method == 'implicit' or method == 'semiimplicit': return self.solve_implicit(self.expression_list) elif method == 'midpoint': return self.solve_midpoint(self.expression_list)
def _get_cython_attribute(self, attribute): """ Returns the value of the given attribute for all neurons in the population, as a NumPy array having the same geometry as the population if it is local. Parameter: * *attribute*: should be a string representing the variables's name. """ try: if attribute in self.neuron_type.description["local"]: return getattr(self.cyInstance, "get_" + attribute)().reshape(self.geometry) else: return getattr(self.cyInstance, "get_" + attribute)() except Exception as e: Global._print(e) Global._error(" the variable " + attribute + " does not exist in this population (" + self.name + ")")
def _create(self): """ create fake LIL object, just for compilation process :return: no return value """ try: from ANNarchy.core.cython_ext.Connector import LILConnectivity except Exception as e: Global._print(e) Global._error('ANNarchy was not successfully installed.') lil = LILConnectivity() lil.max_delay = self.delays lil.uniform_delay = self.delays self.connector_name = "Shared weights" self.connector_description = "Shared weights" self._store_connectivity(self._load_from_lil, (lil, ), self.delays)
def _init_attributes(self): """ Method used after compilation to initialize the attributes.""" self.initialized = True self.set(self.init) self.cyInstance.activate(self.enabled) self.cyInstance.reset() # If the spike population has a refractory period: if self.neuron_type.type == "spike" and self.neuron_type.description["refractory"]: if isinstance(self.neuron_type.description["refractory"], str): # a global variable try: self.refractory = eval("self." + self.neuron_type.description["refractory"]) except Exception as e: Global._print(e, self.neuron_type.description["refractory"]) Global._error("The initialization for the refractory period is not valid.") else: # a value self.refractory = self.neuron_type.description["refractory"]
def implicit(self, expression): "Full implicit method, linearising for example (V - E)^2, but this is not desired." # print('Expression', expression) # Transform the gradient into a difference TODO: more robust... new_expression = expression.replace('d'+self.name, '_t_gradient_') new_expression = re.sub(r'([^\w]+)'+self.name+r'([^\w]+)', r'\1_'+self.name+r'\2', new_expression) new_expression = new_expression.replace('_t_gradient_', '(_'+self.name+' - '+self.name+')') # print('New Expression', new_expression) # Add a sympbol for the next value of the variable new_var = Symbol('_'+self.name) self.local_dict['_'+self.name] = new_var # Parse the string analysed = self.parse_expression(new_expression, local_dict = self.local_dict ) self.analysed = analysed # print('Analysed', analysed) # Solve the equation for delta_mp solved = solve(analysed, new_var) # print('Solved', solved) if len(solved) > 1: Global._print(self.expression) Global._error('the ODE is not linear, can not use the implicit method.') else: solved = solved[0] equation = simplify(collect( solved, self.local_dict['dt'])) # Obtain C code variable_name = self.c_code(self.local_dict[self.name]) explicit_code = Global.config['precision'] + ' _' + self.name + ' = '\ + self.c_code(equation) + ';' switch = variable_name + ' = _' + self.name + ' ;' # Return result return [{}, explicit_code, switch]
def _load_from_matrix(self, pre, post, weights, delays, pre_post): csr = Connector.CSR() uniform_delay = not isinstance(delays, (list, np.ndarray)) if isinstance(delays, list): try: delays= np.array(delays) except: Global._error('connect_from_matrix(): You must provide a dense 2D matrix.') exit(0) if pre_post: # if the user prefers pre as the first index... weights = weights.T if isinstance(delays, np.ndarray): delays = delays.T shape = weights.shape if shape != (self.post.size, self.pre.size): if not pre_post: Global._error("connect_from_matrix(): the matrix does not have the correct dimensions.") Global._print('Expected:', (self.post.size, self.pre.size)) Global._print('Received:', shape) else: Global._error("connect_from_matrix(): the matrix does not have the correct dimensions.") Global._print('Expected:', (self.pre.size, self.post.size)) Global._print('Received:', shape) exit(0) for i in range(self.post.size): if isinstance(self.post, PopulationView): rk_post = self.post.ranks[i] else: rk_post = i r = [] w = [] d = [] for j in range(self.pre.size): val = weights[i, j] if val != None: if isinstance(self.pre, PopulationView): rk_pre = self.pre.ranks[j] else: rk_pre = j r.append(rk_pre) w.append(val) if not uniform_delay: d.append(delays[i,j]) if uniform_delay: d.append(delays) if len(r) > 0: csr.add(rk_post, r, w, d) return csr
def _get_cython_attribute(self, attribute): """ Returns the value of the given attribute for all neurons in the population, as a NumPy array having the same geometry as the population if it is local. *Parameter:* * **attribute**: should be a string representing the variables's name. """ try: if attribute in self.neuron_type.description['local']: data = getattr(self.cyInstance, 'get_'+attribute)() return data.reshape(self.geometry) else: return getattr(self.cyInstance, 'get_'+attribute)() except Exception as e: Global._print(e) Global._error(' the variable ' + attribute + ' does not exist in this population (' + self.name + ')')
def _load_from_sparse(self, pre, post, weights, delays): # Create an empty LIL object lil = LILConnectivity() # Find offsets if isinstance(self.pre, PopulationView): pre_ranks = self.pre.ranks else: pre_ranks = [i for i in range(self.pre.size)] if isinstance(self.post, PopulationView): post_ranks = self.post.ranks else: post_ranks = [i for i in range(self.post.size)] # Process the sparse matrix and fill the lil weights.sort_indices() (pre, post) = weights.shape if (pre, post) != (len(pre_ranks), len(post_ranks)): Global._print("ERROR: connect_from_sparse(): the sparse matrix does not have the correct dimensions.") Global._print('Expected:', (len(pre_ranks), len(post_ranks))) Global._print('Received:', (pre, post)) Global._error('Quitting...') for idx_post in range(post): idx_pre = weights.getcol(idx_post).indices w = weights.getcol(idx_post).data pr = [pre_ranks[i] for i in idx_pre] lil.add(post_ranks[idx_post], pr, w, [float(delays)]) return lil
def _analyse_equation(orig, eq, local_dict, tex_dict): # Analyse the left part left = eq.split('=')[0] split_idx = len(left) if left[-1] in ['+', '-', '*', '/']: op = left[-1] try: left = _analyse_part(left[:-1], local_dict, tex_dict) except Exception as e: Global._print(e) Global._warning('can not transform the left side of ' + orig +' to LaTeX, you have to do it by hand...') left = left[:-1] operator = " = " + left + " " + op + (" (" if op != '+' else '') operator = " \mathrel{" + op + "}= " else: try: left = _analyse_part(left, local_dict, tex_dict) except Exception as e: Global._print(e) Global._warning('can not transform the left side of ' + orig +' to LaTeX, you have to do it by hand...') operator = " = " # Analyse the right part try: right = _analyse_part(eq[split_idx+1:], local_dict, tex_dict) except Exception as e: Global._print(e) Global._warning('can not transform the right side of ' + orig +' to LaTeX, you have to do it by hand...') right = "\\textbf{TODO} %%" + eq[split_idx+1:] return left + operator + right + (" )" if operator.strip().endswith('(') else "")
def connect_one_to_one(self, weights=1.0, delays=0.0, force_multiple_weights=False): """ Builds a one-to-one connection pattern between the two populations. *Parameters*: * **weights**: initial synaptic values, either a single value (float) or a random distribution object. * **delays**: synaptic delays, either a single value or a random distribution object (default=dt). * **force_multiple_weights**: if a single value is provided for ``weights`` and there is no learning, a single weight value will be used for the whole projection instead of one per synapse. Setting ``force_multiple_weights`` to True ensures that a value per synapse will be used. """ if self.pre.size != self.post.size: Global._warning("connect_one_to_one() between", self.pre.name, 'and', self.post.name, 'with target', self.target) Global._print("\t the two populations have different sizes, please check the connection pattern is what you expect.") self.connector_name = "One-to-One" self.connector_description = "One-to-One, weights %(weight)s, delays %(delay)s" % {'weight': _process_random(weights), 'delay': _process_random(delays)} if isinstance(weights, (int, float)) and not force_multiple_weights: self._single_constant_weight = True self._store_connectivity( one_to_one, (weights, delays, "lil", "post_to_pre"), delays ) return self
def check_structure(self): """ Checks the structure to display more useful error messages. """ # Check populations for pop in self.populations: # Reserved variable names for term in ['t', 'dt', 't_pre', 't_post', 't_spike']: if term in pop.attributes: Global._print(pop.neuron_type.parameters) Global._print(pop.neuron_type.equations) Global._error(term + ' is a reserved variable name') exit(0) # Check projections for proj in self.projections: # Reserved variable names for term in ['t', 'dt', 't_pre', 't_post', 't_spike']: if term in proj.attributes: Global._print(proj.synapse_type.parameters) Global._print(proj.synapse_type.equations) Global._error(term + ' is a reserved variable name') exit(0) # Check the connector method has been called if not proj._connection_method: Global._error('The projection between populations', proj.pre.id, 'and', proj.post.id, 'has not been connected. Call a connector method before compiling the network.') exit(0) # Check existing pre variables for dep in proj.synapse_type.description['dependencies']['pre']: if dep.startswith('sum('): target = re.findall(r'\(([\s\w]+)\)', dep)[0].strip() if not target in proj.pre.targets: Global._error('The pre-synaptic population ' + proj.pre.name + ' receives no projection with the type ' + target) exit(0) continue if not dep in proj.pre.attributes: Global._error('The pre-synaptic population ' + proj.pre.name + ' has no variable called ' + dep) exit(0) for dep in proj.synapse_type.description['dependencies']['post']: if dep.startswith('sum('): target = re.findall(r'\(([\s\w]+)\)', dep)[0].strip() if not target in proj.post.targets: Global._error('The post-synaptic population ' + proj.post.name + ' receives no projection with the type ' + target) exit(0) continue if not dep in proj.post.attributes: Global._error('The post-synaptic population ' + proj.post.name + ' has no variable called ' + dep) exit(0)