How can I use the Python library networkx from Mathematica? Planned maintenance scheduled April 23, 2019 at 23:30 UTC (7:30pm US/Eastern) Announcing the arrival of Valued Associate #679: Cesar Manara Unicorn Meta Zoo #1: Why another podcast?How to use Mathematica functions in Python programs?Is there a way to run Python from within Mathematica?Mathematica and Python integration?How can I conveniently call igraph from Mathematica?What is the correct way to provide Mathematica with a path to Python user scripts?Why do Python modules load differently via Mathematica?Mathematica style plotting in Python?How to add new python type translation to ExternalEvaluate?Control a Mathematica kernel from the front-end and from pythonHow can I ExternalEvaluate Python with tesseract?
What are some likely causes to domain member PC losing contact to domain controller?
NIntegrate on a solution of a matrix ODE
Why complex landing gears are used instead of simple, reliable and light weight muscle wire or shape memory alloys?
What is the music which plays over the closing credits of Fleabag series 2 episode 2?
Is this Kuo-toa homebrew race balanced?
Why not use the yoke to control yaw, as well as pitch and roll?
Is it OK to use the testing sample to compare algorithms?
Is this Half-dragon Quaggoth boss monster balanced?
Why are two-digit numbers in Jonathan Swift's "Gulliver's Travels" (1726) written in "German style"?
Why are current probes so expensive?
How to resize main filesystem
How do I find my Spellcasting Ability for my D&D character?
As a dual citizen, my US passport will expire one day after traveling to the US. Will this work?
newbie Q : How to read an output file in one command line
Any stored/leased 737s that could substitute for grounded MAXs?
"Destructive power" carried by a B-52?
How can I prevent/balance waiting and turtling as a response to cooldown mechanics
Should man-made satellites feature an intelligent inverted "cow catcher"?
Adapting the Chinese Remainder Theorem (CRT) for integers to polynomials
Inverse square law not accurate for non-point masses?
Diophantine equation 3^a+1=3^b+5^c
Twin's vs. Twins'
Baking rewards as operations
Where did Ptolemy compare the Earth to the distance of fixed stars?
How can I use the Python library networkx from Mathematica?
Planned maintenance scheduled April 23, 2019 at 23:30 UTC (7:30pm US/Eastern)
Announcing the arrival of Valued Associate #679: Cesar Manara
Unicorn Meta Zoo #1: Why another podcast?How to use Mathematica functions in Python programs?Is there a way to run Python from within Mathematica?Mathematica and Python integration?How can I conveniently call igraph from Mathematica?What is the correct way to provide Mathematica with a path to Python user scripts?Why do Python modules load differently via Mathematica?Mathematica style plotting in Python?How to add new python type translation to ExternalEvaluate?Control a Mathematica kernel from the front-end and from pythonHow can I ExternalEvaluate Python with tesseract?
$begingroup$
Is there an easy way to access the Python library networkx from Mathematica?
The improvements to ExternalEvaluate
in Mathematica 12.0 should make this feasible.
graphs-and-networks interoperability external-calls python
$endgroup$
add a comment |
$begingroup$
Is there an easy way to access the Python library networkx from Mathematica?
The improvements to ExternalEvaluate
in Mathematica 12.0 should make this feasible.
graphs-and-networks interoperability external-calls python
$endgroup$
add a comment |
$begingroup$
Is there an easy way to access the Python library networkx from Mathematica?
The improvements to ExternalEvaluate
in Mathematica 12.0 should make this feasible.
graphs-and-networks interoperability external-calls python
$endgroup$
Is there an easy way to access the Python library networkx from Mathematica?
The improvements to ExternalEvaluate
in Mathematica 12.0 should make this feasible.
graphs-and-networks interoperability external-calls python
graphs-and-networks interoperability external-calls python
asked Apr 17 at 11:48
SzabolcsSzabolcs
165k14450954
165k14450954
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
Mathematica 12.0 brings two new features that make this easier to do than it was before:
ExternalFunction
Wolfram Client for Python
Below we implement a function nxFunction
that automatically handles translating Mathematica expressions of interest to Python, as well as converting the results back. The usage will be
nxFunction["someNetworkxFunction"][graph, positionalArg2, "keyword1" -> keywordArgValue]
Here is a barebones example that serves as a proof of concept. (Improvements posted as additional answers are very welcome!)
Set up external session
First, make sure that the Python you are using has networkx installed, and start an external session. In the below example I am using an Anaconda virtualenv named "py37" on macOS. Adjust as necessary for your machine.
py = StartExternalSession["Python",
"Executable" -> AbsoluteFileName["~/anaconda/envs/py37/bin/python"]]
Load the package:
ExternalEvaluate[py, "import networkx as nx"]
Mathematica -> Python conversion
We are going to use two Python helper function to translate arguments into the correct form. Most networkx functions that take a graph will take it as the first argument. This Python function takes a vertex list, an edge list and a graph type, and translates them to a networkx object. The rest of the arguments/options are passed as normal arguments / keyword arguments.
nxFun = ExternalFunction[py, "
def nxfun(vertices, edges, gtype, fname, args, kwargs):
fun = getattr(nx, fname)
GraphClass = 'su': nx.Graph, 'sd': nx.DiGraph, 'mu': nx.MultiGraph, 'md': nx.MultiDiGraph[gtype]
g = GraphClass()
g.add_nodes_from(vertices)
g.add_edges_from(edges)
return fun(g, *args, **kwargs)
"]
The following is for calling networkx functions that do not take a graph argument:
nxPlainFun = ExternalFunction[py, "
def nxplainfun(fname, args, kwargs):
fun = getattr(nx, fname)
return fun(*args, **kwargs)
"]
Now we create Mathematica functions that call the above Python functions:
ClearAll[nxGraphQ]
nxGraphQ[_?MixedGraphQ] = False;
nxGraphQ[_?GraphQ] = True;
nxGraphQ[_] = False;
(* first argument is a graph *)
nxFunction[name_][g_?nxGraphQ, args___, kwargs : OptionsPattern[]] :=
nxFun[
VertexList[g],
List @@@ EdgeList[g],
If[MultigraphQ[g],
If[DirectedGraphQ[g], "md", "mu"],
If[DirectedGraphQ[g], "sd", "su"]
],
name,
args,
Association[kwargs]
]
(* first argument is not a graph *)
nxFunction[name_][args___, kwargs : OptionsPattern[]] :=
nxPlainFun[
name, args, Association[kwargs]
]
Python -> Mathematica conversion
We create a custom serializer for networkx graphs, as described here:
- https://reference.wolfram.com/language/WolframClientForPython/docpages/advanced_usages.html#extending-serialization-writing-an-encoder
ExternalEvaluate[py,
"
from wolframclient.language import wl
from wolframclient.serializers import wolfram_encoder
@wolfram_encoder.dispatch(nx.Graph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.UndirectedEdge, graph.edges, [1])))
@wolfram_encoder.dispatch(nx.DiGraph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.DirectedEdge, graph.edges, [1])))
"]
Try it out
Create a test graph:
SeedRandom[42]
g = RandomGraph[10, 20, DirectedEdges -> True]
Compute the betweenness:
nxFunction["betweenness_centrality"][g]
(* <|1 -> 0.256944, 2 -> 0.0416667, 3 -> 0., 4 -> 0.333333,
5 -> 0.0277778, 6 -> 0.236111, 7 -> 0.25, 8 -> 0.111111, 9 -> 0.,
10 -> 0.0763889|> *)
Compute betweenness without normalization (and test keyword arguments):
nxFunction["betweenness_centrality"][g, "normalized" -> False]
(* <|1 -> 18.5, 2 -> 3., 3 -> 0., 4 -> 24., 5 -> 2., 6 -> 17.,
7 -> 18., 8 -> 8., 9 -> 0., 10 -> 5.5|> *)
Compare with Mathematica's result:
BetweennessCentrality[g]
(* 18.5, 3., 0., 24., 2., 17., 18., 8., 0., 5.5 *)
A networkx function that returns a graph:
nxFunction["grid_graph"][3, 4]
Graph[nxFunction["margulis_gabber_galil_graph"][6],
VertexLabels -> Automatic]
nxFunction["hexagonal_lattice_graph"][6, 7]
Modify existing graphs:
nxFunction["ego_graph"][GridGraph[5, 6], 1, 3]
nxFunction["mycielskian"][GridGraph[3, 3]]
Compute minimal cycle basis:
nxFunction["minimum_cycle_basis"][GridGraph[3, 4]]
(* 1, 2, 4, 5, 2, 3, 5, 6, 4, 5, 7, 8, 5, 6, 8, 9, 7, 8, 10, 11, 8, 9, 11, 12 *)
This is a first proof of concept. Improvement and suggestions are most welcome. I encourage everyone to post new answers either improving this one, or presenting independent approaches.
$endgroup$
add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "387"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmathematica.stackexchange.com%2fquestions%2f195380%2fhow-can-i-use-the-python-library-networkx-from-mathematica%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Mathematica 12.0 brings two new features that make this easier to do than it was before:
ExternalFunction
Wolfram Client for Python
Below we implement a function nxFunction
that automatically handles translating Mathematica expressions of interest to Python, as well as converting the results back. The usage will be
nxFunction["someNetworkxFunction"][graph, positionalArg2, "keyword1" -> keywordArgValue]
Here is a barebones example that serves as a proof of concept. (Improvements posted as additional answers are very welcome!)
Set up external session
First, make sure that the Python you are using has networkx installed, and start an external session. In the below example I am using an Anaconda virtualenv named "py37" on macOS. Adjust as necessary for your machine.
py = StartExternalSession["Python",
"Executable" -> AbsoluteFileName["~/anaconda/envs/py37/bin/python"]]
Load the package:
ExternalEvaluate[py, "import networkx as nx"]
Mathematica -> Python conversion
We are going to use two Python helper function to translate arguments into the correct form. Most networkx functions that take a graph will take it as the first argument. This Python function takes a vertex list, an edge list and a graph type, and translates them to a networkx object. The rest of the arguments/options are passed as normal arguments / keyword arguments.
nxFun = ExternalFunction[py, "
def nxfun(vertices, edges, gtype, fname, args, kwargs):
fun = getattr(nx, fname)
GraphClass = 'su': nx.Graph, 'sd': nx.DiGraph, 'mu': nx.MultiGraph, 'md': nx.MultiDiGraph[gtype]
g = GraphClass()
g.add_nodes_from(vertices)
g.add_edges_from(edges)
return fun(g, *args, **kwargs)
"]
The following is for calling networkx functions that do not take a graph argument:
nxPlainFun = ExternalFunction[py, "
def nxplainfun(fname, args, kwargs):
fun = getattr(nx, fname)
return fun(*args, **kwargs)
"]
Now we create Mathematica functions that call the above Python functions:
ClearAll[nxGraphQ]
nxGraphQ[_?MixedGraphQ] = False;
nxGraphQ[_?GraphQ] = True;
nxGraphQ[_] = False;
(* first argument is a graph *)
nxFunction[name_][g_?nxGraphQ, args___, kwargs : OptionsPattern[]] :=
nxFun[
VertexList[g],
List @@@ EdgeList[g],
If[MultigraphQ[g],
If[DirectedGraphQ[g], "md", "mu"],
If[DirectedGraphQ[g], "sd", "su"]
],
name,
args,
Association[kwargs]
]
(* first argument is not a graph *)
nxFunction[name_][args___, kwargs : OptionsPattern[]] :=
nxPlainFun[
name, args, Association[kwargs]
]
Python -> Mathematica conversion
We create a custom serializer for networkx graphs, as described here:
- https://reference.wolfram.com/language/WolframClientForPython/docpages/advanced_usages.html#extending-serialization-writing-an-encoder
ExternalEvaluate[py,
"
from wolframclient.language import wl
from wolframclient.serializers import wolfram_encoder
@wolfram_encoder.dispatch(nx.Graph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.UndirectedEdge, graph.edges, [1])))
@wolfram_encoder.dispatch(nx.DiGraph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.DirectedEdge, graph.edges, [1])))
"]
Try it out
Create a test graph:
SeedRandom[42]
g = RandomGraph[10, 20, DirectedEdges -> True]
Compute the betweenness:
nxFunction["betweenness_centrality"][g]
(* <|1 -> 0.256944, 2 -> 0.0416667, 3 -> 0., 4 -> 0.333333,
5 -> 0.0277778, 6 -> 0.236111, 7 -> 0.25, 8 -> 0.111111, 9 -> 0.,
10 -> 0.0763889|> *)
Compute betweenness without normalization (and test keyword arguments):
nxFunction["betweenness_centrality"][g, "normalized" -> False]
(* <|1 -> 18.5, 2 -> 3., 3 -> 0., 4 -> 24., 5 -> 2., 6 -> 17.,
7 -> 18., 8 -> 8., 9 -> 0., 10 -> 5.5|> *)
Compare with Mathematica's result:
BetweennessCentrality[g]
(* 18.5, 3., 0., 24., 2., 17., 18., 8., 0., 5.5 *)
A networkx function that returns a graph:
nxFunction["grid_graph"][3, 4]
Graph[nxFunction["margulis_gabber_galil_graph"][6],
VertexLabels -> Automatic]
nxFunction["hexagonal_lattice_graph"][6, 7]
Modify existing graphs:
nxFunction["ego_graph"][GridGraph[5, 6], 1, 3]
nxFunction["mycielskian"][GridGraph[3, 3]]
Compute minimal cycle basis:
nxFunction["minimum_cycle_basis"][GridGraph[3, 4]]
(* 1, 2, 4, 5, 2, 3, 5, 6, 4, 5, 7, 8, 5, 6, 8, 9, 7, 8, 10, 11, 8, 9, 11, 12 *)
This is a first proof of concept. Improvement and suggestions are most welcome. I encourage everyone to post new answers either improving this one, or presenting independent approaches.
$endgroup$
add a comment |
$begingroup$
Mathematica 12.0 brings two new features that make this easier to do than it was before:
ExternalFunction
Wolfram Client for Python
Below we implement a function nxFunction
that automatically handles translating Mathematica expressions of interest to Python, as well as converting the results back. The usage will be
nxFunction["someNetworkxFunction"][graph, positionalArg2, "keyword1" -> keywordArgValue]
Here is a barebones example that serves as a proof of concept. (Improvements posted as additional answers are very welcome!)
Set up external session
First, make sure that the Python you are using has networkx installed, and start an external session. In the below example I am using an Anaconda virtualenv named "py37" on macOS. Adjust as necessary for your machine.
py = StartExternalSession["Python",
"Executable" -> AbsoluteFileName["~/anaconda/envs/py37/bin/python"]]
Load the package:
ExternalEvaluate[py, "import networkx as nx"]
Mathematica -> Python conversion
We are going to use two Python helper function to translate arguments into the correct form. Most networkx functions that take a graph will take it as the first argument. This Python function takes a vertex list, an edge list and a graph type, and translates them to a networkx object. The rest of the arguments/options are passed as normal arguments / keyword arguments.
nxFun = ExternalFunction[py, "
def nxfun(vertices, edges, gtype, fname, args, kwargs):
fun = getattr(nx, fname)
GraphClass = 'su': nx.Graph, 'sd': nx.DiGraph, 'mu': nx.MultiGraph, 'md': nx.MultiDiGraph[gtype]
g = GraphClass()
g.add_nodes_from(vertices)
g.add_edges_from(edges)
return fun(g, *args, **kwargs)
"]
The following is for calling networkx functions that do not take a graph argument:
nxPlainFun = ExternalFunction[py, "
def nxplainfun(fname, args, kwargs):
fun = getattr(nx, fname)
return fun(*args, **kwargs)
"]
Now we create Mathematica functions that call the above Python functions:
ClearAll[nxGraphQ]
nxGraphQ[_?MixedGraphQ] = False;
nxGraphQ[_?GraphQ] = True;
nxGraphQ[_] = False;
(* first argument is a graph *)
nxFunction[name_][g_?nxGraphQ, args___, kwargs : OptionsPattern[]] :=
nxFun[
VertexList[g],
List @@@ EdgeList[g],
If[MultigraphQ[g],
If[DirectedGraphQ[g], "md", "mu"],
If[DirectedGraphQ[g], "sd", "su"]
],
name,
args,
Association[kwargs]
]
(* first argument is not a graph *)
nxFunction[name_][args___, kwargs : OptionsPattern[]] :=
nxPlainFun[
name, args, Association[kwargs]
]
Python -> Mathematica conversion
We create a custom serializer for networkx graphs, as described here:
- https://reference.wolfram.com/language/WolframClientForPython/docpages/advanced_usages.html#extending-serialization-writing-an-encoder
ExternalEvaluate[py,
"
from wolframclient.language import wl
from wolframclient.serializers import wolfram_encoder
@wolfram_encoder.dispatch(nx.Graph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.UndirectedEdge, graph.edges, [1])))
@wolfram_encoder.dispatch(nx.DiGraph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.DirectedEdge, graph.edges, [1])))
"]
Try it out
Create a test graph:
SeedRandom[42]
g = RandomGraph[10, 20, DirectedEdges -> True]
Compute the betweenness:
nxFunction["betweenness_centrality"][g]
(* <|1 -> 0.256944, 2 -> 0.0416667, 3 -> 0., 4 -> 0.333333,
5 -> 0.0277778, 6 -> 0.236111, 7 -> 0.25, 8 -> 0.111111, 9 -> 0.,
10 -> 0.0763889|> *)
Compute betweenness without normalization (and test keyword arguments):
nxFunction["betweenness_centrality"][g, "normalized" -> False]
(* <|1 -> 18.5, 2 -> 3., 3 -> 0., 4 -> 24., 5 -> 2., 6 -> 17.,
7 -> 18., 8 -> 8., 9 -> 0., 10 -> 5.5|> *)
Compare with Mathematica's result:
BetweennessCentrality[g]
(* 18.5, 3., 0., 24., 2., 17., 18., 8., 0., 5.5 *)
A networkx function that returns a graph:
nxFunction["grid_graph"][3, 4]
Graph[nxFunction["margulis_gabber_galil_graph"][6],
VertexLabels -> Automatic]
nxFunction["hexagonal_lattice_graph"][6, 7]
Modify existing graphs:
nxFunction["ego_graph"][GridGraph[5, 6], 1, 3]
nxFunction["mycielskian"][GridGraph[3, 3]]
Compute minimal cycle basis:
nxFunction["minimum_cycle_basis"][GridGraph[3, 4]]
(* 1, 2, 4, 5, 2, 3, 5, 6, 4, 5, 7, 8, 5, 6, 8, 9, 7, 8, 10, 11, 8, 9, 11, 12 *)
This is a first proof of concept. Improvement and suggestions are most welcome. I encourage everyone to post new answers either improving this one, or presenting independent approaches.
$endgroup$
add a comment |
$begingroup$
Mathematica 12.0 brings two new features that make this easier to do than it was before:
ExternalFunction
Wolfram Client for Python
Below we implement a function nxFunction
that automatically handles translating Mathematica expressions of interest to Python, as well as converting the results back. The usage will be
nxFunction["someNetworkxFunction"][graph, positionalArg2, "keyword1" -> keywordArgValue]
Here is a barebones example that serves as a proof of concept. (Improvements posted as additional answers are very welcome!)
Set up external session
First, make sure that the Python you are using has networkx installed, and start an external session. In the below example I am using an Anaconda virtualenv named "py37" on macOS. Adjust as necessary for your machine.
py = StartExternalSession["Python",
"Executable" -> AbsoluteFileName["~/anaconda/envs/py37/bin/python"]]
Load the package:
ExternalEvaluate[py, "import networkx as nx"]
Mathematica -> Python conversion
We are going to use two Python helper function to translate arguments into the correct form. Most networkx functions that take a graph will take it as the first argument. This Python function takes a vertex list, an edge list and a graph type, and translates them to a networkx object. The rest of the arguments/options are passed as normal arguments / keyword arguments.
nxFun = ExternalFunction[py, "
def nxfun(vertices, edges, gtype, fname, args, kwargs):
fun = getattr(nx, fname)
GraphClass = 'su': nx.Graph, 'sd': nx.DiGraph, 'mu': nx.MultiGraph, 'md': nx.MultiDiGraph[gtype]
g = GraphClass()
g.add_nodes_from(vertices)
g.add_edges_from(edges)
return fun(g, *args, **kwargs)
"]
The following is for calling networkx functions that do not take a graph argument:
nxPlainFun = ExternalFunction[py, "
def nxplainfun(fname, args, kwargs):
fun = getattr(nx, fname)
return fun(*args, **kwargs)
"]
Now we create Mathematica functions that call the above Python functions:
ClearAll[nxGraphQ]
nxGraphQ[_?MixedGraphQ] = False;
nxGraphQ[_?GraphQ] = True;
nxGraphQ[_] = False;
(* first argument is a graph *)
nxFunction[name_][g_?nxGraphQ, args___, kwargs : OptionsPattern[]] :=
nxFun[
VertexList[g],
List @@@ EdgeList[g],
If[MultigraphQ[g],
If[DirectedGraphQ[g], "md", "mu"],
If[DirectedGraphQ[g], "sd", "su"]
],
name,
args,
Association[kwargs]
]
(* first argument is not a graph *)
nxFunction[name_][args___, kwargs : OptionsPattern[]] :=
nxPlainFun[
name, args, Association[kwargs]
]
Python -> Mathematica conversion
We create a custom serializer for networkx graphs, as described here:
- https://reference.wolfram.com/language/WolframClientForPython/docpages/advanced_usages.html#extending-serialization-writing-an-encoder
ExternalEvaluate[py,
"
from wolframclient.language import wl
from wolframclient.serializers import wolfram_encoder
@wolfram_encoder.dispatch(nx.Graph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.UndirectedEdge, graph.edges, [1])))
@wolfram_encoder.dispatch(nx.DiGraph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.DirectedEdge, graph.edges, [1])))
"]
Try it out
Create a test graph:
SeedRandom[42]
g = RandomGraph[10, 20, DirectedEdges -> True]
Compute the betweenness:
nxFunction["betweenness_centrality"][g]
(* <|1 -> 0.256944, 2 -> 0.0416667, 3 -> 0., 4 -> 0.333333,
5 -> 0.0277778, 6 -> 0.236111, 7 -> 0.25, 8 -> 0.111111, 9 -> 0.,
10 -> 0.0763889|> *)
Compute betweenness without normalization (and test keyword arguments):
nxFunction["betweenness_centrality"][g, "normalized" -> False]
(* <|1 -> 18.5, 2 -> 3., 3 -> 0., 4 -> 24., 5 -> 2., 6 -> 17.,
7 -> 18., 8 -> 8., 9 -> 0., 10 -> 5.5|> *)
Compare with Mathematica's result:
BetweennessCentrality[g]
(* 18.5, 3., 0., 24., 2., 17., 18., 8., 0., 5.5 *)
A networkx function that returns a graph:
nxFunction["grid_graph"][3, 4]
Graph[nxFunction["margulis_gabber_galil_graph"][6],
VertexLabels -> Automatic]
nxFunction["hexagonal_lattice_graph"][6, 7]
Modify existing graphs:
nxFunction["ego_graph"][GridGraph[5, 6], 1, 3]
nxFunction["mycielskian"][GridGraph[3, 3]]
Compute minimal cycle basis:
nxFunction["minimum_cycle_basis"][GridGraph[3, 4]]
(* 1, 2, 4, 5, 2, 3, 5, 6, 4, 5, 7, 8, 5, 6, 8, 9, 7, 8, 10, 11, 8, 9, 11, 12 *)
This is a first proof of concept. Improvement and suggestions are most welcome. I encourage everyone to post new answers either improving this one, or presenting independent approaches.
$endgroup$
Mathematica 12.0 brings two new features that make this easier to do than it was before:
ExternalFunction
Wolfram Client for Python
Below we implement a function nxFunction
that automatically handles translating Mathematica expressions of interest to Python, as well as converting the results back. The usage will be
nxFunction["someNetworkxFunction"][graph, positionalArg2, "keyword1" -> keywordArgValue]
Here is a barebones example that serves as a proof of concept. (Improvements posted as additional answers are very welcome!)
Set up external session
First, make sure that the Python you are using has networkx installed, and start an external session. In the below example I am using an Anaconda virtualenv named "py37" on macOS. Adjust as necessary for your machine.
py = StartExternalSession["Python",
"Executable" -> AbsoluteFileName["~/anaconda/envs/py37/bin/python"]]
Load the package:
ExternalEvaluate[py, "import networkx as nx"]
Mathematica -> Python conversion
We are going to use two Python helper function to translate arguments into the correct form. Most networkx functions that take a graph will take it as the first argument. This Python function takes a vertex list, an edge list and a graph type, and translates them to a networkx object. The rest of the arguments/options are passed as normal arguments / keyword arguments.
nxFun = ExternalFunction[py, "
def nxfun(vertices, edges, gtype, fname, args, kwargs):
fun = getattr(nx, fname)
GraphClass = 'su': nx.Graph, 'sd': nx.DiGraph, 'mu': nx.MultiGraph, 'md': nx.MultiDiGraph[gtype]
g = GraphClass()
g.add_nodes_from(vertices)
g.add_edges_from(edges)
return fun(g, *args, **kwargs)
"]
The following is for calling networkx functions that do not take a graph argument:
nxPlainFun = ExternalFunction[py, "
def nxplainfun(fname, args, kwargs):
fun = getattr(nx, fname)
return fun(*args, **kwargs)
"]
Now we create Mathematica functions that call the above Python functions:
ClearAll[nxGraphQ]
nxGraphQ[_?MixedGraphQ] = False;
nxGraphQ[_?GraphQ] = True;
nxGraphQ[_] = False;
(* first argument is a graph *)
nxFunction[name_][g_?nxGraphQ, args___, kwargs : OptionsPattern[]] :=
nxFun[
VertexList[g],
List @@@ EdgeList[g],
If[MultigraphQ[g],
If[DirectedGraphQ[g], "md", "mu"],
If[DirectedGraphQ[g], "sd", "su"]
],
name,
args,
Association[kwargs]
]
(* first argument is not a graph *)
nxFunction[name_][args___, kwargs : OptionsPattern[]] :=
nxPlainFun[
name, args, Association[kwargs]
]
Python -> Mathematica conversion
We create a custom serializer for networkx graphs, as described here:
- https://reference.wolfram.com/language/WolframClientForPython/docpages/advanced_usages.html#extending-serialization-writing-an-encoder
ExternalEvaluate[py,
"
from wolframclient.language import wl
from wolframclient.serializers import wolfram_encoder
@wolfram_encoder.dispatch(nx.Graph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.UndirectedEdge, graph.edges, [1])))
@wolfram_encoder.dispatch(nx.DiGraph)
def encode_animal(serializer, graph):
return serializer.encode(wl.Graph(graph.nodes, wl.Apply(wl.DirectedEdge, graph.edges, [1])))
"]
Try it out
Create a test graph:
SeedRandom[42]
g = RandomGraph[10, 20, DirectedEdges -> True]
Compute the betweenness:
nxFunction["betweenness_centrality"][g]
(* <|1 -> 0.256944, 2 -> 0.0416667, 3 -> 0., 4 -> 0.333333,
5 -> 0.0277778, 6 -> 0.236111, 7 -> 0.25, 8 -> 0.111111, 9 -> 0.,
10 -> 0.0763889|> *)
Compute betweenness without normalization (and test keyword arguments):
nxFunction["betweenness_centrality"][g, "normalized" -> False]
(* <|1 -> 18.5, 2 -> 3., 3 -> 0., 4 -> 24., 5 -> 2., 6 -> 17.,
7 -> 18., 8 -> 8., 9 -> 0., 10 -> 5.5|> *)
Compare with Mathematica's result:
BetweennessCentrality[g]
(* 18.5, 3., 0., 24., 2., 17., 18., 8., 0., 5.5 *)
A networkx function that returns a graph:
nxFunction["grid_graph"][3, 4]
Graph[nxFunction["margulis_gabber_galil_graph"][6],
VertexLabels -> Automatic]
nxFunction["hexagonal_lattice_graph"][6, 7]
Modify existing graphs:
nxFunction["ego_graph"][GridGraph[5, 6], 1, 3]
nxFunction["mycielskian"][GridGraph[3, 3]]
Compute minimal cycle basis:
nxFunction["minimum_cycle_basis"][GridGraph[3, 4]]
(* 1, 2, 4, 5, 2, 3, 5, 6, 4, 5, 7, 8, 5, 6, 8, 9, 7, 8, 10, 11, 8, 9, 11, 12 *)
This is a first proof of concept. Improvement and suggestions are most welcome. I encourage everyone to post new answers either improving this one, or presenting independent approaches.
edited Apr 17 at 12:14
answered Apr 17 at 12:04
SzabolcsSzabolcs
165k14450954
165k14450954
add a comment |
add a comment |
Thanks for contributing an answer to Mathematica Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmathematica.stackexchange.com%2fquestions%2f195380%2fhow-can-i-use-the-python-library-networkx-from-mathematica%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown