Cómo usar Mathematica desde Python

Sep 22 2020

He estado usando las siguientes líneas de código para generar geometría 3D ( ref ). Las entradas edges, vd, vly ewprovienen de Python en los siguientes formatos

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

Convierto manualmente lo anterior a los formatos de entrada utilizados en Mathematica. Me gustaría saber cómo evitar esta conversión manual e integrar y ejecutar directamente el código de Mathematica desde 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]

Las sugerencias sobre cómo proceder e interactuar con Mathematica desde Python serán realmente útiles.

EDITAR: La respuesta publicada a continuación ayuda a pasar argumentos de entrada de Python usando PythonExpression. A continuación, me gustaría evaluar estas expresiones de Mathematica desde un script de Python. Según los comentarios a continuación, instalé wolframclient e hice lo siguiente

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: Puedo hacer

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

Lo intenté,

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"];'
    ))

Pero obtengo el siguiente 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.

¿Podría sugerir cómo se puede solucionar esto? Como se muestra a continuación, no hubo ningún problema al ejecutar las mismas expresiones en el portátil MMA.

Respuestas

3 M.R. Sep 23 2020 at 02:06

Aquí hay una forma de hacer lo que quiera en Mathematica.

Primero, usar "PythonExpression" puede ser útil para importar / exportar cosas:

{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]}"};

Desafortunadamente, los gráficos aún no funcionan con asociaciones, por lo que necesita listas y los bordes deben usar símbolos como UndirectedEdgeo 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]

Su cálculo casi funcionó, pero necesitaba convertir los bordes en reglas:

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]

Entonces funciona bien:

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

2 DanielHuber Sep 25 2020 at 17:47

Lo siento, estaba equivocado y no recordaba que desea usar MMA de Python y no al revés. ExternalEvaluate ["Python", str] es un comando de MMA, no Python.

Aquí se describe cómo dar entrada a MMA desde Python: "https://reference.wolfram.com/language/workflow/EvaluateAWolframLanguageExpressionFromPython.html".

Después de iniciar Python, ejecuta en Python:

from wolframclient.evaluation import WolframLanguageSession

session = WolframLanguageSession()

from wolframclient.language import wlexpr

session.evaluate(wlexpr('myCommandString'))

donde myCommandString es la cadena con sus declaraciones de Mathematica. Por ejemplo, en Python:

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

O si quieres esto en varias líneas:

myCommandString="a=1;"

myCommandString=myCommandString+"b=2;"

myCommandString=myCommandString+"a+b"

espero que esto ayude.

1 DorianB. Sep 25 2020 at 16:51

Si tiene un bloque de código que abarca varias líneas, puede ser útil colocarlo en un archivo .m y Getel archivo de python. He aquí un ejemplo:

Primero cree un archivo y escriba el código de Wolfram Language que le gustaría evaluar en él. Llamemos al archivo /tmp/test.m.

Ahora en 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'))

No olvide cerrar, de lo WolframLanguageSessioncontrario obtendrá kernels huérfanos (más información aquí ).

Como puede ver, el paso de evaluación ahora encaja en una línea. Puede actualizar su código en su editor favorito. Eso es exactamente lo que hice con su código inicial, solo agregué dos puntos y comas faltantes y exporté el resultado como se realizó más adelante.

Aquí está mi /tmp/test.marchivo:

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