# Synaptic Weight Conversion From Brian1 to Brian2

**URL:** <https://brian.discourse.group/t/synaptic-weight-conversion-from-brian1-to-brian2/862>\
**Category:** Support\
**Tags:** synapses, brian1\
**Created:** [12 January 2023 20:02 UTC](https://brian.discourse.group/t/synaptic-weight-conversion-from-brian1-to-brian2/862 "2023-01-12T20:02:28Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![luol3](https://avatars.discourse-cdn.com/v4/letter/l/82dd89/32.png) [@luol3](https://brian.discourse.group/u/luol3)\
**Post date:** [12 January 2023 20:02 UTC](https://brian.discourse.group/t/synaptic-weight-conversion-from-brian1-to-brian2/862/1 "2023-01-12T20:02:28Z")

</div>

# Description of problem

I am trying to reproduce the model from Klaus Wimmer’s 2015 paper (Sensory integration dynamics in a hierarchical network explains choice probabilities in cortical area MT) in Brian2, and all of the Brian1 code is publicly available on ModelDB here: [https://senselab.med.yale.edu/ModelDB/showmodel.cshtml?model=168867&file=/hierarchical\_network/sensory\_circuit.py#tabs-2](https://senselab.med.yale.edu/ModelDB/showmodel.cshtml?model=168867&file=/hierarchical_network/sensory_circuit.py#tabs-2)  
When trying to convert the synapse weights from Brian1 to Brian2, I end up with the following error: ‘The identifier “max” could not be resolved.’

# Minimal code to reproduce problem

Wimmer’s Brian1 code is:

```auto
C_SE_SE = Connection(sensoryE, sensoryE, 'xe', delay=True, max_delay=1.5 * ms)
C_SE_SE.connect_random(sensoryE1, sensoryE1, sparseness=p, weight=lambda:w_p * gEE/gLeakE * max(0.0, 1.0 + 0.5 * randn()), delay=dE) 

```

My Brian2 translation is

```auto
C_SE1_SE1 = Synapses(sensoryE1, sensoryE1, 'w: siemens', on_pre='ye+=w') 
C_SE1_SE1.connect(p=0.2) 
#C_SE1_SE1.w=max(0.0, w_p*gEE/gLeakE*(1.0 + 0.5 * randn()))*nS
C_SE1_SE1.w='w_p*gEE/gLeakE*max(0.0, 1.0 + 0.5 * randn())*nS' # This is the string that throws an error 
C_SE1_SE1.delay='randn()*1.5*ms'

```

# What you have already tried

If `C_SE1_SE1.w='w_p*gEE/gLeakE*max(0.0, 1.0 + 0.5 * randn())*nS'` isn’t a string, then the code runs fine but all of the synaptic weights have the same value. However, I’m pretty sure the line above is a function.

# Full traceback of error (if relevant)

```pytb
Traceback (most recent call last)
Cell In[7], line 5
      3 C_SE1_SE1.connect(p=0.2) #I think you need to separate these guys into different groups as the weights are different. 
      4 #C_SE1_SE1.w=max(0.0, w_p*gEE/gLeakE*(1.0 + 0.5 * randn()))*nS
----> 5 C_SE1_SE1.w='w_p*gEE/gLeakE*max(0.0, 1.0 + 0.5 * randn())*nS'
      6 C_SE1_SE1.delay='randn()*1.5*ms'
      7 #This works without putting anything into randn, but it doesn't when you turn it into a string? 

File ~/Library/Python/3.11/lib/python/site-packages/brian2/groups/group.py:420, in VariableOwner. __setattr__ (self, name, val, level)
    418 raise TypeError(f'Variable {name} is read-only.')
    419 # Make the call X.var = ... equivalent to X.var[:] = ...
--> 420 var.get_addressable_value_with_unit(name, self).set_item(slice(None),
    421 val,
    422 level=level+1)
    423 elif len(name) and name[-1]=='_' and name[:-1] in self.variables:
    424 # no unit checking
    425 var = self.variables[name[:-1]]

File ~/Library/Python/3.11/lib/python/site-packages/brian2/core/variables.py:873, in VariableView.set_item(self, item, value, level, namespace)
    870 # Both index and values are strings, use a single code object do deal
    871 # with this situation
    872 if isinstance(value, str) and isinstance(item, str):
--> 873 self.set_with_expression_conditional(item, value,
    874 check_units=check_units,
    875 run_namespace=namespace)
    876 elif isinstance(item, str):
    877 try:

File ~/Library/Python/3.11/lib/python/site-packages/brian2/core/base.py:293, in device_override.<locals>.device_override_decorator.<locals>.device_override_decorated_function(*args, **kwds)
    291 return getattr(curdev, name)(*args, **kwds)
    292 else:
--> 293 return func(*args, **kwds)

File ~/Library/Python/3.11/lib/python/site-packages/brian2/core/variables.py:1027, in VariableView.set_with_expression_conditional(self, cond, code, run_namespace, check_units)
   1025 from brian2.devices.device import get_device
   1026 device = get_device()
-> 1027 codeobj = create_runner_codeobj(self.group,
   1028 {'condition': abstract_code_cond,
   1029 'statement': abstract_code},
   1030 'group_variable_set_conditional',
   1031 additional_variables=variables,
   1032 check_units=check_units,
   1033 run_namespace=run_namespace,
   1034 codeobj_class=device.code_object_class(fallback_pref='codegen.string_expression_target'))
   1035 codeobj()

File ~/Library/Python/3.11/lib/python/site-packages/brian2/codegen/codeobject.py:352, in create_runner_codeobj(group, code, template_name, run_namespace, user_code, variable_indices, name, check_units, needed_variables, additional_variables, template_kwds, override_conditional_write, codeobj_class)
    349 needed_variables = []
    350 # Resolve all variables (variables used in the code and variables needed by
    351 # the template)
--> 352 variables = group.resolve_all(sorted(identifiers | set(needed_variables) | set(template_variables)),
    353 # template variables are not known to the user:
    354 user_identifiers=user_identifiers,
    355 additional_variables=additional_variables,
    356 run_namespace=run_namespace)
    357 # We raise this error only now, because there is some non-obvious code path
    358 # where Jinja tries to get a Synapse's "name" attribute via syn['name'],
    359 # which then triggers the use of the `group_get_indices` template which does
    360 # not exist for standalone. Putting the check for template == None here
    361 # means we will first raise an error about the unknown identifier which will
    362 # then make Jinja try syn.name
    363 if template is None:

File ~/Library/Python/3.11/lib/python/site-packages/brian2/groups/group.py:731, in Group.resolve_all(self, identifiers, run_namespace, user_identifiers, additional_variables)
    729 resolved = {}
    730 for identifier in identifiers:
--> 731 resolved[identifier] = self._resolve(identifier,
    732 user_identifier=identifier in user_identifiers,
    733 additional_variables=additional_variables,
    734 run_namespace=run_namespace)
    735 return resolved

File ~/Library/Python/3.11/lib/python/site-packages/brian2/groups/group.py:691, in Group._resolve(self, identifier, run_namespace, user_identifier, additional_variables)
    687 return resolved_internal
    689 # We did not find the name internally, try to resolve it in the external
    690 # namespace
--> 691 return self._resolve_external(identifier, run_namespace=run_namespace)

File ~/Library/Python/3.11/lib/python/site-packages/brian2/groups/group.py:814, in Group._resolve_external(self, identifier, run_namespace, user_identifier, internal_variable)
    812 else:
    813 error_msg = f'The identifier "{identifier}" could not be resolved.'
--> 814 raise KeyError(error_msg)
    816 elif len(matches) > 1:
    817 # Possibly, all matches refer to the same object
    818 first_obj = matches[0][1]

KeyError: 'The identifier "max" could not be resolved.'

```

---

<div class="post-metadata">

**Author:** ![mstimberg](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/mstimberg/32/11_2.png) [@mstimberg](https://brian.discourse.group/u/mstimberg)\
**Post date:** [13 January 2023 10:47 UTC](https://brian.discourse.group/t/synaptic-weight-conversion-from-brian1-to-brian2/862/2 "2023-01-13T10:47:16Z")

</div>

Hi @luol3. Your basic approach is correct: in Brian 1, you’d specify synaptic weights that are calculated for each synapse via a function (or a lambda expression, which is the same thing), whereas in Brian 2, you’d use a string expression ([Synapses (Brian 1 –\> 2 conversion) — Brian 2 2.5.1 documentation](https://brian2.readthedocs.io/en/stable/introduction/brian1_to_2/synapses.html#connections-defined-by-functions)).

These two often look very similar. One difference though is that in the Brian 1 method you can refer to arbitrary Python functions, while in Brian 2 you are restricted to the functions provided here: [Functions — Brian 2 2.5.1 documentation](https://brian2.readthedocs.io/en/stable/advanced/functions.html#default-functions) (this is because Brian 2 will not necessarily execute this code in Python, but it might be C++, etc.). As you can see, this list does not include the `max` function. Luckily, the `max` function in this code is actually used to do clipping (the weights should not become negative), and Brian 2 comes with a dedicated function for that purpose: `clip`. Therefore, the following string should work:

```python
C_SE1_SE1.w='w_p*gEE/gLeakE*clip(1.0 + 0.5 * randn(), 0.0, inf)*nS'

```

A quick explanation why it seemingly works if you don’t put things into the string, but leads to a single value for all synapses: the expression provided in the lambda (Brian 1) or in the string (Brian 2), gets evaluated for each of the synapses individually, therefore `rand()` gives a different random number each time. When you instead call the same expression directly from Python, as you did in your commented line, `randn()` will return a single number. This means that Brian will receive something like `C_SE1_SE1.w = 1.294`, i.e. a single value for all the weights.

Hope that helps!  
Marcel

---

<div class="post-metadata">

**Author:** ![luol3](https://avatars.discourse-cdn.com/v4/letter/l/82dd89/32.png) [@luol3](https://brian.discourse.group/u/luol3)\
**Post date:** [13 January 2023 20:01 UTC](https://brian.discourse.group/t/synaptic-weight-conversion-from-brian1-to-brian2/862/3 "2023-01-13T20:01:57Z")

</div>

Thank you for your help, @mstimberg. That line of code worked.

Later in Wimmer’s code ([ModelDB: Hierarchical network model of perceptual decision making (Wimmer et al 2015)](https://senselab.med.yale.edu/ModelDB/showmodel.cshtml?model=168867&file=%2fhierarchical_network%2fintegration_circuit.py#tabs-2)), there is a line that displays a connection (C\_DE\_DI\_AMPA = Connection(decisionE, decisionI, ‘gea’, weight = gEI\_AMPA / gLeakI, delay = d)) that seems to have no connectivity properties and I’m not sure how to convert that into Brian2.

---

<div class="post-metadata">

**Author:** ![mstimberg](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/mstimberg/32/11_2.png) [@mstimberg](https://brian.discourse.group/u/mstimberg)\
**Post date:** [13 January 2023 20:30 UTC](https://brian.discourse.group/t/synaptic-weight-conversion-from-brian1-to-brian2/862/4 "2023-01-13T20:30:20Z")

</div>

> [@luol3](#):
>
> Later in Wimmer’s code ([ModelDB: Hierarchical network model of perceptual decision making (Wimmer et al 2015)](https://senselab.med.yale.edu/ModelDB/showmodel.cshtml?model=168867&file=%2fhierarchical_network%2fintegration_circuit.py#tabs-2)), there is a line that displays a connection (C\_DE\_DI\_AMPA = Connection(decisionE, decisionI, ‘gea’, weight = gEI\_AMPA / gLeakI, delay = d)) that seems to have no connectivity properties and I’m not sure how to convert that into Brian2.

My Brian 1 knowledge is a bit rusty by now, but I think if you defined a `weight` directly in the `Connection` constructor, this meant that it should connect things with all-to-all connectivity. In Brian 2, this means you’ll have to call `C_DE_DI_AMPA.connect()`.

Just to be sure, I looked it up and I was correct 😉 : [Connections — Brian 1.4.4 documentation](https://brian.readthedocs.io/en/stable/connections.html#simple-connections)
