-
Notifications
You must be signed in to change notification settings - Fork 55
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] ENH: interactive connectivity #376
[MRG] ENH: interactive connectivity #376
Conversation
@ntolley if you want to fetch this PR locally, try to do: $ git fetch upstream pull/376/head:interactive_connectivity
$ git checkout interactive_connectivity |
After messing with this I'm realizing that the method to get the The pick_connectivity PR will further help resolve this as it replaces src_range with src_gids, which specific exactly which gids are included without looking at the In any case I'm a fan! Is is it possible to label the subplots separately so it's clear which one represents source vs target gids? |
@ntolley do you have a short script to share that demoes the issue? would be helpful to use it for debugging here |
Minimal script to reproduce the issue: import hnn_core
import os.path as op
from hnn_core import default_network, read_params
from hnn_core.viz import plot_cell_connectivity
hnn_core_root = op.dirname(hnn_core.__file__)
params_fname = op.join(hnn_core_root, 'param', 'default.json')
params = read_params(params_fname)
net = default_network(params, add_drives_from_params=True)
conn_idx = 13
gid_idx = 11
src_gid = net.connectivity[conn_idx]['src_range'][gid_idx]
fig, ax = plot_cell_connectivity(net, conn_idx, src_gid) |
This is happening because the |
@ntolley pushed some fixes. This is my script to debug: import hnn_core
import os.path as op
from hnn_core import default_network, read_params
from hnn_core.viz import plot_cell_connectivity
hnn_core_root = op.dirname(hnn_core.__file__)
params_fname = op.join(hnn_core_root, 'param', 'default.json')
params = read_params(params_fname)
net = default_network(params, add_drives_from_params=True)
del net.connectivity[-1]
conn_idx = 29
net.add_connection(net.gid_ranges['L2_pyramidal'][::2],
'L5_basket', 'soma',
'ampa', 0.00025, 1.0, lamtha=3.0,
probability=0.8)
fig, ax = plot_cell_connectivity(net, conn_idx) what do you think? One issue that still remains is that the drives have all their positions at origin ... so it's not possible to visualize them in this way. Currently, we only show two axes if it's not a drive. Any further thoughts? |
Codecov Report
@@ Coverage Diff @@
## master #376 +/- ##
==========================================
+ Coverage 89.97% 90.01% +0.03%
==========================================
Files 16 16
Lines 2934 2965 +31
==========================================
+ Hits 2640 2669 +29
- Misses 294 296 +2
Continue to review full report at Codecov.
|
add412e
to
da2b9e7
Compare
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.
This is very cool, especially the _fake_click
! +1 to merge
@jasmainak would you prefer some level of interactivity with the drives? I suppose one option would be a slider placed horizontally the bottom that is set to the This may be an over-complicated solution to a pretty clean PR. I am ok with leaving it as is, because there's less of a benefit of exploring the drives. (On the flip side, going the slider route would allow for flipping through different connections in the same figure.) |
return im | ||
|
||
|
||
def plot_cell_connectivity(net, conn_idx, src_gid=None, axes=None, |
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.
Update docstring to indicate interactive plot? Also it'd be good to add a notes section describing how to use it.
# Extract indeces to get position in network | ||
# Index in gid range aligns with net.pos_dict | ||
target_src_pair = conn['gid_pairs'][src_gid] | ||
target_indeces = np.where(np.in1d(target_range, target_src_pair))[0] |
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.
Great solution, much cleaner
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's your code, I didn't do anything ... just moved it around :)
src_gid : int | ||
Each cell in a network is uniquely identified by it's "global ID": GID. | ||
ax : instance of Axes3D | ||
src_gid : int | None |
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.
This is a good description, but after writing so many docstrings it's making me think it will be worthwhile creating some consistency for the arguments that appear super often. Sort of in line with the hnn-core glossary we had talked about, these parameters could start with the boilerplate description, and then more function specific text.
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 will defer this to a separate PR: #387 :)
hnn_core/viz.py
Outdated
src_idx = np.where(src_range == src_gid)[0][0] | ||
src_pos = src_type_pos[src_idx] | ||
if src_gid not in valid_src_gids: | ||
raise ValueError(f'src_gid not a valid cell ID for this connection ' |
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.
raise ValueError(f'src_gid not a valid cell ID for this connection ' | |
raise ValueError(f'src_gid {src_gid} not a valid cell ID for this connection ' |
hnn_core/tests/test_viz.py
Outdated
@@ -47,9 +55,21 @@ def test_network_visualization(): | |||
with pytest.raises(TypeError, match='src_gid must be an instance of'): | |||
plot_cell_connectivity(net, conn_idx, src_gid='blah') | |||
|
|||
with pytest.raises(ValueError, match='src_gid not in the'): | |||
with pytest.raises(ValueError, match='src_gid not a valid cell ID'): |
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.
with pytest.raises(ValueError, match='src_gid not a valid cell ID'): | |
with pytest.raises(ValueError, match='src_gid -1 not a valid cell ID'): |
plot_cell_connectivity(net, conn_idx, src_gid=-1) | ||
|
||
# smoke test interactive clicking | ||
del net.connectivity[-1] |
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.
why is the deletion necessary?
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 strictly necessary but I thought this would be good practice, otherwise the same "kind of connection" would occur in two different net.connectivity
elements and the net effect would be a higher weight than we intended?
ax_src = fig.axes[0] | ||
pos = net.pos_dict['L2_pyramidal'][2] | ||
_fake_click(fig, ax_src, [pos[0], pos[1]]) | ||
|
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.
Can you add a test to make sure the figure is actually updated? A really simple one would be to grab the title with fig.texts
and assert that it is different after running _fake_click
.
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.
you're making me work harder for my merges ;-) It's good! Take a look at the last commit
77c0445
to
d8d7a47
Compare
d8d7a47
to
b1fe671
Compare
|
||
pos = net.pos_dict['L2_pyramidal'][2] | ||
_fake_click(fig, ax_src, [pos[0], pos[1]]) | ||
pos_in_plot = ax_target.collections[2].get_offsets().data[0] |
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 fancy! Great unit test
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.
This looks great @jasmainak! Happy to merge if you're all wrapped up here.
Good to go for me! |
Not sure if this is useful, could get some feedback before working more on it ...
closes #375