Race condition with multiple artificial cell instances acces

NMODL and the Channel Builder.
Post Reply
sec6
Posts: 39
Joined: Sat Mar 15, 2008 10:32 am

Race condition with multiple artificial cell instances acces

Post by sec6 » Mon Aug 04, 2008 4:52 pm

I've written an artificial cell which generates spikes at times specified by an input data file.
The NMODL code just reads spike times out of a file, and calls net_send() once for each spike.
All of this happens at initialization time -- there's no actual NMODL code that executes
during the simulation run. All the heavy lifting is done by NEURON's event delivery system,
and the artificial cell does nothing but load up the event queue with spike times.

Code: Select all

NEURON	{ 
  ARTIFICIAL_CELL testRead
  RANGE cellID
}

PARAMETER {
	cellID = 0
}

ASSIGNED {
	q : remember -- this is a double, not a float
	qq
	ret
}

INITIAL {
	VERBATIM
		FILE *fp;
		fp = fopen("/usr/me/testData.txt","r");
		printf("testRead opened file\n");
	ENDVERBATIM
	ret = 1
	WHILE (ret == 1) {
		VERBATIM
			ret = fscanf(fp,"%lf",&q); //lf, not f, 'cause q is a double, not a float
			ret = fscanf(fp,"%lf",&qq);
		ENDVERBATIM
		IF (ret == 1) {
			printf("testRead read %lf,%lf from file\n",q,qq)
			IF (qq == cellID) {
				net_send(q,99)
			}
		}
	}
	VERBATIM
		fclose(fp);
		printf("testRead closed file\n");
	ENDVERBATIM
}

NET_RECEIVE (w) {
	IF (flag == 99) {
		net_event(t)
		printf("testRead event at t= %g\n",t)
	}
}
Here's the problem, though.
The model includes multiple instances of this artificial cell type.
Nothing, in principle, prevents multiple instances from reading the data file simultaneously.
On a single-cpu system, the INITIAL blocks of the cells probably execute one at a time,
but that seems like a pretty fragile way to prevent a race condition.
Moreover, the model will be run in threaded NEURON someday, and that'll break it for sure.

I thought, at first, the solution would be to put the main part of the code
inside a PROCEDURE, (call it queue_spikes() ) rather than the INITIAL block.
Then, in hoc, or python, I could do something like this:

Code: Select all

for cell in artCells: cell.queue_spikes()
Unfortunately, one can't put a net_send inside a PROCEDURE
(only inside an INITIAL or a NET_RECEIVE block).

An alternative approach, avoiding the race condition would be to read
the file in hoc or Python and pass it to the artificial cells
via a FUNCTION_TABLE. In some respects, this is preferable. True, it'd involve some
computational overhead for interpolation, which is wasted in this case.
More importantly, however, If I understand
correctly, this approach will be very memory inefficient -- i.e. all the spike times
will be kept in memory in the FUNCTION_TABLE *after* they've been copied into the event queue
and are no longer needed. (Essentially, there'll be two, redundant copies of the same data,
one in the FUNCTION_TABLE, and the other in the event queue.)
There are about 100,000 spikes, and someday, when the network's upscaled, there'll be more, so I think
memory efficiency's a legitimate consideration, here.

Can I attach vectors to a FUNCTION_TABLE, use the FUNCTION_TABLE, and then delete the vectors?
Obviously, I have to make sure my NMODL code never calls the FUNCTION_TABLE again after the
vectors have been deleted, but, assuming my program logic guarantees that, am I safe with this maneuver?

Code: Select all

from numpy import *
import neuron
import nrn
h = neuron.h
h.load_file("stdrun.hoc")

h.nrn_load_dll('./mod/i686/.libs/libnrnmech.so')  # Assume this loads a mechanism myArtCell with a FUNCTION_TABLE named spkTimeFunc
spkTimes = readSpikeTimesFromFile("./parameters/spkTimes.txt") # Assume this returns a 1-d numpy array.
numSpks = len(spkTimes)
spkTimeVec = h.Vector()
spkTimeVec.from_python(spkTimes)
spkNumVec = h.Vector()
spkNumVec.from_python(arange(numSpks))

h.table_spkTimeFunc_myArtCell(_ref_spkTimeVec.x(0), numSpks, _ref_spkNumVec.x(0)) # Is the ".x(0)" really necessary?  Seems a bit redundant.

h.stdinit()
del(spkTimeVec)
del(spkNumVec)
tstop = max(spkTimes)
h.continuerun(tstop)
... or is there some better way of doing this that I'm overlooking?

ted
Site Admin
Posts: 5702
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Race condition with multiple artificial cell instances acces

Post by ted » Tue Aug 05, 2008 9:22 am

If the goal is to stuff the event queue, why not do it all in hoc with an FInitializeHandler? During model setup, create a List nclist that contains all the NetCons that are to be driven, and read the spike time file into a Vector stvec. The FInitializeHandler could stuff the queue with all of the events that are to drive all the NetCons in nclist.

Code: Select all

fih = new FInitializeHandler("initspikes()")

// get it over with--stuff all spikes into the event queue
proc initspikes() { local i,j
  for i=0,nclist.count()-1 {
    for j=0,stvec.size()-1 {
      nclist.o(i).event(stvec.x[j])
    }
  }
}
An alternative to stuffing all the spike events into the event queue on initialization would be to place them in the queue with a "just in time" strategy.

Code: Select all

// put spike $1 into the event queue, then prepare to deal with the next one
proc putspike() { local i
  if ($1<stvec.size()) {
    for i=0,nclist.count()-1 nclist.o(i).event(stvec.x[$1])
    cvode.event(stvec.x[$1], putspike($1+1))
  }
}

// put the first spike into the event queue and get ready to deal with the next one
proc initspikes() { local i
  putspike(0)
}

sec6
Posts: 39
Joined: Sat Mar 15, 2008 10:32 am

Re: Race condition with multiple artificial cell instances acces

Post by sec6 » Tue Aug 05, 2008 5:32 pm

Thanks. I should have known there was a simpler way to do it.

(My questions are now moot, but I'm still curious about the answer to the first one, i.e. how to prevent race conditions between multiple instances of artificial cells sharing a common resource (like a file). Does anyone know an answer?)

Am I right in thinking I need to construct a "dummy" artificial cell that does nothing, in order to have a source for the Netcon?

ted
Site Admin
Posts: 5702
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Race condition with multiple artificial cell instances acces

Post by ted » Tue Aug 05, 2008 8:08 pm

how to prevent race conditions between multiple instances of artificial cells sharing a common resource (like a file).
Clever, often situation-specific, programming. You have a particular case in mind?
sec6 wrote:Am I right in thinking I need to construct a "dummy" artificial cell that does nothing, in order to have a source for the Netcon?
What does the Programmer's Reference documentation of the NetCon class say?

sec6
Posts: 39
Joined: Sat Mar 15, 2008 10:32 am

Re: Race condition with multiple artificial cell instances acces

Post by sec6 » Wed Aug 06, 2008 7:16 pm

What does the Programmer's Reference documentation of the NetCon class say?
It says:

Code: Select all

section netcon = new NetCon(&v(x), target)
netcon = new NetCon(source, target)
section netcon = new NetCon(&v(x), target, threshold, delay, weight)
netcon = new NetCon(source, target, threshold, delay, weight)
i.e. no legal syntax without both source & target specified.

It also says:
The target is allowed to be nil (NULLObject) in which case the NetCon is always inactive.
It doesn't say "the source is allowed to be nil"

So, source is not allowed to be nil, and therefore I do need a dummy artificial cell?
Clever, often situation-specific, programming. You have a particular case in mind?
Yes, the code at the top of this thread. However, that's now moot: I only asked from curiosity. Don't waste too much time on a hypothetical question.

ted
Site Admin
Posts: 5702
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Race condition with multiple artificial cell instances acces

Post by ted » Wed Aug 06, 2008 9:49 pm

sec6 wrote:It doesn't say "the source is allowed to be nil"
Five paragraphs below the mention that the "target is allowed to be nil (NULLObject)" you'll find a paragraph that starts
"The source may be a NULLObject. In this case events can only occur by calling event from hoc."
So go ahead and do
objref nil, nc
nc = new NetCon(nil, target)
where target is something that really exists and can respond to events.

Sorry, that was a trick question I asked, but your reply confirmed something I've been thinking about. I didn't see that paragraph either, the first time I wanted to use NetCon's event() method. At that time, my thought was that I missed it because of excess familiarity with NetCon's documentation--a case of "knowing what should be there," therefore not seeing what actually _was_ there.

But recently I have been thinking that others might have difficulty finding what they need to know about NetCon, even though this stuff is relatively fresh for them. NetCon is one of the more complex classes, not just in terms of syntax and usage, but also in terms of implications that the reader is assumed to know about. Consequently there are many different cases to consider, and it's easy to miss something when they are presented in a "flat" style. Maybe bullets or subheadings or indenting or judicious use of bold font might help draw attention to each of the cases.

sec6
Posts: 39
Joined: Sat Mar 15, 2008 10:32 am

Re: Race condition with multiple artificial cell instances acces

Post by sec6 » Thu Aug 07, 2008 10:59 am

I'm pleased to hear that I've been beta-testing the documentation, rather than wasting your time with silly questions, as I had begun to think.

I don't think there's a *general* solution here. Personally, I prefer very dry, "language definition" style documentation -- it may be hard to read, but it's compact, complete, and unambiguous. Most people don't like that, though. Ideally, there's a User's Manual, a Programmer's Manual, *and* a Language Definition (and maybe even a tutorial, too). No way NEURON's ever going to have documentation as extensive as that -- even if it got written, think of the labor involved in keeping it all up to date!

Therefore, I think the solution is to tweak the docs on a case-by-case basis based on user feedback, and let them evolve towards perfection. [BTW, what's the status of that Druple thing you mentioned?]

Actually, the solution to my problem was not "RTFM" but another of your precepts, namely "try it and see." This morning, before I saw your reply, I tried the following:

Code: Select all

oc>objref nil
oc>objref nc
oc>nc = new NetCon(nil,nil)
oc>nc.event(5)
	0 
oc>
Unfortunately, I'm trying to do this in Python, and, so far, when I try binding a hoc NULLObject to a Python identifier, I get the Python None object. That seems sensible, but when I pass None to the NetCon creator method, hoc complains that it's *not* NULLObject I'll chew on that one a little longer, and, if I don't find a solution, I may be posting the question over in the NEURON-Python subforum.

ted
Site Admin
Posts: 5702
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Race condition with multiple artificial cell instances acces

Post by ted » Thu Aug 07, 2008 12:47 pm

sec6 wrote:I'm pleased to hear that I've been beta-testing the documentation
All NEURON users are, by implicit definition, documentation beta testers, whether they know it or not. The pay is intangible, but it won't trigger the AMT, you can name your own hours, and there's no commute.
I prefer very dry, "language definition" style documentation -- it may be hard to read, but it's compact, complete, and unambiguous.
Right. UNIX man pages are probably a good example. And man pages make use of exactly the "style" features I mentioned: bullets, subheadings, indenting and occasional bold font.
I think the solution is to tweak the docs on a case-by-case basis based on user feedback, and let them evolve towards perfection. [BTW, what's the status of that Druple thing you mentioned?]
That's a good rationale for allowing comments. Drupal can allow comments, but the problem is how to automate the process of putting the Programmer's Reference into Drupal (a nontrivial task--note that Drupal's own site does not contain a "programmer's reference"). For the near term, we may have to cook up some kind of workaround, e.g. leave the reference material in its present form (static html), but with embedded links that take users to someplace where they can enter comments.

Aside from that, we have a test site that is about 80% complete in all other regards. Of course, that last 20% is turning out to require about 80% of the effort. I think it'll be ready for use by late September.
I'm trying to do this in Python, and, so far, when I try binding a hoc NULLObject to a Python identifier, I get the Python None object. That seems sensible, but when I pass None to the NetCon creator method, hoc complains that it's *not* NULLObject I'll chew on that one a little longer, and, if I don't find a solution, I may be posting the question over in the NEURON-Python subforum.
I can't help you there. Sounds like something that would be of general interest. If you do find a solution on your own, would you post it to the NEURON-Python subforum?

Post Reply