Bulletin Board Style

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

Moderator: hines

Post Reply
Ruben Moor
Posts: 6
Joined: Tue Nov 17, 2009 9:02 am

Bulletin Board Style

Post by Ruben Moor »

Hello,

I want to use the Bulletin Board Style system of ParallelContext of Neuron with embedded Python. However my function definition:

Code: Select all

def function():
    ...
is not recognized by the submit command:

Code: Select all

pc.submit("function")
How can I expose my python defined function to the hoc-namespace?


Running the command
mpiexec -np 4 nrniv -python -mpi paralleltest.py
recieving error:
1 nrniv: function undefined function
ted
Site Admin
Posts: 6289
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Bulletin Board Style

Post by ted »

How about
pc.submit("nrnpython(function)")
?
Ruben Moor
Posts: 6
Joined: Tue Nov 17, 2009 9:02 am

Re: Bulletin Board Style

Post by Ruben Moor »

Code: Select all

pc.submit("nrnpython(function)")
yields the following error output:
nrnpython(function) not a function in NULLobject
hines
Site Admin
Posts: 1682
Joined: Wed May 18, 2005 3:32 pm

Re: Bulletin Board Style

Post by hines »

Forgot about that.
I need to allow the pc.submit to take python callbacks in the same way as is done for
the gui routines, FInitializeHandler, etc. For now create a hoc stub function that you pass
and have the stub function return the value of the call to the python function using
a hoc PythonObject, (either temporary or global).
Ruben Moor
Posts: 6
Joined: Tue Nov 17, 2009 9:02 am

Re: Bulletin Board Style

Post by Ruben Moor »

Thanks for the help.

Now I and up with an entirely different problem. I have not found anything on this here. It is about my stub function definition in python:

Code: Select all

h('func task() { return $1 * $1 }')
works fine. Unfortunately the result of my jobs is a vector, which needs to be posted. So I end up with a function definition over multiple lines which caused problems.

This first example works fine:

Code: Select all

h("""func task() { return $1 * $1 }""")
But this one not:

Code: Select all

h("""func task() { return $1 * $1 
}""")
Neither does this work:

Code: Select all

h('func task() { local myid\nmyid = hoc_ac_\nobjref vec\nvec = new Vector(3, 1)\npc.post(myid, vec)\n}')
Actually any statement separator would solve this issue.
hines
Site Admin
Posts: 1682
Joined: Wed May 18, 2005 3:32 pm

Re: Bulletin Board Style

Post by hines »

An example of a stub function I was thinking about is

Code: Select all

from neuron import h
h('''objref po
po = new PythonObject()
func stub() {\
  return po.pyfun($1)\
}
''')
def pyfun(arg):
  return  arg*arg

print h.stub(2)
Since you are returning something fairly elaborate, You could insteaad use the statement form indicated by Ted above
and use the post, take mechanisms. For the statement form, the last character must be \n, so
'nrnpython("pythoncall()")\n'
hines
Site Admin
Posts: 1682
Joined: Wed May 18, 2005 3:32 pm

Re: Bulletin Board Style

Post by hines »

Here is a python example for a function that takes a vector argument, reverses it and posts a result vector.
Again, a hoc stub function is used to help with the callback.
It may be a while before I get to pure python callbacks. Mostly because I expect the typical
usage is the requirement of handling vectors and (without a lot of programming effort on my part)
those have to be hoc vectors.
The output of a three core run is:

Code: Select all

$ mpiexec -n 3 nrniv -mpi -python submitlistold.py
numprocs=3
NEURON -- VERSION 7.2 (371:249faaed5faa) 2009-11-13
Duke, Yale, and the BlueBrain Project -- Copyright 1984-2008
See http://www.neuron.yale.edu/credits.html

i= 0 from host 0 [0.0, 1.0, 2.0, 3.0] [3.0, 2.0, 1.0, 0.0]
i= 3 from host 0 [300.0, 301.0, 302.0, 303.0] [303.0, 302.0, 301.0, 300.0]
i= 2 from host 2 [200.0, 201.0, 202.0, 203.0] [203.0, 202.0, 201.0, 200.0]
i= 1 from host 1 [100.0, 101.0, 102.0, 103.0] [103.0, 102.0, 101.0, 100.0]
i= 4 from host 2 [400.0, 401.0, 402.0, 403.0] [403.0, 402.0, 401.0, 400.0]
And here is the code. Some things to note are that vector contents are copied to the
bulletin board and the original is available despite the modification of the copy when
it reaches f. Also a python quit() doesn't clean up properly and one gets an error message
on exit which is avoided with h.quit()

Code: Select all

from neuron import h
from time import sleep
pc = h.ParallelContext()

def f(i, hvec): 
  sleep(.1)
  hvec.reverse()
  pc.post(i, pc.id(), hvec)
  return hvec.sum()           

h('''objref po
po = new PythonObject()
func f() { return po.f($1, $o2) }
''')

pc.runworker()

for i in range(5):
  pc.submit('f', i, h.Vector(4).indgen().add(i*100))
  
x = 0
while pc.working():
  i = int(pc.upkscalar()) #the first arg of f
  va = pc.upkvec()   #the second arg of f
  x += pc.retval()
  pc.take(i)
  pid = int(pc.upkscalar()) 
  v = pc.upkvec()
  print 'i=', i,  'from host', pid, list(va), list(v) 

pc.done()
h.quit()
Ruben Moor
Posts: 6
Joined: Tue Nov 17, 2009 9:02 am

Re: Bulletin Board Style

Post by Ruben Moor »

Thanks a lot for your great efforts!

Your example is very clarifying and it finally works on my machine.
hines
Site Admin
Posts: 1682
Joined: Wed May 18, 2005 3:32 pm

Re: Bulletin Board Style

Post by hines »

It may be a while before I get to pure python callbacks
http://www.neuron.yale.edu/hg/neuron/nr ... 5ae43de640 has been committed that
supports a more Pythonic style when using the ParallelContext bulletin board. The help
file has also been updated, see
http://www.neuron.yale.edu/neuron/stati ... arcon.html
In summary:
When using the Bulletin board with Python, the methods submit , context , pack , and post have been augmented and pyret and upkpyobj have been introduced to allow a more Pythonic style. I.e. The executable string for submit and context may be replaced by a Python callable that returns a Python Object (retrieved with pyret), the args to submit, context, pack, and post may be Python Objects, and a bulletin board message value which is a Python Object may be retrieved with upkpyobj.

It may be the case that further performance tweaking might be necessary. In particular, I worry that when the master is excuting Python, the bulletin board server
will be starved and the workers will not be able to communicate in a timely fashion. In the hoc implementation, the server is polled every few hoc statements to see
if there are any MPI messages to handle and this is not done when python is getting 100% of the cpu time. I don't expect a problem if python is regularly executing
something within hoc.
wwlytton
Posts: 66
Joined: Wed May 18, 2005 10:37 pm
Contact:

Re: Bulletin Board Style

Post by wwlytton »

the following works for pack and unpack in python

Code: Select all

tvec = h.Vector().from_python([1.2,3.3,4.7]) # ersatz vec of spike times
myid=8
pc.pack([myid],tvec.as_numpy()) # turn myid and vec into python objects when packing
pc.post('test')  
pc.take('test')
upmyid=pc.upkpyobj()[0] # grab the myid out of the 1 element array
uptvec=h.Vector().from_python(pc.upkpyobj())   # grab the vector by copying from the array
uptvec.printf() # 1.2	3.3	4.7
However when I put in a parallel context I get errors -- I'm not sure if I'm correctly using pc.look() in while loop here though??

mpiexec -n 4 nrniv -python -mpi test.py
numprocs=4
NEURON -- VERSION 7.4 (1187:c12e80e9f5fe) 2014-11-04 ...
0 unpack size=320 upkpos=55 type[0]=1 datatype=4 type[1]=25 count=25
Assertion failed: file /usr/site/nrniv/nrn/src/nrnmpi/bbsmpipack.c, line 64
0 nrniv: type[0] == my_datatype
0 near line 0
0 objref hoc_obj_[2]
^
0 ParallelContext[0].upkpyobj()

Code: Select all

# mpiexec -n 4 nrniv -python -mpi test.py
from neuron import h
pc = h.ParallelContext()
myid = int(pc.id())
nhost = int(pc.nhost())

def storevecs ():
  pc.pack([myid],h.Vector().from_python(range(myid,100,nhost))) # ersatz vec of spike times
  pc.post('test')

def getvecs ():
  while pc.look('test')==1: # only take things that have been posted
    pc.take('test')
    id=pc.upkpyobj()[0]
    uptvec=h.Vector().from_python(pc.upkpyobj())   # grab the vector by copying from the array
    print id,
    uptvec.printf()

def do ():
  storevecs()
  pc.runworker()
  pc.done()
  getvecs()
  h.quit()

do()
hines
Site Admin
Posts: 1682
Joined: Wed May 18, 2005 3:32 pm

Re: Bulletin Board Style

Post by hines »

You are packing a HOC Vector but trying to unpack it as a python object. It will work if you

Code: Select all

  pc.pack([myid],h.Vector().from_python(range(myid,100,nhost)).as_numpy())
Although, in this example you could just pack the 'range(myid,100,nhost)' as a python list.

The intention of 'pc.done()' is to signal that you are finished with the bulletin board and the workers can exit. So I would prefer you write

Code: Select all

  getvecs()
  pc.done()
Since only the master is taking things from the bulletin board, it is safe to do a look followed by a take. In general, though if many processes are looking and taking at the same time
a race can result and it is best to do the combination atomically with pc.look_take(...)
wwlytton
Posts: 66
Joined: Wed May 18, 2005 10:37 pm
Contact:

Re: Bulletin Board Style

Post by wwlytton »

With MH's assistance I have produced a functioning test for packing and unpacking

Code: Select all

# mpiexec -n 4 nrniv -python -mpi test.py
from neuron import h
pc = h.ParallelContext()
myid = int(pc.id())
nhost = int(pc.nhost())

def storevecs ():
  pc.pack([myid],h.Vector().from_python(range(myid,100,nhost)).as_numpy()) # ersatz vec of spike times
  pc.post('test')

def getvecs ():
  pc.barrier()  # make sure everyone is done posting
  if (myid==0): # if don't do this then the nodes will get hold of printing out and will be interspersed
    while pc.look_take('test')==1: # only take things that have been posted; could also count them since nhosts of them
      id=pc.upkpyobj()[0]
      uptvec=h.Vector().from_python(pc.upkpyobj())   # grab the vector by copying from the array
      print "====",id,"====  ",
      uptvec.printf()

def do ():
  storevecs()
  getvecs()
  pc.runworker()
  pc.done()
  h.quit()

do()
Post Reply