Skip to content

Can we replace nest.Prepare/nest.Run/nest.Simulate/nest.Cleanup by a single or two wrapper command(s)? #1773

@jd41

Description

@jd41

See also #1772.

Update: The basic idea of this change is to keep track of whether the simulation is currently in a prepared state (e.g. nest.Prepare() has been called, but nest.Cleanup() hasn't), and put nest.Prepare and nest.Cleanup into wrappers which only invoke the expensive kernel functions if they are necessary/appropriate according to that status. The current motivation for exposing Prepare/Run/Cleanup rather than just one simulation command to the PyNEST user is that the user can save time by not cleaning up between runs (see the Running simulations guide). With the wrappers, the overhead of calling nest.Prepare twice becomes negligible, and we can remove much of that complexity from the API and documentation (see below for details and precisifications).

Is your feature request related to a problem? Please describe.
It is related to the problem I described in #1772, which is related to the fact that the user has to learn about 4 commands (Update: actually 5, if you include the RunManager)+at least 1 pitfall (see #1772 for the pitfall) for running simulations once vs. in stages. It seems to me that, by introducing some wrappers on the PyNEST level, the pitfall would vanish, only 2 functions would be necessary to document/understand (and the others could be deprecated/removed), and almost all users would only need to learn about 1 of them. I write here to understand whether I am missing something and it's more complicated in reality.

Describe the solution you'd like

  • The PyNEST API module keeps track of a global Boolean variable "prepared". It is initialized to False.
  • nest.Prepare() is replaced by a wrapper that only calls the original nest.Prepare() if prepared == False, and sets prepared = True afterwards.
  • nest.Run() is replaced by a wrapper that checks if prepared == True, and calls nest.Prepare() before the original nest.Run() if it was false. UPDATE/Clarification: If it finds that prepared == True already, it skips the Prepare() call. Another UPDATE: Actually, because the new Prepare() checks whether prepared == True already and doesn't invoke the NEST kernel if it is, it wouldn't hurt performance too much to skip the check in the new Run() function.
  • nest.Simulate() is replaced by a wrapper that calls the new nest.Run() (which in turn will call nest.Prepare() if the simulation wasn't prepared), followed by nest.Cleanup().
  • nest.SetStatus() is replaced by a wrapper that checks if prepared == False, and calls nest.Cleanup() before the original nest.SetStatus() otherwise. UPDATE: We also could skip the if check without much performance cost, as the new nest.Cleanup() will also check whether prepared == True and not do anything expensive if it is already/still False.
  • model.set() changed in the same way as nest.SetStatus().
  • nest.Cleanup() is replaced by a wrapper that only calls the original nest.Cleanup() if prepared == True, and sets prepared = False afterwards.
  • include atexit.register(Cleanup) from the atexit module in the PyNEST initialisation code so that nest.Cleanup() is called on program exit automatically (would this work in the way I understand it works?).
  • nest.RunManager() deprecated/removed.

Alternatively, one could implement these wrapper functions in the NEST kernel.

If I understand correctly, these changes by themselves would be completely backwards-compatible and not change the behaviour of currently correct PyNEST simulation code (as well as introducing minimal overhead). However, they would make life easier for the user, who now can usually call nest.Run() instead of understanding and distinguishing Prepare/Cleanup/Simulate/Run. Only in the presumably rare case where they want to close/sync files in the middle of the simulation script, they would also need to know about and call nest.Cleanup(). The other functions (nest.Simulate() and nest.Prepare()) could be deprecated/removed from the documentation and user-visible API in the next step. The SetStatus-after-Prepare pitfall (#1772) would vanish.

Describe alternatives you've considered
I'm somewhat on the fence on whether to call the new nest.Run() function "nest.Run()" or "nest.Simulate()", because:

In the variant described above, the remaining difference between the new nest.Run() and the new nest.Simulate() would be that, as is the case currently, nest.Simulate() will be guaranteed to exit with the kernel in a cleaned-up state/files closed/whatever.

If we instead call the function that becomes the new nest.Run() above "nest.Simulate()", I see two advantages and a disadvantage:

  • Advantages: I guess that most simulation code out there is fine with an unclean kernel after Simulate, and the cleanup being done automatically once on script termination (with the atexit handler). So just calling the new nest.Run() nest.Simulate() will be fine, and scripts will continue to work without users having to learn about a new command/changing their scripts/understanding one more entry in the NEST-X-to-X+1 porting guide. Scripts that didn't care about using Prepare/Run/Simulate before will can become faster.

  • Disadvantage: However, if a script depends on the kernel being cleaned up after a Simulate() call in the middle of the python code, and the Simulate suddenly stops doing that, it may cause a lot of grief to whatever student tries to port that script to the next NEST version and doesn't understand why it doesn't work anymore.

Am I missing something here, and is it more complicated to do? If my understanding is correct, this seems like a relatively straightforward change in the PyNEST module to me, which I'd be happy to implement and write some unit tests for.

Metadata

Metadata

Assignees

No one assigned

    Labels

    I: User InterfaceUsers may need to change their code due to changes in function callsS: NormalHandle this with default priorityT: DiscussionStill searching for the right way to proceed / suggestions welcome

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions