Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MRG: improve feeds #204

Merged
merged 25 commits into from Nov 18, 2020
Merged

Conversation

jasmainak
Copy link
Collaborator

Giving this another shot. To complement efforts by @cjayb

hnn_core/feed.py Outdated Show resolved Hide resolved
@codecov-io
Copy link

codecov-io commented Nov 12, 2020

Codecov Report

Merging #204 (9b51bf7) into master (fd545cb) will increase coverage by 1.22%.
The diff coverage is 67.82%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #204      +/-   ##
==========================================
+ Coverage   67.78%   69.00%   +1.22%     
==========================================
  Files          21       21              
  Lines        2213     2168      -45     
==========================================
- Hits         1500     1496       -4     
+ Misses        713      672      -41     
Impacted Files Coverage Δ
hnn_core/feed.py 49.31% <48.61%> (+5.43%) ⬆️
hnn_core/__init__.py 100.00% <100.00%> (ø)
hnn_core/network.py 53.08% <100.00%> (-0.39%) ⬇️
hnn_core/tests/test_feed.py 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fd545cb...9b51bf7. Read the comment docs.

@jasmainak jasmainak force-pushed the improve_feeds branch 2 times, most recently from fea9630 to 64d5040 Compare November 12, 2020 20:19
@jasmainak
Copy link
Collaborator Author

jasmainak commented Nov 12, 2020

okay this PR is beginning to get big. I am now planning to add unit tests for each of the different functions I documented. Might get to it next week. But in the meanwhile, would appreciate another set of eyes to look at it (@cjayb @rythorpe @ntolley ?).

The easiest way to review would be to look at the new feed.py file along with the diff. Here is a summary of the main changes:

  • simplification of seed generation with a _get_prng method.
  • dismantling of ExtFeed class to feed_event_times function.
  • unifying truncating event_times > 0 and sorting of event_times in one function
  • unification of several if-else clauses, for example in the _create_common_input function (previously a method)

I hope this helps a lot in seeing clearly what we should do next with feeds.

Copy link
Collaborator

@cjayb cjayb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from a single typo, I find this to be only positive. A lot of the nestedness is gone, even though there remain a lot of idiosyncrasies from "HNN Classic". With #191 we can get all this into Network, where it'll be easier to think about a good API (and tests!) for prng as well. Definite +1 from me!

hnn_core/feed.py Outdated
The events per cycle. Must be 1 or 2.
prng : instance of RandomState
The random state.
prng : instance of RandomState
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prng2

hnn_core/feed.py Outdated
Comment on lines 302 to 307
# Two arrays store doublet times
t_input_low = t_array - 5
t_input_high = t_array + 5
# Array with ALL stimulus times for input
# np.append concatenates two np arrays
t_input = np.append(t_input_low, t_input_high)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this isn't a comment on this PR, more to the code: Could we get one of the OPs to give us a link to the part of documentation where this is specified?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe @rythorpe or @ntolley can help since they use the GUI?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand. What specifically are you looking for here @cjayb?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is now simplified to

return np.append(t_array - 5, t_array + 5)

but doesn't it creep you out that there are undocumented, hard-coded time shifts there? Clearly it's creating spike "doublets", and certainly for a very specific reason. I was just wondering whether anyone still remembers what the reason was?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but doesn't it creep you out that there are undocumented, hard-coded time shifts there?

LOL yes :) Maybe it makes sense for some reason, I don't know ...

I think @rythorpe or @ntolley can throw some light on what this function does and how the spike train is supposed to look like and why. I opened the GUI many aeons ago ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you should restore the comment explaining how this is equivalent to the spikes/burst parameter in GUI?. As for the fixed time shifts, I'm guessing there is a biologic justification for a doublet with a 10 ms ISI.

I see that it was added in a pretty large commit in the GUI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the 10 ms is a somewhat standard approximation for the amount of time between spikes within a thalamic burst. The HNN webset/tutorials don't explicitly address this, however, they do reference this paper that discusses the bursting properties of thalamic drive into cortex.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we'll define an API in a later PR for creating rhythmic feeds that allows the user to define the intra-burst ISI.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, I updated the docstring :) A demo of the GUI for these functions would be nice one day, now that we have everything nicely documented in the code.

hnn_core/feed.py Outdated Show resolved Hide resolved
hnn_core/feed.py Outdated Show resolved Hide resolved
hnn_core/feed.py Outdated
Comment on lines 30 to 33
prng : instance of RandomState
The seed for events assuming a given start time.
prng2 : instance of RandomState
The seed for start times.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused on the use of these two prng values. Could we clarify them a little in the documentation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be honest, I am a bit confused myself. I thought prng2 is used to get the start time jitter but actually it turns out if t0 = -1 then prng is used. See:

hnn-core/hnn_core/feed.py

Lines 225 to 230 in 56f3565

if t0 == -1:
t0 = self.prng.uniform(25., 125.)
# randomize start time based on t0_stdev
elif self.params['t0_stdev'] > 0.0:
# start time uses different prng
t0 = self.prng2.normal(t0, self.params['t0_stdev'])

Nonetheless I clarified in the documentation assuming the intent was actually for the start time for both cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like prng2 is only used for rhythmic inputs and defines the jittered start time of the rhythmic burst. Then, prng is used to define the jittered input of single input within that burst. If my understanding is correct, we may want to simplify _get_prng() by elimnating any reference to prng2 and then adding the logic for creating a second random state variable to feed_event_times() only if a rhythmic (i.e., common) input is in the making.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this will probably get resolved when we work on a more flexible API for creating new feeds so let's ignore this for now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Note though the current "API". Every function has the last argument as prng. This is how it should be. The user should be able to supply their own prng. It shouldn't be generated opaquely inside a function or class

return prng, prng2


def feed_event_times(feed_type, target_cell_type, params, gid):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be a good time to start explicitly defining the elements of the params argument for this function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if it would be a good use of our time to document what is created as an output of create_pext. I think it might be helpful to move create_pext into feed.py so folks can see what parameter corresponds to what. We would avoid duplicating what is in params.py just for documentation ... Let me know if you want me to do that or if you'd rather keep that in params.py

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a strong +1 on leaving as-is for this PR. create_pext is a symptom of unwieldy parameter handling, so fixing (or documenting) one or two methods, I think, has lower priority than figuring out what to do about the root cause.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can wait on this. At some point we need to start dismantling the large param dicts that get passed around and I'm beginning to wonder if the best way to start this process is to begin explicitly defining function arguments where we can.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very likely so, let's see what we can do with a new feeds (drives) API

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+100 on explicitly defining function arguments where we can! It's the best way to avoid opaque code

@jasmainak jasmainak force-pushed the improve_feeds branch 3 times, most recently from 2678b97 to 508799a Compare November 15, 2020 05:30
@jasmainak jasmainak changed the title WIP: improve feeds MRG: improve feeds Nov 15, 2020
@jasmainak
Copy link
Collaborator Author

jasmainak commented Nov 15, 2020

Alright, I'm done here. Addressed all the comments and updated tests.

feed.py is massively simplified. Half of the file is now just docstrings. I don't quite get _create_common_input is supposed to do so I didn't test any of the functionality other than checking that errors are raised correctly. I'd leave this checking for another PR. But any help from GUI users to explain the purpose of this would be very helpful :)

The poisson input function is simplified and it uses numpy now.

Before you decide to merge, please make sure that the example plots (in the CircleCI artifacts) look similar to what we see on the current version of the website. Thank a lot for reviewing guys!

@jasmainak
Copy link
Collaborator Author

PR rebased :)

@cjayb
Copy link
Collaborator

cjayb commented Nov 16, 2020

Sweet! I'm offline until tomorrow so feel free to merge if you think it's clean enough.

@cjayb
Copy link
Collaborator

cjayb commented Nov 17, 2020

Bringing discussion on net.trial_event_times here @jasmainak :

It should be dict (keys are the src_type) of list (gids) of list (n_trials) of list (n_events) ? I think it will help unify things related to trial jitter etc.

The place look at is NetworkBuilder._create_cells_and_feeds:L404: the for-loop over all gids on this rank. I suppose you could do:

        for gid in self._gid_list:  # L404
            src_type, src_pos, is_cell = self.net._get_src_type_and_pos(gid)
            this_src_event_times = self.net.trial_event_times[src_type]
    ...
        else:
            gid_idx = gid - self.net.gid_ranges[src_type][0]  # still needed
            et = this_src_event_times[self.trial_idx][gid_idx]
...

Want to try to flip it around (to dict of lists)? Perhaps rename net.trial_event_times (back) to net.feed_event_times. NB for single-trial simulations this means that the first list dimension len is 1.

@cjayb
Copy link
Collaborator

cjayb commented Nov 17, 2020

I'm also +1 for a merge.

if lamtha > 0.:
t_gen = t0 + self._t_wait(lamtha)
if t_gen < T:
np.append(val_pois, t_gen)
Copy link
Collaborator Author

@jasmainak jasmainak Nov 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rythorpe found the source of the discrepancy you noticed in the gamma example. When you use np.append, the return value needs to be val_pois. The array is not modified in-place like a list. So, this is actually a bug in the old code. One spike is missed at the beginning. Should we consider this a bugfix (and document it in whats_new.rst) or should I push a commit to revert to the old behavior?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. This is a good find. My vote is to move forward with your current solution as a bug fix and document it in whats_new.rst.

@jasmainak
Copy link
Collaborator Author

Want to try to flip it around (to dict of lists)?

Let's keep this for another PR. I don't want to increase the size of this PR any further :)

@jasmainak
Copy link
Collaborator Author

I updated the whats_new with the bugfix note

@rythorpe
Copy link
Contributor

Any other last minute fixes or are we good to rebase and merge?

@jasmainak
Copy link
Collaborator Author

Good to merge!

@rythorpe rythorpe merged commit b506c2f into jonescompneurolab:master Nov 18, 2020
@rythorpe
Copy link
Contributor

Done! Thanks @jasmainak!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants