mcdougallab / matlabneuroninterface

Interface for connecting NEURON and MATLAB
BSD 3-Clause "New" or "Revised" License
5 stars 1 forks source link

Iterating over Sections and Segments #48

Closed edovanveen closed 1 year ago

edovanveen commented 1 year ago

Implement MATLAB equivalents for:

ramcdougal commented 1 year ago

There is now a allsec-mac-nrn8 folder in https://github.com/mcdougallab/neuron-c-api-demos that demonstrates looping over NEURON's global section_list variable, which basically defines a circularly linked list of all the sections.

The key part here is:

for (auto list_ptr = (*section_list)->next; list_ptr != *section_list; list_ptr=list_ptr->next) {
    Section* sec = list_ptr->element.sec;
    // check to make sure the section has properties
    // missing properties means that the section has been invalidated
    // but not removed
    if (sec->prop) {
        // we've found a real section, now let's do something
        cout << "    sec = " << secname(sec) << endl;
    } else {
        // invalidated section; we can remove it from the section_list and unref it
        // it would be mostly harmless to skip this, but NEURON does not routinely
        // search for and remove sections
        hoc_l_delete(list_ptr);
        section_unref(sec);
    }
}

Here I'm simply calling cout to print the name of the Section* sec but we could do whatever we want once we have the Section. (Note: checking that sec->prop is non-null is essential to make sure the section is still active, but strictly speaking we could just ignore null values.)

I believe that SectionList objects also store their data in the same structure and the iteration should work, but I haven't tried to figure out how to get the underlying pointer to a SectionList object's circularly linked list from C++ yet.

ramcdougal commented 1 year ago

The plotshape-linux-nrn8 folder on neuron-c-api-demos builds two PlotShape objects (one with no specified sections hence covering all, and one with a specified SectionList). It then queries the PlotShape to get the SectionList (if specified), variables, and bounds back... loops through either the SectionList or all sections, and displays all the info that would be needed to generate a PlotShape 3d graph.

Relative to the previous example, if sl is an Object* referring to a SectionList, then instead of using the global section_list, you'd use my_section_list = (hoc_Item**) &sl->u.this_pointer; instead for looping over the SectionList.

edovanveen commented 1 year ago

I made a first version of allsec, but it cannot yet handle invalidated sections. How do i make an invalidated section, just so I can test allsec behaviour?

ramcdougal commented 1 year ago

If you create a bunch of sections, then loop over allsec you should get all of them.

Suppose then that you delete a section, which you can probably do by setting it to be the currently accessed section and then calling delete_section.

Now loop over everything. The section should not appear. If it does and isn't invalidated, check its ref count.

Then repeat the same test (of deleting a section) with a SectionList that contains the section.

edovanveen commented 1 year ago

@ramcdougal The allsec function is here and the example is here.

Everything seems to work! Output is:

|-|       soma(0-1)
|-|       dend1(0-1)
|-|       axon1(0-1)
|-|       axon2(0-1)

1 soma
2 dend1
3 axon1
4 axon2
dend1 length: 37
axon2 length: 42
1 soma
2 axon1
ramcdougal commented 1 year ago

Sounds great!

Here's a comment for you: in Python, h.allsec() is a generator; it returns sections as needed instead of returning a list of everything. I see pros (it's nice to be able to grab the ith item and you can reuse a list) and cons (have to store a MATLAB alias to everything in memory), just wanted to point out the difference and see if anyone has any opinions about which is better in the MATLAB context.

Just to point this out: the PlotShape C++ example also loops over segments. Segments are essential for cleanly specifying e.g. membrane potential, but there's also potential for a rabbit hole of making functions work with segments. (There's a quick and dirty hack that works most of the time, which would be to replace and argument that's a segment with the x value from a segment and push the corresponding section to the section stack... it obviously wouldn't let you do anything that takes two segments in one command -- but neither does HOC, only Python... the weirder thing would be that segments could then be misused for values between 0 and 1... but I think that may be the only real downside.)

edovanveen commented 1 year ago

As far as I know, iterators/generators aren't really a thing in MATLAB, although it is probably possible to make a generator-like class ourselves.

I will work on segments next.

edovanveen commented 1 year ago

I implemented the 'quick hack' for Segments, see example_netcon.m in branch https://github.com/mcdougallab/matlabneuroninterface/tree/48-iterating-over-sections-and-segments. We can now do n.ExpSyn(soma(0.5)), for example.