How can I use the Python library networkx from Mathematica?












6












$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.










share|improve this question









$endgroup$

















    6












    $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.










    share|improve this question









    $endgroup$















      6












      6








      6


      1



      $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.










      share|improve this question









      $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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 33 mins ago









      SzabolcsSzabolcs

      164k14448950




      164k14448950






















          1 Answer
          1






          active

          oldest

          votes


















          8












          $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}]


          enter image description here



          Graph[nxFunction["margulis_gabber_galil_graph"][6], 
          VertexLabels -> Automatic]


          enter image description here



          nxFunction["hexagonal_lattice_graph"][6, 7]


          enter image description here



          Modify existing graphs:



          nxFunction["ego_graph"][GridGraph[{5, 6}], 1, 3]


          enter image description here



          nxFunction["mycielskian"][GridGraph[{3, 3}]]


          enter image description here



          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.






          share|improve this answer











          $endgroup$














            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
            });


            }
            });














            draft saved

            draft discarded


















            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









            8












            $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}]


            enter image description here



            Graph[nxFunction["margulis_gabber_galil_graph"][6], 
            VertexLabels -> Automatic]


            enter image description here



            nxFunction["hexagonal_lattice_graph"][6, 7]


            enter image description here



            Modify existing graphs:



            nxFunction["ego_graph"][GridGraph[{5, 6}], 1, 3]


            enter image description here



            nxFunction["mycielskian"][GridGraph[{3, 3}]]


            enter image description here



            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.






            share|improve this answer











            $endgroup$


















              8












              $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}]


              enter image description here



              Graph[nxFunction["margulis_gabber_galil_graph"][6], 
              VertexLabels -> Automatic]


              enter image description here



              nxFunction["hexagonal_lattice_graph"][6, 7]


              enter image description here



              Modify existing graphs:



              nxFunction["ego_graph"][GridGraph[{5, 6}], 1, 3]


              enter image description here



              nxFunction["mycielskian"][GridGraph[{3, 3}]]


              enter image description here



              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.






              share|improve this answer











              $endgroup$
















                8












                8








                8





                $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}]


                enter image description here



                Graph[nxFunction["margulis_gabber_galil_graph"][6], 
                VertexLabels -> Automatic]


                enter image description here



                nxFunction["hexagonal_lattice_graph"][6, 7]


                enter image description here



                Modify existing graphs:



                nxFunction["ego_graph"][GridGraph[{5, 6}], 1, 3]


                enter image description here



                nxFunction["mycielskian"][GridGraph[{3, 3}]]


                enter image description here



                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.






                share|improve this answer











                $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}]


                enter image description here



                Graph[nxFunction["margulis_gabber_galil_graph"][6], 
                VertexLabels -> Automatic]


                enter image description here



                nxFunction["hexagonal_lattice_graph"][6, 7]


                enter image description here



                Modify existing graphs:



                nxFunction["ego_graph"][GridGraph[{5, 6}], 1, 3]


                enter image description here



                nxFunction["mycielskian"][GridGraph[{3, 3}]]


                enter image description here



                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.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 7 mins ago

























                answered 17 mins ago









                SzabolcsSzabolcs

                164k14448950




                164k14448950






























                    draft saved

                    draft discarded




















































                    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.




                    draft saved


                    draft discarded














                    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





















































                    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







                    Popular posts from this blog

                    Scriptures other than the Hata Yoga Pradipika containing asanas

                    De Pijp

                    Joseph Stallaert