Skip to content

adds wrappers for networkx so you can modify the graph from within mesa#617

Closed
ghost wants to merge 6 commits intomasterfrom
unknown repository
Closed

adds wrappers for networkx so you can modify the graph from within mesa#617
ghost wants to merge 6 commits intomasterfrom
unknown repository

Conversation

@ghost
Copy link
Copy Markdown

@ghost ghost commented Jan 5, 2019

Never done this before not sure what to write here. Goes with ticket I just opened.

@coveralls
Copy link
Copy Markdown

coveralls commented Jan 5, 2019

Coverage Status

Coverage decreased (-2.2%) to 77.629% when pulling c232777 on 6661696c:networkxwrappers into 6e40c76 on projectmesa:master.

@dmasad
Copy link
Copy Markdown
Member

dmasad commented Jan 7, 2019

This looks good! I'm adding a few comments on specific lines.

[deleted an idea that upon slightly more thought wasn't very helpful]

PS: Congratulations on your first pull request! For future reference -- if you want to link directly to an issue, you can type the hash sign to get a list of issues, and if you write eg #616 GitHub will create the link automatically.

@jackiekazil
Copy link
Copy Markdown
Member

@dmasad is there a reason that this wasn't merged in already?

G.nodes[node_id]['agent'] = list()
self.G.nodes[node_id]['agent'] = list()

def add_nodes(self, node_ids):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

networkx has add_node() and add_nodes_from(). Why is the function naming different here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I still think the method to add a list of nodes, and a node has to be separate. In addition to the consistency with networkx's API, doing this sort of multiple dispatch is not common in Python, and even with multiple dispatch, List[Node] and Node doesn't have a common superclass that makes sense.

@rht
Copy link
Copy Markdown
Contributor

rht commented Jan 18, 2019

What about inheriting directly from nx.Graph (class NetworkGrid(nx.Graph))? This way, all the methods are automatically propagated.

@ghost
Copy link
Copy Markdown
Author

ghost commented Jan 19, 2019

My logic for not doing it that way was that I imagined if sometime in the future they wanted to support different graph packages or provide an option to use them then it would be easier if it was an interface than if it was all just inherited from NetworkX. Since then it could be changed without breaking past uses as the interface would remain the same on the surface. But I have no idea if that was a reasonable thought process. Maybe that would never be an issue?

@rht
Copy link
Copy Markdown
Contributor

rht commented Jan 19, 2019

In that case, for every method that wraps the graph operation, e.g. _place_agent() there needs to be an if-else code selection,

if graph_is_networkx:
    self.G.node[node_id]['agent'].append(agent)
elif graph_is_graphtool:
    self.G.vertex(node_id)['agent'].append(agent)
elif graph_is_igraph:
    ...

Each method becomes more costly to run.

mesa/space.py Outdated
if G is not None:
self.G = G
elif generator is not None:
if type(args) is tuple:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the recommended way for type checks is
isinstance(args, tuple)
You might want to consider changing this here and below.
Alternatively you might just as well skip the check and just use a try...except statement.
This comment applies multiple times below

Copy link
Copy Markdown
Author

@ghost ghost Jan 22, 2019

Choose a reason for hiding this comment

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

I think try/except won't work (least not everywhere) because e.g. anything can be a node in networkx graphs.

But I take on board the isinstance comment, it makes sense that anything inheriting from those types could be valid too.

I can change it once I am sure my toddler is not going to be getting out of bed again!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ok I changed that in my branch, is that all I need to do?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I can't see your changes, maybe you forgot to push your local changes to your forked repo?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

herp derp no, forgot to add changed file!

mesa/space.py Outdated

import itertools

import networkx
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

From what I've seen, the convention tends to be to import networkx as nx -- is there a reason to prefer to keep the full networkx name here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Nope no reason, happy to change it.

mesa/space.py Outdated

class NetworkGrid:
""" Network Grid where each node contains zero or more agents. """
def __init__(self, G=None, generator="Graph", args=()):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add a docstring explaining the arguments

I think args might work better as a dictionary of named keyword-arguments, i.e.
def __init__(self, G=None, generator="Graph", **kwargs):

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ok done. Not sure I worded it great though.


import networkx as nx
import numpy as np
import pytest
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The rest of the tests don't use pytest. Is there a reason to prefer it here? Otherwise, it's probably best not to add a new requirement / tool.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

No, I just didn't know about self.assertRaises which I would have if I'd looked through the rest of the tests. Changed.

@dmasad
Copy link
Copy Markdown
Member

dmasad commented Feb 9, 2019

I wrote the review when this was originally submitted, but apparently never actually pressed the button to have the review post -- sorry!

re why not just inherit from networkx: I think that networkx is also sufficiently agnostic about what nodes are that having the added functionality that @SophiaWilliams's wrappers add is necessary, even if there's never a different network library underlying it. (Also, if we were to add another library, I'd suggest just subclassing NetworkGrid and keeping the same API, instead of putting if-else in statements in each method).

@dmasad
Copy link
Copy Markdown
Member

dmasad commented Jul 5, 2019

This has been sitting here (but looking in good shape) for the last six months or so. It looks good to me -- I'm bumping this to allow everyone to take another quick look, and unless anyone spots a new issue I'll approve the PR. Sorry for letting it drag on for so long, @6661696c!

pre.tests Outdated
@@ -0,0 +1,34 @@
============================= test session starts ==============================
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This file is accidentally committed.

""" Network Grid where each node contains zero or more agents. """
""" kwargs = the arguments to the nx generator function
e.g for watts_strogatz_graph(n, k, p[, seed])"""
def __init__(self, G=None, generator="Graph", **kwargs):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The generator should be an object that defaults to nx.Graph. This is in line with networkx's read_edgelist's create_using argument https://networkx.github.io/documentation/networkx-1.9/reference/generated/networkx.readwrite.edgelist.read_edgelist.html.

@jackiekazil jackiekazil deleted the branch mesa:master March 14, 2021 05:26
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.

5 participants