Changeset 955

Show
Ignore:
Timestamp:
04/22/11 18:04:21 (13 months ago)
Author:
apdavison
Message:

Cell types specified in 9ML can now have multiple synaptic channels (pyNN.neuron)

Location:
trunk
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • trunk/examples/nineml_neuron.py

    r947 r955  
     1""" 
     2Example of using a cell type defined in 9ML with pyNN.neuron 
     3""" 
     4 
     5 
    16import sys 
    2 sys.path.append("/home/andrew/dev/nineml_all/trunk/lib9ml/python/examples/AL/") 
    3 sys.path.append("/home/andrew/dev/nineml_all/trunk/code_generation/nmodl/") 
     7from os.path import abspath, realpath, join 
     8import nineml 
     9root = abspath(join(realpath(nineml.__path__[0]), "../../..")) 
     10sys.path.append(join(root, "lib9ml/python/examples/AL")) 
     11sys.path.append(join(root, "code_generation/nmodl"))                 
    412leaky_iaf = __import__("leaky_iaf") 
    513coba_synapse = __import__("coba_synapse") 
     
    715from pyNN.utility import init_logging 
    816import pyNN.neuron as sim 
     17from copy import deepcopy 
    918 
    1019init_logging(None, debug=True) 
     
    1423                                leaky_iaf.c1, 
    1524                                excitatory=coba_synapse.c1, 
    16                                 port_map=[('V', 'V'), ('Isyn', 'Isyn')]) 
     25                                inhibitory=deepcopy(coba_synapse.c1), 
     26                                port_map={ 
     27                                    'excitatory': [('V', 'V'), ('Isyn', 'Isyn')], 
     28                                    'inhibitory': [('V', 'V'), ('Isyn', 'Isyn')] 
     29                                }, 
     30                                weight_variables={ 
     31                                    'excitatory': 'q', 
     32                                    'inhibitory': 'q' 
     33                                }) 
    1734 
    1835parameters = { 
     
    2037    'gL': 50.0, 
    2138    't_ref': 5.0, 
    22     'tau': 2.0, 
     39    'excitatory_tau': 2.0, 
     40    'inhibitory_tau': 5.0, 
     41    'excitatory_E': 0.0, 
     42    'inhibitory_E': -70.0, 
    2343    'theta': -50.0, 
    2444    'vL': -65.0, 
     
    2646} 
    2747 
    28 #celltype = celltype_cls(parameters) 
    29 #cell = celltype.model(**celltype.parameters) 
    30  
    3148cells = sim.Population(1, celltype_cls, parameters) 
    3249cells.initialize('V', parameters['vL']) 
    3350cells.initialize('t_spike', -1e99) # neuron not refractory at start 
     51cells.initialize('regime', 1002) # temporary hack 
    3452 
    35 input = sim.Population(1, sim.SpikeSourcePoisson, {'rate': 100}) 
     53input = sim.Population(2, sim.SpikeSourcePoisson, {'rate': 100}) 
    3654 
    37 connector = sim.OneToOneConnector(weights=0.1, delays=0.5) 
    38 conn = sim.Projection(input, cells, connector, target='excitatory') 
     55connector = sim.OneToOneConnector(weights=1.0, delays=0.5) 
     56conn = [sim.Projection(input[0:1], cells, connector, target='excitatory'), 
     57        sim.Projection(input[1:2], cells, connector, target='inhibitory')] 
    3958 
    4059cells._record('V') 
    41 cells._record('g') 
     60cells._record('excitatory_g') 
     61cells._record('inhibitory_g') 
    4262cells.record() 
    4363 
     
    4565 
    4666cells.recorders['V'].write("Results/nineml_neuron.V", filter=[cells[0]]) 
    47 cells.recorders['g'].write("Results/nineml_neuron.g", filter=[cells[0]]) 
     67cells.recorders['excitatory_g'].write("Results/nineml_neuron.g_exc", filter=[cells[0]]) 
     68cells.recorders['inhibitory_g'].write("Results/nineml_neuron.g_inh", filter=[cells[0]]) 
    4869 
    4970sim.end() 
  • trunk/src/connectors.py

    r954 r955  
    442442        `weights` -- may either be a float, a RandomDistribution object, a list/ 
    443443                     1D array with at least as many items as connections to be 
    444                      created, or a DistanceDependence object. Units nA. 
     444                     created, or a distance expression as for `d_expression`. Units nA. 
    445445        `delays`  -- as `weights`. If `None`, all synaptic delays will be set 
    446446                     to the global minimum delay. 
  • trunk/src/neuron/nineml.py

    r950 r955  
    1 ## Not compatible with Python 2.4 ### 
    2 from __future__ import absolute_import 
     1""" 
     2Support cell types defined in 9ML with NEURON. 
     3 
     4Requires the 9ml2nmodl script to be on the path. 
     5 
     6Classes: 
     7    NineMLCell       - a single neuron instance 
     8    NineMLCellType   - base class for cell types, not used directly 
     9 
     10Functions: 
     11    nineml_cell_type - return a new NineMLCellType subclass 
     12 
     13Constants: 
     14    NMODL_DIR        - subdirectory to which NMODL mechanisms will be written 
     15     
     16""" 
     17 
     18from __future__ import absolute_import # Not compatible with Python 2.4 
    319import subprocess 
    420import neuron 
     
    723import logging 
    824import os 
     25import re 
    926from itertools import chain 
    1027 
     
    6077 
    6178 
    62 def _compile_nmodl(nineml_component): 
     79def _compile_nmodl(nineml_component, weight_variables): # weight variables should really be within component 
     80    """ 
     81    Generate NMODL code for the 9ML component, run "nrnivmodl" and then load 
     82    the mechanisms into NEURON. 
     83    """ 
    6384    if not os.path.exists(NMODL_DIR): 
    6485        os.makedirs(NMODL_DIR) 
     
    6990    nineml_component.write(xml_file) 
    7091    nineml2nmodl = __import__("9ml2nmodl") 
    71     nineml2nmodl.write_nmodl(xml_file) 
     92    nineml2nmodl.write_nmodl(xml_file, weight_variables) # weight variables should really come from xml file 
    7293    p = subprocess.check_call(["nrnivmodl"]) 
    7394    os.chdir(cwd) 
     
    7596 
    7697 
     98def _add_prefix(synapse_model, prefix, port_map): 
     99    """ 
     100    Add a prefix to all variables in `synapse_model`, except for variables with 
     101    receive ports and specified in `port_map`. 
     102    """ 
     103    synapse_model.__cache__ = {} 
     104    exclude = [] 
     105    new_port_map = [] 
     106    for name1, name2 in port_map: 
     107        if synapse_model.ports_map[name2].mode == 'recv': 
     108            exclude.append(name2) 
     109            new_port_map.append((name1, name2)) 
     110        else: 
     111            new_port_map.append((name1, prefix + '_' + name2)) 
     112    synapse_model.add_prefix(prefix + '_', exclude=exclude) 
     113    return new_port_map 
     114 
     115 
    77116class _build_nineml_celltype(type): 
    78117    """ 
     
    80119    """ 
    81120    def __new__(cls, name, bases, dct): 
    82         assert len(dct["synapse_models"]) == 1, "For now, can't handle multiple synapse models" 
    83         combined_model = join(dct["neuron_model"], 
    84                               dct["synapse_models"].values()[0], 
    85                               dct["port_map"], 
    86                               name=name) 
     121        # join the neuron and synapse components into a single component 
     122        combined_model = dct["neuron_model"] 
     123        for label in dct["synapse_models"].keys(): 
     124            port_map = dct["port_map"][label] 
     125            port_map = _add_prefix(dct["synapse_models"][label], label, port_map) 
     126            dct["weight_variables"][label] = label + "_" + dct["weight_variables"][label] 
     127            combined_model = join(combined_model, 
     128                                  dct["synapse_models"][label], 
     129                                  port_map, 
     130                                  name=name) 
    87131        dct["combined_model"] = combined_model 
     132        # set class attributes required for a PyNN cell type class 
    88133        dct["default_parameters"] = dict((name, 1.0) 
    89134                                      for name in combined_model.parameters) 
     
    92137        dct["synapse_types"] = dct["synapse_models"].keys() #really need an ordered dict 
    93138        dct["injectable"] = True # need to determine this. How?? 
    94         dct["recordable"] = [port.name for port in combined_model.analog_ports] + ['spikes'] 
     139        dct["recordable"] = [port.name for port in combined_model.analog_ports] + ['spikes', 'regime'] 
    95140        dct["standard_receptor_type"] = (dct["synapse_types"] == ('excitatory', 'inhibitory')) 
    96141        dct["conductance_based"] = True # how to determine this?? 
    97142        dct["model_name"] = name 
    98143        logger.debug("Creating class '%s' with bases %s and dictionary %s" % (name, bases, dct)) 
    99         _compile_nmodl(combined_model) 
     144        # generate and compile NMODL code, then load the mechanism into NEUORN 
     145        _compile_nmodl(combined_model, dct["weight_variables"]) # weight variables should really be stored within combined_model 
    100146        return type.__new__(cls, name, bases, dct) 
    101147     
    102148     
    103149 
    104 def nineml_cell_type(name, neuron_model, port_map={}, **synapse_models): 
     150def nineml_cell_type(name, neuron_model, port_map={}, weight_variables={}, **synapse_models): 
    105151    """ 
    106152    Return a new NineMLCellType subclass. 
     
    109155                                  {'neuron_model': neuron_model, 
    110156                                   'synapse_models': synapse_models, 
    111                                    'port_map': port_map}) 
    112      
     157                                   'port_map': port_map, 
     158                                   'weight_variables': weight_variables}) 
     159 
    113160 
    114161def join(c1, c2, port_map=[], name=None): 
     
    116163    logger.debug("Joining components %s and %s with port map %s" % (c1, c2, port_map)) 
    117164    logger.debug("New component will have name '%s'" % name) 
    118     bindings = [] # TODO: combine bindings from c1 and c2 
     165    # combine bindings from c1 and c2 
     166    bindings = {} 
     167    for b in chain(c1.bindings, c2.bindings): 
     168        bindings[b.name] = b 
     169    # combine ports (some will later be removed) 
    119170    all_ports = c1.ports_map.copy() 
    120171    all_ports.update(c2.ports_map) 
     172    # event ports do not be passed to the constructor, as they are attached to transitions 
    121173    for port_name, port in all_ports.items(): 
    122174        if isinstance(port, nineml.EventPort): 
    123175            all_ports.pop(port_name) 
     176    # connect ports. 
     177    # currently, when ports are connected they disappear. It might be better to 
     178    # explicitly keep the ports in the new component but mark them as connected 
    124179    for name1, name2 in port_map: 
    125         assert name1 in c1.ports_map 
    126         assert name2 in c2.ports_map 
    127         all_ports.pop(name1) 
    128         if name1 != name2: 
    129             #c2.substitute(name2, name1) # need to implement this 
    130             all_ports.pop(name2) 
    131          
     180        assert name1 in c1.ports_map, "%s is not in %s" % (name1, c1.ports_map.keys()) 
     181        assert name2 in c2.ports_map, "%s is not in %s" % (name2, c2.ports_map.keys()) 
     182 
    132183        port1 = c1.ports_map[name1] 
    133184        port2 = c2.ports_map[name2] 
     
    135186        if port1.mode == 'send': 
    136187            send_port = port1 
    137             port_name = name1 
     188            recv_port = port2 
     189            send_port_name = name1 
     190            recv_port_name = name2 
    138191        else: 
    139192            send_port = port2 
    140             port_name = name2 
     193            recv_port = port1 
     194            send_port_name = name2 
     195            recv_port_name = name1 
     196        # when connecting ports in which the send port has an expression, need 
     197        # to create a binding for this expression in the new component 
    141198        if send_port.expr: 
    142199            func_args = c1.non_parameter_symbols.union(c2.non_parameter_symbols).intersection(send_port.expr.names) 
    143             lhs = "%s(%s)" % (port_name, ",".join(func_args)) 
    144             bindings.append(nineml.Binding(lhs, send_port.expr.rhs)) 
     200            lhs = "%s(%s)" % (send_port_name, ",".join(func_args)) 
     201            send_binding = nineml.Binding(lhs, send_port.expr.rhs) 
     202            bindings[send_binding.name] = send_binding 
    145203            for eq in chain(c1.equations, c2.equations): 
    146                 if port_name in eq.names: 
    147                     eq.rhs = eq.rhs_name_transform({port_name: lhs}) 
     204                if send_port_name in eq.names: 
     205                    eq.rhs = eq.rhs_name_transform({send_port_name: lhs})             
     206            if recv_port.mode == 'reduce': 
     207                # need to retain reduce ports as they can be connected to in a future join 
     208                if recv_port_name in bindings: 
     209                    # this reduce port has already been connected to, so combine using its reduce_op 
     210                    reduce_binding = bindings[recv_port_name] 
     211                    func_args = func_args.union(reduce_binding.args) 
     212                    lhs = "%s(%s)" % (recv_port_name, ",".join(func_args)) 
     213                    rhs = recv_port.reduce_op.join([reduce_binding.rhs, send_binding.lhs]) 
     214                else: 
     215                    # this is the first time this reduce port has been connected to 
     216                    lhs = "%s(%s)" % (recv_port_name, ",".join(func_args)) 
     217                    rhs = send_binding.lhs 
     218                bindings[recv_port_name] = nineml.Binding(lhs, rhs) 
     219                recv_port.connected = True 
     220            else: 
     221                all_ports.pop(name1) 
     222        else: 
     223            if recv_port.mode == 'reduce': 
     224                raise NotImplementedError 
     225            else: 
     226                all_ports.pop(name1) 
     227 
     228        if name1 != name2: 
     229            #c2.substitute(name2, name1) # need to implement this. Currently this all only works if name1 == name2 
     230                                         # probably needs to happen sooner in the function 
     231            all_ports.pop(name2) 
     232 
     233    # where parameters have become bindings due to connecting ports, replace 
     234    # bare names with function calls in the equations 
     235    for bname, binding in bindings.items(): 
     236        for eq in chain(c1.equations, c2.equations): 
     237            if bname in eq.names: 
     238                print "#### replacing %s by %s" % (bname, binding.lhs) 
     239                pattern = re.compile(r'%s(\([\w\, ]*\))?' % bname) 
     240                m = pattern.search(eq.rhs) 
     241                if m: 
     242                    eq.rhs = pattern.sub(binding.lhs, eq.rhs) 
     243                else: 
     244                    eq.rhs = eq.rhs_name_transform({bname: binding.lhs}) 
     245 
     246    # create new regimes from all possible combinations of the regimes from the 
     247    # two components 
    148248    regime_map = {} 
    149249    for r1 in c1.regimes: 
    150250        regime_map[r1.name] = {} 
    151251        for r2 in c2.regimes: 
    152             kwargs = {'name': "%s_AND_%s" % (r1.name, r2.name)} 
     252            if r1.name == r2.name: 
     253                new_name = r1.name 
     254            else: 
     255                new_name = "%s_AND_%s" % (r1.name, r2.name) 
     256            kwargs = {'name': new_name} 
    153257            new_regime = nineml.Regime(*r1.nodes.union(r2.nodes), **kwargs) 
    154258            regime_map[r1.name][r2.name] = new_regime 
     259    # create transitions between all the new regimes 
    155260    transitions = [] 
    156261    for r1 in c1.regimes: 
     
    168273                                                   condition=t.condition) 
    169274                transitions.append(new_transition) 
     275 
    170276    regimes = [] 
    171277    for d in regime_map.values(): 
     
    176282                            transitions=transitions, 
    177283                            ports=all_ports.values(), 
    178                             bindings=bindings) 
     284                            bindings=bindings.values()) 
  • trunk/src/nineml/read.py

    r790 r955  
    188188    """ 
    189189     
    190     def __init__(self, sim, nineml_file): 
     190    def __init__(self, sim, nineml_model): 
    191191        """ 
    192192        Instantiate a network from a 9ML file, in the specified simulator. 
    193193        """ 
    194194        global random_distributions 
    195         assert isinstance(nineml_file, basestring) 
    196195        self.sim = sim 
    197         self.nineml_model = nineml.parse(nineml_file) 
     196        if isinstance(nineml_model, basestring): 
     197            self.nineml_model = nineml.parse(nineml_model) 
     198        elif isinstance(nineml_model, nineml.Model): 
     199            self.nineml_model = nineml_model 
     200        else: 
     201            raise TypeError("nineml_model must be a nineml.Model instance or the path to a NineML XML file.") 
    198202        self.random_distributions = {} 
    199203        self.assemblies = {} 
     
    207211    def _handle_group(self, group): 
    208212        # create an Assembly 
    209         self.assemblies[group.name] = self.sim.Assembly(group.name) 
     213        self.assemblies[group.name] = self.sim.Assembly(label=group.name) 
    210214         
    211215        # extract post-synaptic response definitions from projections