# How to use Mathematica from Python

I've been using the following lines of code to generate 3D geometry (ref). The inputs edges, vd, vl and ew come from Python in the following formats

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 manually convert the above to the input formats used in Mathematica. I'd like to know how to avoid this manual conversion and directly integrate and run the Mathematica code from Python.

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]

Suggestions on how to proceed and interface Mathematica from Python will be really helpful.

EDIT: The answer posted below helps in passing input arguments from Python using PythonExpression. Next, I would like to evalute these Mathematica expressions from a python script. Based on the comments below, I installed wolframclient and did the following

from wolframclient.evaluation import WolframLanguageSession
session = WolframLanguageSession()
from wolframclient.language import wlexpr
session.evaluate(wlexpr('')) # I would like to know how to inclide Mathematica expressions here

EDIT2: Can I do

session.evaluate(wlexpr(
'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];'))

I tried,

from wolframclient.evaluation import WolframLanguageSession
session = WolframLanguageSession()
from wolframclient.language import wlexpr

session.evaluate(wlexpr(
'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];'

'z1 = Values[solution3d] // Partition[#, 3] &;'

'theFile = File["op.txt"];'

'Export[theFile, z1, "Table"];'
))

But I get the following error,

String expected at position 1 in StringForm[MessageName[General, msgl], $$MessageList]. Undefined message MessageName[Message, msgl] with arguments {MessageList} The problem may be unbounded. Specifying a value for MaxIterations greater than 5000 may improve the solution. String expected at position 1 in StringForm[MessageName[General, msgl],$$MessageList].
Undefined message MessageName[Message, msgl] with arguments {$$MessageList} String expected at position 1 in StringForm[MessageName[General, msgl],$$MessageList].
Further output of MessageName[StringForm, string] will be suppressed during this calculation.
The problem may be unbounded. Specifying a value for MaxIterations greater than 5000 may improve the solution.
String expected at position 1 in StringForm[MessageName[General, msgl], $$MessageList]. Undefined message MessageName[Message, msgl] with arguments {MessageList} The problem may be unbounded. Specifying a value for MaxIterations greater than 5000 may improve the solution. String expected at position 1 in StringForm[MessageName[General, msgl],$$MessageList].
Undefined message MessageName[Message, msgl] with arguments {$$MessageList} String expected at position 1 in StringForm[MessageName[General, msgl],$$MessageList].
Further output of MessageName[StringForm, string] will be suppressed during this calculation.
The problem may be unbounded. Specifying a value for MaxIterations greater than 5000 may improve the solution.

Could you please suggest how this can be fixed? As shown below there was no problem in running the same expressions in MMA notebook.

• What is the exact question here? – user5601 Sep 22 '20 at 0:28
• @user5601 How to establish Python Mathematica interface – Natasha Sep 22 '20 at 0:30
• You may use Mathematica from Python. How to proceed is described her: reference.wolfram.com/language/workflowguide/… – Daniel Huber Sep 22 '20 at 12:03
• @DanielHuber Thank you, could you please have a look at my edit? – Natasha Sep 23 '20 at 2:14
• An example is shown in: "reference.wolfram.com/language/workflow/…" under heading "2": "session.evaluate(wlexpr('Map[#^2 &, Range[5]]'))". The MMA expression is: "Map[#^2 &, Range[5]]" – Daniel Huber Sep 23 '20 at 8:40

Here's one way to do what you want in Mathematica.

First, using "PythonExpression" can be handy to import/export things:

{edges,vl,ew,vd}=ImportString[#,"PythonExpression"]&/@ {"[(1,2),(1,3),(1,4),(2,5),(2,6),(5,6),(3,4),(3,7),(6,7),(7,8),(2,9)]",
"[1,2,3,4,5,6,7,8,9]","{(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}",
"{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]}"};

Unfortunately Graphs don't work with Associations yet, so you need lists, and edges should use symbols like UndirectedEdge or DirectedEdge:

edges = UndirectedEdge @@@ edges; vcoords = List @@ vd;
ew = Normal @ KeyMap[UndirectedEdge @@ # &,ew];
g3d = Graph3D[vl, edges, VertexCoordinates -> vcoords,
EdgeWeight->ew, VertexLabels->Placed["Name",Center],
EdgeLabels->{e_:>Placed["EdgeWeight",Center]}, VertexSize->.5, BaseStyle->16]

Your computation almost worked, but you needed to make the edges into rules:

vars3d = Array[Through[{x, y, z}@#] &, Length@vd];
\[Lambda] = 1/100.; lbnd = 0; ubnd = 500;
obj3d = Total[(Norm[vars3d[[First@#]] - vars3d[[Last@#]]] - # /. (Rule @@@ ew))^2 & /@ EdgeList[g3d]] + \[Lambda] * Total[Norm /@ (vars3d - Values@vd)];
solution3d = Last @ Minimize[{obj3d, And @@ Thread[lbnd <= Join @@ vars3d <= ubnd]}, Join @@ vars3d]

Then it works fine:

edgeLengths3d = # -> Norm[vars3d[[First@#]] - vars3d[[Last@#]]] /.
solution3d & /@ EdgeList[g3d];
ResourceFunction["PrettyGrid"][{#, # /. ew, # /. edgeLengths3d} & /@
EdgeList[g3d],  "ColumnHeadings" -> {"edge", "EdgeWeight", "Edge Length"}]

• Thanks a lot. This is really really helpful. I'm afraid it's still not clear to me know these Mathematica expressions have to be evaluated from Python. I've installed wolframclient in python and did from wolframclient.evaluation import WolframLanguageSession session = WolframLanguageSession() – Natasha Sep 23 '20 at 1:58
• Could you please have a look at my edit? – Natasha Sep 23 '20 at 2:14
• I do not think this will work. But you can join the different strings into one. – Daniel Huber Sep 23 '20 at 10:45
• @DanielHuber Thank you, but I still don't understand how to introduce line breaks. If I introduce a line break it automatically changes to different strings. – Natasha Sep 25 '20 at 5:53
• @Natasha Does the following do what you want? str = "a=2;"; str = str <> "b=3;"; str = str <> "a*b"; ExternalEvaluate["Python", str] – Daniel Huber Sep 25 '20 at 9:01

Sorry, I was mistaken and did not remember that you want to use MMA from Python and not the other way round. ExternalEvaluate["Python", str] is a MMA command, not Python.

How to give MMA input from Python is described here: "https://reference.wolfram.com/language/workflow/EvaluateAWolframLanguageExpressionFromPython.html".

After starting Python, you execute in Python:

from wolframclient.evaluation import WolframLanguageSession

session = WolframLanguageSession()

from wolframclient.language import wlexpr

session.evaluate(wlexpr('myCommandString'))

where myCommandString is the string with your Mathematica statements. E.g. in Python:

myCommandString="a=1;b=2;a+b"

Or if you want this on several lines:

myCommandString="a=1;"

myCommandString=myCommandString+"b=2;"

myCommandString=myCommandString+"a+b"

hope this helps.

• I'm afraid this may not work github.com/WolframResearch/WolframClientForPython/issues/24. I will give it a try. The above approach appears to be straightforward though - loading the contents from an m file. I think the only problem now is to figure out how to pass the python data types as input – Natasha Sep 25 '20 at 11:09
• Try to give ONE string to evaluate not several. – Daniel Huber Sep 25 '20 at 12:05

If you have a block of code spanning on multiple lines, it might be useful to put it in a .m file, and Get the file from python. Here is a example:

First create a file and write the Wolfram Language code you'd like to evaluate in it. Let's call the file /tmp/test.m.

Now in Python:

from wolframclient.evaluation import WolframLanguageSession
from wolframclient.language import wl

# important note: the session is automatically closed.
with WolframLanguageSession() as s:
s.evaluate(wl.Get('/tmp/test.m'))

Don't forget to close WolframLanguageSession otherwise you'll get orphan kernels (more info here).

As you can see the evaluation step now fits in one line. You are free to update your code in your favorite editor. That's exactly what I did with your initial code, I just added two missing semicolons, and exported the result as performed later on.

Here is the my /tmp/test.m file:

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];

\[Lambda] = 1/100.;

obj3d = Total[(Norm[vars3d[[First@#]] - vars3d[[Last@#]]] - # /.
ew)^2 & /@ EdgeList[g3d]] + \[Lambda] 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];

z1 = Values[solution3d] // Partition[#, 3] &;
Export["/tmp/result.txt", z1, "Table"];
$$`$$
• Thanks a lot, this helps! Could you please let me know how to pass input arguments to .m file? The variables edges, vd, ew and vl are python data types. Above, it was suggested to do {edges,vl,ew,vd}=ImportString[#,"PythonExpression"]&/@ {"[(1,2),(1,3),(1,4),(2,5),(2,6),(5,6),(3,4),(3,7),(6,7),(7,8),(2,9)]", "[1,2,3,4,5,6,7,8,9]","{(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}", "{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]}"}; – Natasha Sep 25 '20 at 10:14
• But I am not sure how to pass the input arguments from my python script for evaluating MMA expressions. – Natasha Sep 25 '20 at 10:20
• You can't really pass argument to a .m file, but you can declare functions in such a file and once you Get it, the functions become available in python as well. Say you have declared a WL function solution3d[...] := (...), you can call it from python using: first from wolframclient.language import Global followed by s.evaluate(Global.solution3d(edge, vd, vl, ed)) Also note that a lot of python native types are automatically converted to WL counterpart. e.g: tuple (1,2) becomes a list {1, 2}, etc. – Dorian B. Sep 25 '20 at 14:04
• Ahh, that's a wonderful suggestion. If you don't mind could you please show how to declare a WL function that accepts arguments in the solution you have posted? I'm new to MMA :( – Natasha Sep 25 '20 at 14:26
• Like this : solution3d = wl.Get('test.m') s.evaluate(Global.solution3d(edges, vd, vl, ew)) ? cross-posted – Natasha Sep 28 '20 at 5:57