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
Add nx.config
dict for configuring dispatching and backends
#7225
Changes from 1 commit
52f96fe
023c47f
0510588
ee437ad
93ba39e
276a371
dc8366a
438c83a
9980f6a
ba886c7
40a27a7
ae480b2
c81595e
c4b7892
65c8583
dd0096e
eb7ea72
2323189
f45b1d2
71d37e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,7 +95,19 @@ class WrappedSparse: | |
|
||
from ..exception import NetworkXNotImplemented | ||
|
||
__all__ = ["_dispatchable"] | ||
__all__ = ["_dispatchable", "backend_config"] | ||
|
||
# Get default configuration from environment variables at import time. | ||
# Default backend config will be initialized from `backend_info` just below. | ||
backend_config = { | ||
"fallback_to_nx": os.environ.get("NETWORKX_FALLBACK_TO_NX", "true").strip().lower() | ||
== "true", | ||
"automatic_backends": [ | ||
x.strip() | ||
for x in os.environ.get("NETWORKX_AUTOMATIC_BACKENDS", "").split(",") | ||
if x.strip() | ||
], | ||
} | ||
|
||
|
||
def _get_backends(group, *, load_and_call=False): | ||
|
@@ -126,6 +138,12 @@ def _get_backends(group, *, load_and_call=False): | |
|
||
backends = _get_backends("networkx.backends") | ||
backend_info = _get_backends("networkx.backend_info", load_and_call=True) | ||
# Initialize default configuration from backends (if provided) | ||
backend_config.update( | ||
(backend, info["default_config"]) | ||
for backend, info in backend_info.items() | ||
if "default_config" in info | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here, it might be more optimal to have a separate function(like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I completely understand your comment. By optionally including import networkx as nx
nx.backend_config["parallel"]["n_job"] = 5
We could make a separate entry point for the default config, but I don't know if it would be worth doing since it seems to me that the "backend_info" entry point works well enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, I thought this kind of thing was possible : import networkx as nx
nx.backend_config["parallel"]["n_job"] = 5
# defining G
print(nx.betweenness_centrality(G, backend="parallel"))
nx.backend_config["parallel"]["n_job"] = -1
nx.backend_config["parallel"]["backend"] = 'threading'
print(nx.betweenness_centrality(G, backend="parallel")) so I thought we would be extracting all the configurations and adding them in the But, I think, based on the clarification above, that all the configuration code lines will be executed while importing networkx so the final configuration for both the function calls would be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Coming back to this question:
I think your |
||
|
||
# Load and cache backends on-demand | ||
_loaded_backends = {} # type: ignore[var-annotated] | ||
|
@@ -154,14 +172,6 @@ class _dispatchable: | |
# For example: `PYTHONPATH=. pytest --backend graphblas --fallback-to-nx` | ||
# Future work: add configuration to control these | ||
_is_testing = False | ||
_fallback_to_nx = ( | ||
os.environ.get("NETWORKX_FALLBACK_TO_NX", "true").strip().lower() == "true" | ||
) | ||
_automatic_backends = [ | ||
x.strip() | ||
for x in os.environ.get("NETWORKX_AUTOMATIC_BACKENDS", "").split(",") | ||
if x.strip() | ||
] | ||
|
||
def __new__( | ||
cls, | ||
|
@@ -436,13 +446,14 @@ def __call__(self, /, *args, backend=None, **kwargs): | |
# if (val := args[pos] if pos < len(args) else kwargs.get(gname)) is not None | ||
# } | ||
|
||
if self._is_testing and self._automatic_backends and backend_name is None: | ||
automatic_backends = backend_config["automatic_backends"] | ||
if self._is_testing and automatic_backends and backend_name is None: | ||
# Special path if we are running networkx tests with a backend. | ||
return self._convert_and_call_for_tests( | ||
self._automatic_backends[0], | ||
automatic_backends[0], | ||
args, | ||
kwargs, | ||
fallback_to_nx=self._fallback_to_nx, | ||
fallback_to_nx=backend_config["fallback_to_nx"], | ||
) | ||
|
||
# Check if any graph comes from a backend | ||
|
@@ -504,7 +515,7 @@ def __call__(self, /, *args, backend=None, **kwargs): | |
raise ImportError(f"Unable to load backend: {graph_backend_name}") | ||
if ( | ||
"networkx" in graph_backend_names | ||
and graph_backend_name not in self._automatic_backends | ||
and graph_backend_name not in automatic_backends | ||
): | ||
# Not configured to convert networkx graphs to this backend | ||
raise TypeError( | ||
|
@@ -520,11 +531,11 @@ def __call__(self, /, *args, backend=None, **kwargs): | |
graph_backend_name, | ||
args, | ||
kwargs, | ||
fallback_to_nx=self._fallback_to_nx, | ||
fallback_to_nx=backend_config["fallback_to_nx"], | ||
) | ||
# All graphs are backend graphs--no need to convert! | ||
return getattr(backend, self.name)(*args, **kwargs) | ||
# Future work: try to convert and run with other backends in self._automatic_backends | ||
# Future work: try to convert and run with other backends in automatic_backends | ||
raise NetworkXNotImplemented( | ||
f"'{self.name}' not implemented by {graph_backend_name}" | ||
) | ||
|
@@ -538,13 +549,13 @@ def __call__(self, /, *args, backend=None, **kwargs): | |
# Only networkx graphs; try to convert and run with a backend with automatic | ||
# conversion, but don't do this by default for graph generators or loaders. | ||
if self.graphs: | ||
for backend_name in self._automatic_backends: | ||
for backend_name in automatic_backends: | ||
if self._can_backend_run(backend_name, *args, **kwargs): | ||
return self._convert_and_call( | ||
backend_name, | ||
args, | ||
kwargs, | ||
fallback_to_nx=self._fallback_to_nx, | ||
fallback_to_nx=backend_config["fallback_to_nx"], | ||
) | ||
# Default: run with networkx on networkx inputs | ||
return self.orig_func(*args, **kwargs) | ||
|
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.
what does
NETWORKX_AUTOMATIC_BACKENDS
mean here? like what's its functionality? is it the default backend?Sorry, I don't think I understand all of the backend functionality fully, so sorry if I am being repetitive here.
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.
No worries, thanks for asking!
Previously, configuring backends was only possible via environment variables.
NETWORKX_AUTOMATIC_BACKENDS
environment variable is a comma separated list of backends that indicates which backends we want to automatically convert to and run when e.g. the input graph is a NetworkX graph. This is a prioritized list, so backends listed first are tried first.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.
okay, thank you :)