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
MRG: improve feeds #204
Conversation
f448dce
to
6ccce68
Compare
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
fea9630
to
64d5040
Compare
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:
I hope this helps a lot in seeing clearly what we should do next with feeds. |
There was a problem hiding this 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 |
There was a problem hiding this comment.
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
# 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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 ...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
prng : instance of RandomState | ||
The seed for events assuming a given start time. | ||
prng2 : instance of RandomState | ||
The seed for start times. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
2678b97
to
508799a
Compare
Alright, I'm done here. Addressed all the comments and updated tests.
The poisson input function is simplified and it uses 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! |
9e47d91
to
b0d974f
Compare
PR rebased :) |
b0d974f
to
5658324
Compare
Sweet! I'm offline until tomorrow so feel free to merge if you think it's clean enough. |
5658324
to
9b51bf7
Compare
Bringing discussion on
The place look at is 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 |
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
.
Let's keep this for another PR. I don't want to increase the size of this PR any further :) |
I updated the |
Any other last minute fixes or are we good to rebase and merge? |
Good to merge! |
Done! Thanks @jasmainak! |
Giving this another shot. To complement efforts by @cjayb