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

[ENH] Fixed bug in clear_connectivity and improved tests for deleting drives #613

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

raj1701
Copy link
Contributor

@raj1701 raj1701 commented Mar 8, 2023

Closes #549
Fixed all bugs in clear_drives and clear_connectivity functions
Wrote tests for the following

  • clear_connectivity with no arguments passed. Deletes all connections apart from external drive connections
  • clear_drives with drive_names as argument passed. Deletes connections of the specified drives
  • clear_drives with no argument. Deletes connections of all external drives

@raj1701 raj1701 changed the title Fixed bud and improved tests for deleting drives [ENH] Fixed bug in clear_connectivity and improved tests for deleting drives Mar 8, 2023
"""
connectivity = list()
def clear_connectivity(self, src_types=None):
"""Remove connections with src_type in Network.connectivity."""
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you document src_types ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

also is clear_connectivity tested?

Copy link
Contributor Author

@raj1701 raj1701 Mar 8, 2023

Choose a reason for hiding this comment

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

Sure will add the documentation for src_types.
Yes it is tested in all three tests. First test checks the base conditions where no arguments are passed. Here it successfully deletes all cell connections apart from those originating from external drives
The other two tests it tests for deletion of connections from specific source types.

del self.external_drives[drive_name]
self.clear_connectivity(src_types=drive_names)

def get_external_drive_names(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about?

Suggested change
def get_external_drive_names(self):
@property
def drive_names(self):

It feels more Pythonic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure will make this change

@codecov-commenter
Copy link

codecov-commenter commented Mar 8, 2023

Codecov Report

Merging #613 (8512d57) into master (78d26fa) will decrease coverage by 0.28%.
The diff coverage is 86.98%.

❗ Current head 8512d57 differs from pull request most recent head c7c4a6c. Consider uploading reports for the commit c7c4a6c to get more accurate results

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

@@            Coverage Diff             @@
##           master     #613      +/-   ##
==========================================
- Coverage   92.15%   91.87%   -0.28%     
==========================================
  Files          22       22              
  Lines        4219     4333     +114     
==========================================
+ Hits         3888     3981      +93     
- Misses        331      352      +21     
Impacted Files Coverage Δ
hnn_core/drives.py 98.54% <ø> (ø)
hnn_core/gui/_viz_manager.py 86.93% <73.07%> (-2.08%) ⬇️
hnn_core/network.py 93.19% <91.17%> (-0.16%) ⬇️
hnn_core/gui/gui.py 94.87% <93.75%> (-1.57%) ⬇️
hnn_core/__init__.py 100.00% <100.00%> (ø)
hnn_core/dipole.py 92.59% <100.00%> (+0.13%) ⬆️
hnn_core/network_models.py 100.00% <100.00%> (ø)
hnn_core/optimization.py 94.85% <100.00%> (+2.10%) ⬆️
hnn_core/params.py 91.89% <100.00%> (+0.25%) ⬆️
hnn_core/viz.py 89.23% <100.00%> (+0.04%) ⬆️

... and 1 file with indirect coverage changes

@raj1701
Copy link
Contributor Author

raj1701 commented Mar 8, 2023

@jasmainak Is the short description for src_types enough or should I elaborate it more?

The drive_names to remove
"""
if drive_names == 'all':
drive_names = list(self.external_drives.keys())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
drive_names = list(self.external_drives.keys())
drive_names = self.drive_names

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure will do

@@ -71,7 +71,7 @@
# directly.
def get_network(probability=1.0):
net = jones_2009_model(add_drives_from_params=True)
net.clear_connectivity()
net.connectivity = list()
Copy link
Collaborator

Choose a reason for hiding this comment

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

undo this since you have clear_connectivity function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure will do

Copy link
Contributor Author

@raj1701 raj1701 Mar 9, 2023

Choose a reason for hiding this comment

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

Here net.connectivity needs to be completely cleared.
So both clear_connectivity() and clear_drives() will need to be called one by one.

  • clear_connectivity() will leave connections of external drives open. For deleting them clear_drives will need to be called.
  • But if there are existing external drive connections only clear_connectivity needs to be called....

Which direction is better?

net.clear_drives()
assert len(net.connectivity) == 0
assert len(net.connectivity) == 0 # All connections are deleted
Copy link
Collaborator

@jasmainak jasmainak Mar 8, 2023

Choose a reason for hiding this comment

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

clear_drives should still leave the connections within the cortical column ... were there no connections within the cell populations?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Earlier clear_connectivity() was called which deleted all connections other than the ones involving external drives. That was pushed by the previous pull request i think, the one I got merge conflict with so I kept that change

hnn_core/network.py Outdated Show resolved Hide resolved
@rythorpe
Copy link
Contributor

I'm guessing you merged master with your clear_drive branch? We want to maintain a clean commit history, so rebasing instead of merging is the way to go here. You could probably do an interactive rebase to clean up your history. Any recommendations @jasmainak?

@raj1701
Copy link
Contributor Author

raj1701 commented Mar 10, 2023

Yes I merged master in clear_drive because of which I got external commits
For correcting :
I reset the HEAD to the last correct commit. Made the changes and commited again. Now this branch has 4 commits and only the changes made by me. Is this fine @rythorpe or should I do something else?

@jasmainak
Copy link
Collaborator

you almost never want to do a merge or pull (fetch + merge) when working on public repos. Just fetch the latest master and rebase:

$ git fetch upstream master:master
$ git rebase master

@raj1701
Copy link
Contributor Author

raj1701 commented Mar 10, 2023

Yes will remember this in future. Thanks!!

@raj1701
Copy link
Contributor Author

raj1701 commented Mar 16, 2023

Hey @rythorpe @jasmainak what changes need to be made in this?

src_types : list | None
Source types of connections to be removed
None - Remove all connections other than those which have an
external drive as a source type
Copy link
Collaborator

Choose a reason for hiding this comment

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

@raj1701 I think you have been struggling with the logic because you are trying to preserve the old behavior and logic without fully understanding the purpose of that behavior. You need to clarify the behavior before writing the logic. To me, what makes the most sense is:

net.clear_connectivity()  # clear all connections
net.clear_connectivity(src_types='drives')  # clear connections from drives to cells
net.clear_connectivity(src_types='local')  # clear connections within cells
net.clear_connectivity(src_types=['L2_Pyramidal', 'L5_basket'])  # clear connection starting with cell populations L2_pyramidal and L5_basket

To do this, you should use pick_connection and avoid rewriting the logic of finding the appropriate connections. Once you have the indices of the connections, you can write a simple for loop to delete the items:

for conn_idx in conn_idxs:
   del net.connectivity[conn_idx]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes @jasmainak I was trying to preserve the old logic. If no argument is passed to clear_connectivity then the default value of src_types should be called "All" instead of "None". Will refactor the code accordingly and make the required changes.

@raj1701
Copy link
Contributor Author

raj1701 commented Mar 21, 2023

Hey @jasmainak I redesigned the code. It passed all the tests in my local system but here the tests are failing for a file I didn't even change...

@rythorpe
Copy link
Contributor

Hey @jasmainak I redesigned the code. It passed all the tests in my local system but here the tests are failing for a file I didn't even change...

Looks like an MPI error... I'm going to rerun the tests because sometimes running MPIBackend on the CI servers is finicky.

Comment on lines 1270 to 1273
src_types = list()
for conn in self.connectivity:
if conn['src_type'] not in src_types:
src_types.append(conn['src_type'])
Copy link
Collaborator

Choose a reason for hiding this comment

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

this seems pretty convoluted ... could you use self.gid_ranges.keys() ?

Comment on lines 1277 to 1282
src_types = list()
external_drives = self.drive_names
for conn in self.connectivity:
if (conn['src_type'] not in src_types and
conn['src_type'] not in external_drives):
src_types.append(conn['src_type'])
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here ... I think you just need to do a list comprehension to leave out the external drives from all the cell populations


Parameters
----------
net - The network instance
Copy link
Collaborator

Choose a reason for hiding this comment

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

we follow the numpy docstring conventions: https://numpydoc.readthedocs.io/en/latest/format.html#parameters

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey @jasmainak I have simplified the loops and followed the numpy docstring conventions

Comment on lines 1269 to 1271
src_types = list()
for src_type in self.gid_ranges.keys():
src_types.append(src_type)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
src_types = list()
for src_type in self.gid_ranges.keys():
src_types.append(src_type)
src_types = list(self.gid_ranges.keys())

Copy link
Collaborator

Choose a reason for hiding this comment

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

the constructor of list can take an iterable

Comment on lines 1275 to 1279
src_types = list()
all_src_types = self.gid_ranges.keys()
local_src_types = all_src_types - self.drive_names
for src_type in local_src_types:
src_types.append(src_type)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
src_types = list()
all_src_types = self.gid_ranges.keys()
local_src_types = all_src_types - self.drive_names
for src_type in local_src_types:
src_types.append(src_type)
src_types = [src_type for src_type in list(self.gid_ranges.keys()) if src_type not in self.drive_names]


Parameters
----------
src_types : list | all | drives | local
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
src_types : list | all | drives | local
src_types : list | 'all' | 'drives' | 'local'

to be clear that these are strings ... also fix below

@raj1701
Copy link
Contributor Author

raj1701 commented Mar 24, 2023

@jasmainak I made these changes. Sorry for the extra commit. I pushed by mistake before changing one thing.

@raj1701
Copy link
Contributor Author

raj1701 commented Apr 27, 2023

Hey @jasmainak can you suggest any changes required for this PR?

Comment on lines 1264 to 1265
'drives' - Clear connections originating from external drives
'local' - Clear connections within cells
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
'drives' - Clear connections originating from external drives
'local' - Clear connections within cells
'drives' - Clear connections from the external drives to the local network
'local' - Clear connections between cells of the local network

How about 'external' or 'exogenous' instead of 'drives', and 'internal' or 'endogenous' instead of 'local'? I've gotten the impression our documentation is already a little hazy on how we delineate between these two distinct categories of connections.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure will rename instances of drives as exogenous and instances of local as endogenous

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

I like 'external' and 'internal' ... very explicit, clear and I prefer it over exogenous/endogenous which is a bit too academic.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's fair!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure will rename accordingly

Copy link
Collaborator

@jasmainak jasmainak left a comment

Choose a reason for hiding this comment

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

Looks like I never posted the comments from my review ... here goes

examples/howto/plot_connectivity.py Show resolved Hide resolved
Comment on lines 1264 to 1265
'drives' - Clear connections originating from external drives
'local' - Clear connections within cells
Copy link
Collaborator

Choose a reason for hiding this comment

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

I like 'external' and 'internal' ... very explicit, clear and I prefer it over exogenous/endogenous which is a bit too academic.

@@ -510,6 +510,30 @@ def test_network_drives_legacy():
n_bursty_sources)


def get_expected_connectivities(net, src_types='all'):
Copy link
Collaborator

Choose a reason for hiding this comment

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

just make this a private function ... it's not user facing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure will do

@@ -1253,23 +1253,77 @@ def add_connection(self, src_gids, target_gids, loc, receptor,

self.connectivity.append(deepcopy(conn))

def clear_connectivity(self):
"""Remove all connections defined in Network.connectivity
def _get_expected_connectivities(self, src_types='all'):
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this is solely for testing purposes, should this function be moved to test_network.py?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jasmainak wanted this function as private because it was not user facing, that is why I shifted it here. But yes it is used only for testing purposes, if required I can move it to test_network.py. @jasmainak @rythorpe wdyt?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes, let's move this to test_network.py

def clear_connectivity(self):
"""Remove all connections defined in Network.connectivity
def _get_expected_connectivities(self, src_types='all'):
"""Return expected connectivities left after clearng connections.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"""Return expected connectivities left after clearng connections.
"""Return expected connectivities left after clearing connections.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure

Copy link
Contributor

@rythorpe rythorpe left a comment

Choose a reason for hiding this comment

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

It looks like there are two ways to modify drive connectivity: net.clear_connectivity(src_types='internal') and net.clear_drives(drive_names='all'). The first method, however, doesn't clear net.external_drives, which could be misleading to users. I'm not sure why we need two different ways to clear drive connections, so my recommendation would be to consolidate.

Zen of Python: There should be one-- and preferably only one --obvious way to do it.

@rythorpe
Copy link
Contributor

rythorpe commented May 9, 2023

Just to clarify, I understand the utility of having net.clear_drives call net.clear_connectivity: the first clears net.external_drives and then outsources clearing the drive connectivity to the second. My only issue is that we should encourage users to only use net.clear_drives when touching drive connectivity. The simplest solution would be to make clear_connectivity private, and then make a public wrapper method with the same name that users can call to clear internal network connectivity but doesn't give them access to external drive connectivity.

Alternatively, you could just separate the functionality of the two existing functions s.t. one takes care of drives + external connectivity while the other takes care of internal connectivity.

Apologies if this contradicts some of your previous conversations @raj1701 and @jasmainak - LMK if you disagree.

@raj1701
Copy link
Contributor Author

raj1701 commented May 9, 2023

Just to clarify, I understand the utility of having net.clear_drives call net.clear_connectivity: the first is clears net.external_drives and then outsources clearing the drive connectivity to the second. My only issue is that we should encourage users to only use net.clear_drives when touching drive connectivity. The simplest solution would be to make clear_connectivity private, and then make a public wrapper method with the same name that users can call to clear internal network connectivity but doesn't give them access to external drive connectivity.

Alternatively, you could just separate the functionality of the two existing functions s.t. one takes care of drives + external connectivity while the other takes care of internal connectivity.

Apologies if this contradicts some of your previous conversations @raj1701 and @jasmainak - LMK if you disagree.

Can we have a private function _clear_connectivity() to clear connections. A function clear_internal_connectivity() for deleting internal connections with a check to make sure the input src_types do not have an external drive and then just call the private method to clear connections(internal)? Similarly clear_drives() can have a check to ensure src_types only has drive names.

self.external_drives = dict()
self.connectivity = connectivity
if src_types == 'all':
return 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

instead of hard coding this, it might be better to do:

Suggested change
return 0
src_types = self.gid_ranges().keys()

or something similar (please check what it should be).

Copy link
Contributor Author

@raj1701 raj1701 May 15, 2023

Choose a reason for hiding this comment

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

Just to clarify, I understand the utility of having net.clear_drives call net.clear_connectivity: the first is clears net.external_drives and then outsources clearing the drive connectivity to the second. My only issue is that we should encourage users to only use net.clear_drives when touching drive connectivity. The simplest solution would be to make clear_connectivity private, and then make a public wrapper method with the same name that users can call to clear internal network connectivity but doesn't give them access to external drive connectivity.
Alternatively, you could just separate the functionality of the two existing functions s.t. one takes care of drives + external connectivity while the other takes care of internal connectivity.
Apologies if this contradicts some of your previous conversations @raj1701 and @jasmainak - LMK if you disagree.

Can we have a private function _clear_connectivity() to clear connections. A function clear_internal_connectivity() for deleting internal connections with a check to make sure the input src_types do not have an external drive and then just call the private method to clear connections(internal)? Similarly clear_drives() can have a check to ensure src_types only has drive names.

This line of code may not be required if the above behavior is used.

src_gids=src_type))
return len(self.connectivity) - deleted_connectivities

def clear_connectivity(self, src_types="all"):
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a bit of a nitpick. But for stylistic consistency, I suggest using single quotes everywhere unless you need to escape certain characters. You can also check what the PEP 8 document says.

Copy link
Collaborator

Choose a reason for hiding this comment

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

https://peps.python.org/pep-0008/#string-quotes

Ok, it makes no recommendation but I personally prefer single quotes :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure will use single quotes

Comment on lines +1275 to +1276
def clear_connectivity(self, src_types='all'):
"""Clear connections between cells of the local network
Copy link
Collaborator

Choose a reason for hiding this comment

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

@rythorpe is this what you intended? A "dangling" cell population will be problematic even with local network connectivity ... I'm wondering if we should make clear_connectivity private altogether until it has clarified in our minds how to do it right

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep! I don't see how this could be problematic. If you think we should prevent users from selectively removing local network connections, perhaps we can just get rid of the src_types arg and make this function clear all local network connections (which I guess is more consistent with the name of this method anyway).

Copy link
Collaborator

Choose a reason for hiding this comment

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

but clear_drives needs the src_types argument ...

Copy link
Contributor

Choose a reason for hiding this comment

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

but clear_drives needs the src_types argument ...

why?

connectivity.append(conn)
self.external_drives = dict()
self.connectivity = connectivity
_validate_type(src_types, list, 'src_types', 'list, drives, local')
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be updated? I think it should say something like this for the last arg: 'list of drive names or local network connections'.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we can remove this check as types are already validated in the public functions - clear_connectivity() and clear_drives(). Should I remove this check or update it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point, go ahead and remove it!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure will do

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.

show users how to delete drives
4 participants