Search code examples
python-3.xoptimizationgraphwolfram-mathematicamathematical-optimization

How to convert code in Mathematica to Python?


I've the following Mathematica code to scale the edge length of a graph to be equal to edge weights. (ref)

edges = {1 <-> 2, 1 <-> 3, 1 <-> 4, 2 <-> 5, 2 <-> 6, 5 <-> 6, 
   3 <-> 4, 3 <-> 7, 6 <-> 7, 7 <-> 8, 2 <-> 9};

vd = {{75., 25., 0}, {115., 45., 0}, {10., 5., 0}, {45., 0, 0}, 
  {90., 60., 0}, {45., 55., 0}, {0, 25., 0}, {10., 50., 0}, {115.,  25.,0}};

vl = Range[Length@vd];

vcoords = MapIndexed[#2[[1]] -> # &, vd];
ew = {1 \[UndirectedEdge] 2 -> 49.6, 1 \[UndirectedEdge] 3 -> 74.4, 
 1 \[UndirectedEdge] 4 -> 49.6, 2 \[UndirectedEdge] 5 -> 37.2, 
 2 \[UndirectedEdge] 6 -> 74.4, 5 \[UndirectedEdge] 6 -> 49.6, 
 3 \[UndirectedEdge] 4 -> 37.2, 3 \[UndirectedEdge] 7 -> 24.8, 
 6 \[UndirectedEdge] 7 -> 62, 7 \[UndirectedEdge] 8 -> 37.2, 
 2 \[UndirectedEdge] 9 -> 24.8}

g3d = Graph3D[vl, edges, VertexCoordinates -> vcoords, 
  EdgeWeight -> ew, VertexLabels -> Placed["Name", Center], 
  EdgeLabels -> {e_ :> Placed["EdgeWeight", Center]}, 
  VertexSize -> .3, VertexStyle -> Red]
vars3d = Array[Through[{x, y, z}@#] &, Length @ vd];

λ = 1/100.;

obj3d = Total[(Norm[vars3d[[First@#]] - vars3d[[Last@#]]] - # /. ew)^2 & /@ 
  EdgeList[g3d]] +  λ Total[Norm /@ (vars3d - vd)];

lbnd = 0;
ubnd = 500;

solution3d = Last@Minimize[{obj3d, And @@ Thread[lbnd <= Join @@ vars3d <= ubnd]}, 
    Join @@ vars3d];

edgeLengths3d = # -> Norm[vars3d[[First@#]] - vars3d[[Last@#]]] /. 
     solution3d & /@ EdgeList[g3d];

Grid[Prepend[{#, # /. ew, # /. edgeLengths3d} & /@ 
   EdgeList[g3d], {"edge", "EdgeWeight", "Edge Length"}], 
 Dividers -> All]

I am trying to rewrite the same code in Python. The inputs have been converted to python data types lists and dict.

edges = [(1,2), (1,3), (1,4), (2,5), (2,6), (5,6), (3,4), (3,7), (6,7), (7,8), (2,9)];
vl = [1,2,3,4,5,6,7,8,9]
ew = {(1, 2) : 49.6, (1,3): 74.4, (1, 4) : 49.6, (2,5): 37.2, (2, 6) : 74.4, (5, 6): 49.6, 
     (3, 4) : 37.2, (3, 7):24.8, (6, 7) : 62, (7, 8) : 37.2, (2, 9) : 24.8}
vd = {1:[75., 25., 0], 2:[115., 45., 0], 3:[10., 5., 0], 4:[45., 0, 0], 
      5:[90., 60., 0], 6:[45., 55., 0], 7:[0, 25., 0], 8:[10., 50., 0], 9:[115.,  25.,0]}; 

I'm not sure how the optimizations commands must be converted in Python. Suggestions on how to write the lines obj3d and solution3d in Python will be of great help.


Solution

  • Converting Mathematica to Python

    This has only been done to some extent using various languages and tools, and I don't know of a complete solution to this as of yet.

    I've researched it a bit and viable routes could be either:

    Mathematica -> SymPy / ANTLR -> Python or Mathematica -> C / Fortran -> Callable from Python

    The Mathematica way of doing things are (naturally) more functional than object oriented in nature, so translating directly to Python would probably be possible, but would result in some fairly hairy looking Python code.

    If you do want to continue down the Mathematica -> Pythonroute, there is a really smart guy (senior scientist at Quantum Information and Foundations Group CSIC), named Juan José García Ripoll that has started a tool for converting Mathematica Notebooks to Python directly.

    It uses a pretty straight forward parser based method, that basically crawls through the Mathematica expression, translates operators into Python equivalents and creates a valid Python expression out of it. It works for the most simple operations for the time being, but it would be a nice starting point if you don't want to start from scratch.

    It is already capable of translating Mathematica expressions like:

    enter image description here

    Into Python code, so it's not that basic. Not a bad starting point.

    You can download a Mathematica Notebook file that includes code and example here

    The blog post that explains this tool is here

    The Mathics project also contains some interesting code in the same domain. You might want to check out the related source files.

    I know this doesn't fully answer your question, but it does provide pointers to a possible solution for making it work, albeit this requires some work.