For loop over section's segments in Python and HOC returns different values

When Python is the interpreter, what is a good
design for the interface to the basic NEURON
concepts.

Moderator: hines

Post Reply
ziemek
Posts: 45
Joined: Thu May 23, 2019 8:02 am
Location: Warsaw, Poland
Contact:

For loop over section's segments in Python and HOC returns different values

Post by ziemek »

Hi!

When I iterate over segments in a section using Python:

Code: Select all

[x for x in sec]
I got the following values: [0.166667, 0.5, 0.833333]

However if I iterate over the same section in HOC:

Code: Select all

for(x) {print x}
I got: [0, 0.166667, 0.5, 0.833333, 1]

So as you can see - the beginning and the end (0 and 1) are also added.

I know there is a function:

Code: Select all

sec.allseg()
but it maybe confusing if general iteration works as well.
ramcdougal
Posts: 267
Joined: Fri Nov 28, 2008 3:38 pm
Location: Yale School of Public Health

Re: For loop over section's segments in Python and HOC returns different values

Post by ramcdougal »

The short version, as you've figured out, is that your Python code and your HOC code are not equivalent; they do different things, but both languages support both types of loops.

Density mechanisms (like hh) exist on segments of positive surface area not on the 0 and 1 end points (which by definition have no area and thus cannot include anything that is proportional to area).

To loop over true segments in HOC, use for (x, 0):

Code: Select all

oc>create soma
oc>soma.nseg = 3
oc>for (x, 0) {print(x)}
0.16666667 
0.5 
0.83333333 
This is the equivalent of the Python for seg in sec:, as in:

Code: Select all

>>> from neuron import h
>>> soma = h.Section(name='soma')
>>> soma.nseg = 3
>>> for seg in soma:
...     print(seg.x)
... 
0.16666666666666666
0.5
0.8333333333333333
Point processes (like an IClamp), on the other hand, exist at points, so they're perfectly fine going onto the math for a zero area node like 0 or 1, or in any node (segment) of non-zero surface area. (You can specify any normalized position you want for a point process, but the mathematical approximation needed for any given simulation will put it to either an end point or to the center of a segment.)

To loop over all places where point processes mathematically can contribute their currents, use for (x) in HOC:

Code: Select all

oc>create soma
oc>soma.nseg = 3
oc>for (x) {print(x)}
0 
0.16666667 
0.5 
0.83333333 
1 
Or, equivalently, for seg in sec.allseg(): in Python:

Code: Select all

>>> from neuron import h
>>> soma = h.Section(name='soma')
>>> soma.nseg = 3
>>> for seg in soma.allseg():
...     print(seg.x)
... 
0.0
0.16666666666666666
0.5
0.8333333333333334
1.0
In particular, beware:

Changing parameters on a density mechanism at the 1 location will set them at the last node of positive area (since density mechanisms cannot exist at points). As a consequence, using for(x) instead of for(x, 0) to loop over segments to setup a conductance gradient within a section (e.g. gmax varying with distance from the soma) would be a logical error.

If you're a Python programmer trying to make sense of old HOC code, you may be interested in our HOC for reading knowledge slides which highlight some subtle points of possible confusion such as this one (which is on slide 30).
ziemek
Posts: 45
Joined: Thu May 23, 2019 8:02 am
Location: Warsaw, Poland
Contact:

Re: For loop over section's segments in Python and HOC returns different values

Post by ziemek »

Thanks for the fast and detailed answer!

However I work with Hay et al. 2011 model. In one file, they call distribute_channels() function:
https://senselab.med.yale.edu/modeldb/s ... hoc#tabs-2

Code: Select all

$o1.distribute_channels("apic","gIhbar_Ih",2,-0.8696,3.6161,0.0,2.0870,0.00010000000) 
 $o1.distribute_channels("apic","gCa_LVAstbar_Ca_LVAst",3,1.000000,0.010000,685.000000,885.000000,0.1419540000*1.6) 
which is defined as:
https://senselab.med.yale.edu/modeldb/s ... hoc#tabs-2

Code: Select all

proc distribute_channels()	{local dist,val,base,maxLength
	base = $8
	soma distance()
	maxLength = getLongestBranch($s1)

	forsec $s1		{
		if(0==strcmp($s2,"Ra")){
			Ra = $8
		} else {
			for(x) {
				if ($3==3) {
					dist = distance(x)
				} else {
					dist = distance(x)/maxLength
				}
				val = calculate_distribution($3,dist,$4,$5,$6,$7,$8)
				sprint(tstr,"%s(%-5.10f) = %-5.10f",$s2,x,val)
				execute(tstr)
			}
		}
	}
}
However both Ih and Ca_LVAst are not PointProcesses, but they did for(x) segments, so 0 and 1 ends included.

Is this an error, and if not - how to recreate this behavior in Python?

I rewrite their distribute_channels() function in Python:

Code: Select all

for x in sec.allseg():
   val = [...] # computation of the value
   mech_obj = getattr(x, mech)
   setattr(mech_obj, mech_param, val) 
I have an error, since insert() function didn't insert mechanisms to the 1-end (however it does to the 0-end):

Code: Select all

sec.insert("Ih")
sec.insert("Ca_LVAst")
ted
Site Admin
Posts: 6286
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: For loop over section's segments in Python and HOC returns different values

Post by ted »

Changing parameters on a density mechanism at the 1 location will set them at the last node of positive area (since density mechanisms cannot exist at points). As a consequence, using for(x) instead of for(x, 0) to loop over segments to setup a conductance gradient within a section (e.g. gmax varying with distance from the soma) would be a logical error.
Yes, and the result is a computational model whose properties differ from the modeler's conceptual model. The Poirazi and Mel pyramidal cell model, so widely reused by others, makes this very mistake.
Changing parameters on a density mechanism at the 1 location will set them at the last node of positive area (since density mechanisms cannot exist at points).
And of course specifying the value of a density mechanism's parameter at the 0 end of a section actually affects that parameter's value in the section's first segment, i.e. the segment that contains the node at 0. But if you are iterating over the section from the 0 end to the 1 end, the erroneous assignment is made first, but then is overridden by assignment of the correct value.

Regarding ziemek's latest question
However both Ih and Ca_LVAst are not PointProcesses, but they did for(x) segments, so 0 and 1 ends included.

Is this an error, and if not - how to recreate this behavior in Python?
Yes, it is an error, and it shouldn't be recreated in Python.

Think about it this way. The situation is directly analogous to wet lab experimentation in which there has been a lapse of proper methodology. If you picked up a wet lab experimental paper and found that the authors didn't use correct experimental methods, what should be your response? "Well, they screwed up experimentally, but we're interested in similar questions, so we're going to follow their example and screw up just like they did."

Really?

My own very strong opinion is

1. The error should be fixed, not recreated in Python. It should be fixed in the original hoc file, and tested to verify that the fixed model produces results that are qualitatively similar to the original buggy model, so the authors' original conclusions are not invalidated.

2. Any new model development, whether in hoc or Python, should use the corrected model specification. Why? If modeling played a significant role in the original paper, it was because the model was useful for evaluating a hypothesis posed by the authors. So the hypothesis was sufficiently complex that the authors didn't rely on their unaided intuition to infer the consequences of their assumptions. That's why they resorted to computational modeling. And that's why it is so important that there be a close match between the authors' conceptual model (hypothesis) and their computational model. Without such a match, results generated with the computational model cannot be relied on as a means for evaluating the hypothesis.

Someone might say, "Well, it's a small mistake, so probably it didn't have much of an effect on simulation results."

To which the reply is: that is an interesting but unsubstantiated assertion. Prove it. Fix the bug, repeat the simulations, and show that the results produced by the corrected model are qualitatively similar to those reported in the paper.

Who should do it? Ideally, the original authors. Will they? They ought to, but they might not. Does anybody in wet lab experimental neuroscience, who made some methodological error, go back years later and repeat their experiments with proper methodology? Maybe, but (1) have you ever seen a report of such work, (2) how will they get credit for their new effort, and (3) where will they get the $$ needed to properly redo the original experiments?
ramcdougal
Posts: 267
Joined: Fri Nov 28, 2008 3:38 pm
Location: Yale School of Public Health

Re: For loop over section's segments in Python and HOC returns different values

Post by ramcdougal »

Given that you're trying to make a Python version of the HOC... you could change the HOC to do it right using for (x,0), and then validate your Python against that.
Post Reply